Skip to content

Commit 53dd383

Browse files
committed
avoid speculative guards
1 parent 46c241e commit 53dd383

File tree

3 files changed

+32
-57
lines changed

3 files changed

+32
-57
lines changed

Lib/test/test_capi/test_opt.py

Lines changed: 30 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3349,75 +3349,75 @@ def testfunc(args):
33493349
self.assertNotIn("_UNARY_NEGATIVE_FLOAT_INPLACE", uops)
33503350

33513351
def test_float_truediv_inplace_unique_lhs(self):
3352-
# (a + b) produces a unique float; dividing by c reuses it
3352+
# (a + b) / (c + d): LHS is unique float from add, RHS is unique
3353+
# float from add. The division reuses the LHS in place.
33533354
def testfunc(args):
3354-
a, b, c, n = args
3355+
a, b, c, d, n = args
33553356
total = 0.0
33563357
for _ in range(n):
3357-
total += (a + b) / c
3358+
total += (a + b) / (c + d)
33583359
return total
33593360

3360-
res, ex = self._run_with_optimizer(testfunc, (2.0, 3.0, 4.0, TIER2_THRESHOLD))
3361+
res, ex = self._run_with_optimizer(testfunc, (2.0, 3.0, 1.0, 3.0, TIER2_THRESHOLD))
33613362
self.assertAlmostEqual(res, TIER2_THRESHOLD * 1.25)
33623363
self.assertIsNotNone(ex)
33633364
uops = get_opnames(ex)
33643365
self.assertIn("_BINARY_OP_TRUEDIV_FLOAT_INPLACE", uops)
33653366

33663367
def test_float_truediv_inplace_unique_rhs(self):
3367-
# (a + b) produces a unique float on the right side of /
3368+
# x = c + d stores to a local (not unique when reloaded).
3369+
# (a + b) is unique. The division should use inplace on the RHS.
33683370
def testfunc(args):
3369-
a, b, c, n = args
3371+
a, b, c, d, n = args
33703372
total = 0.0
33713373
for _ in range(n):
3372-
total += c / (a + b)
3374+
x = c + d
3375+
total += x / (a + b)
33733376
return total
33743377

3375-
res, ex = self._run_with_optimizer(testfunc, (2.0, 3.0, 4.0, TIER2_THRESHOLD))
3376-
self.assertAlmostEqual(res, TIER2_THRESHOLD * 0.8)
3378+
res, ex = self._run_with_optimizer(testfunc, (2.0, 3.0, 4.0, 5.0, TIER2_THRESHOLD))
3379+
self.assertAlmostEqual(res, TIER2_THRESHOLD * (9.0 / 5.0))
33773380
self.assertIsNotNone(ex)
33783381
uops = get_opnames(ex)
33793382
self.assertIn("_BINARY_OP_TRUEDIV_FLOAT_INPLACE_RIGHT", uops)
33803383

33813384
def test_float_truediv_type_propagation(self):
3382-
# (a/b) + (c/d): the optimizer speculatively inserts float guards
3383-
# for both divisions, specializing them to _BINARY_OP_TRUEDIV_FLOAT.
3384-
# Their results are unique floats, so the + uses inplace.
3385+
# (a+b) / (c+d) - (e+f) / (g+h): all additions produce known-float
3386+
# results, so both divisions are specialized. The subtraction between
3387+
# the two division results should use inplace.
33853388
def testfunc(args):
3386-
a, b, c, d, n = args
3389+
a, b, n = args
33873390
total = 0.0
33883391
for _ in range(n):
3389-
total += (a / b) + (c / d)
3392+
x = (a + b) # type of x will specialize to float
3393+
total += x / x - x / x
33903394
return total
33913395

3392-
res, ex = self._run_with_optimizer(testfunc, (10.0, 3.0, 4.0, 5.0, TIER2_THRESHOLD))
3393-
expected = TIER2_THRESHOLD * (10.0 / 3.0 + 4.0 / 5.0)
3396+
res, ex = self._run_with_optimizer(testfunc,
3397+
(2.0, 3.0, TIER2_THRESHOLD))
3398+
expected = TIER2_THRESHOLD * ((2.0 + 3.0) / (2.0 + 3.0) - (2.0 + 3.0) / (2.0 + 3.0))
33943399
self.assertAlmostEqual(res, expected)
33953400
self.assertIsNotNone(ex)
33963401
uops = get_opnames(ex)
3397-
# Both divisions are specialized with speculative guards
3398-
self.assertIn("_BINARY_OP_TRUEDIV_FLOAT", uops)
3399-
# The + uses inplace (a/b result is unique)
3400-
self.assertIn("_BINARY_OP_ADD_FLOAT_INPLACE", uops)
3401-
# The += uses inplace (+ result is unique)
3402-
self.assertIn("_BINARY_OP_ADD_FLOAT_INPLACE_RIGHT", uops)
3402+
self.assertIn("_BINARY_OP_TRUEDIV_FLOAT_INPLACE", uops)
3403+
self.assertIn("_BINARY_OP_SUBTRACT_FLOAT_INPLACE", uops)
34033404

34043405
def test_float_truediv_unique_result_enables_inplace(self):
3405-
# (a+b) / c / d: (a+b) is unique float, so the first / uses
3406-
# inplace. Its result is also unique, so the second / can use
3407-
# _BINARY_OP_TRUEDIV_FLOAT_INPLACE too.
3406+
# (a+b) / (c+d) / (e+f): chained divisions where each result
3407+
# is unique, enabling inplace for subsequent divisions.
34083408
def testfunc(args):
3409-
a, b, c, d, n = args
3409+
a, b, c, d, e, f, n = args
34103410
total = 0.0
34113411
for _ in range(n):
3412-
total += (a + b) / c / d
3412+
total += (a + b) / (c + d) / (e + f)
34133413
return total
34143414

3415-
res, ex = self._run_with_optimizer(testfunc, (2.0, 3.0, 4.0, 5.0, TIER2_THRESHOLD))
3416-
expected = TIER2_THRESHOLD * ((2.0 + 3.0) / 4.0 / 5.0)
3415+
res, ex = self._run_with_optimizer(testfunc,
3416+
(2.0, 3.0, 1.0, 1.0, 1.0, 1.0, TIER2_THRESHOLD))
3417+
expected = TIER2_THRESHOLD * ((2.0 + 3.0) / (1.0 + 1.0) / (1.0 + 1.0))
34173418
self.assertAlmostEqual(res, expected)
34183419
self.assertIsNotNone(ex)
34193420
uops = get_opnames(ex)
3420-
# Both divisions should use inplace (chained uniqueness)
34213421
self.assertIn("_BINARY_OP_TRUEDIV_FLOAT_INPLACE", uops)
34223422

34233423
def test_float_add_chain_both_unique(self):

Python/optimizer_bytecodes.c

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -254,22 +254,8 @@ dummy_func(void) {
254254
bool rhs_int = sym_matches_type(rhs, &PyLong_Type);
255255
bool lhs_float = sym_matches_type(lhs, &PyFloat_Type);
256256
bool rhs_float = sym_matches_type(rhs, &PyFloat_Type);
257-
// Specialize float/float true division in tier 2.
258-
// Speculatively insert guards for operands not yet known to be
259-
// float. Skip if both are known int (int/int handled below) or
260-
// if either is a known non-int/float type (Fraction, Decimal, etc.)
261257
if ((oparg == NB_TRUE_DIVIDE || oparg == NB_INPLACE_TRUE_DIVIDE)
262-
&& !(lhs_int && rhs_int)
263-
&& !(!lhs_int && !lhs_float && sym_has_type(lhs))
264-
&& !(!rhs_int && !rhs_float && sym_has_type(rhs))) {
265-
if (!rhs_float) {
266-
ADD_OP(_GUARD_TOS_FLOAT, 0, 0);
267-
sym_set_type(rhs, &PyFloat_Type);
268-
}
269-
if (!lhs_float) {
270-
ADD_OP(_GUARD_NOS_FLOAT, 0, 0);
271-
sym_set_type(lhs, &PyFloat_Type);
272-
}
258+
&& lhs_float && rhs_float) {
273259
if (PyJitRef_IsUnique(lhs)) {
274260
ADD_OP(_BINARY_OP_TRUEDIV_FLOAT_INPLACE, 0, 0);
275261
l = sym_new_null(ctx);
@@ -289,7 +275,6 @@ dummy_func(void) {
289275
}
290276
else if ((oparg == NB_TRUE_DIVIDE || oparg == NB_INPLACE_TRUE_DIVIDE)
291277
&& (lhs_int || lhs_float) && (rhs_int || rhs_float)) {
292-
// int / int always returns float. No guards needed.
293278
res = sym_new_type(ctx, &PyFloat_Type);
294279
}
295280
else if (!((lhs_int || lhs_float) && (rhs_int || rhs_float))) {

Python/optimizer_cases.c.h

Lines changed: 1 addition & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)