Skip to content

Commit a486da7

Browse files
committed
mlir: build the while test where py.condition is, not at its operand's definition
1 parent e165e24 commit a486da7

2 files changed

Lines changed: 96 additions & 7 deletions

File tree

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
"""A while condition whose value is defined outside the condition region.
2+
3+
WhileOpLowering built the loop's test and cf.cond_br at the *condition value's*
4+
definition site. That is usually inside the condition region, but not always: CSE
5+
merges the constant behind `while True:` with an identical constant in the
6+
enclosing function, after which py.condition tests a value defined in the
7+
function's entry block. Inserting there put the cf.cond_br in the middle of that
8+
block, as a second terminator, and MLIR's region DCE then segfaulted on the block
9+
whose last operation was no longer a terminator.
10+
11+
py.condition is by construction the terminator of the condition region's last
12+
block, and the value it tests necessarily dominates it, so that is where the
13+
branch belongs.
14+
15+
`b = True` before the loop is what creates the constant CSE merges with — without
16+
it the loop's `True` is unique and the bug does not appear. Reduced from
17+
sre_parse._parse; the same fault was the long-standing `import weakref` crash.
18+
"""
19+
20+
21+
def only_exit_is_raise(a):
22+
b = True
23+
if a:
24+
while True:
25+
raise ValueError("boom")
26+
return b
27+
28+
29+
try:
30+
only_exit_is_raise(True)
31+
raise AssertionError("should have raised")
32+
except ValueError as e:
33+
assert str(e) == "boom", str(e)
34+
assert only_exit_is_raise(False) is True
35+
36+
37+
def shared_true_constant(limit):
38+
flag = True
39+
n = 0
40+
while True:
41+
n += 1
42+
if n >= limit:
43+
break
44+
return (n, flag)
45+
46+
47+
assert shared_true_constant(3) == (3, True), shared_true_constant(3)
48+
49+
50+
def shared_false_constant(a):
51+
flag = False
52+
n = 0
53+
while not flag:
54+
n += 1
55+
if n >= a:
56+
flag = True
57+
return n
58+
59+
60+
assert shared_false_constant(2) == 2, shared_false_constant(2)
61+
62+
63+
def condition_is_a_parameter(cond, limit):
64+
# The condition value is a block argument rather than an op result, the other
65+
# branch of the insertion-point choice that used to exist.
66+
n = 0
67+
while cond:
68+
n += 1
69+
if n >= limit:
70+
cond = False
71+
return n
72+
73+
74+
assert condition_is_a_parameter(True, 2) == 2, condition_is_a_parameter(True, 2)
75+
assert condition_is_a_parameter(False, 2) == 0, condition_is_a_parameter(False, 2)
76+
77+
78+
def nested_loops_sharing_true(limit):
79+
t = True
80+
outer = 0
81+
while True:
82+
outer += 1
83+
inner = 0
84+
while True:
85+
inner += 1
86+
if inner >= 2:
87+
break
88+
if outer >= limit:
89+
break
90+
return (outer, inner, t)
91+
92+
93+
assert nested_loops_sharing_true(2) == (2, 2, True), nested_loops_sharing_true(2)

‎src/executable/mlir/Conversion/PythonToPythonBytecode/PythonToPythonBytecode.cpp‎

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -332,13 +332,9 @@ namespace py {
332332
rewriter.setInsertionPointToEnd(initBlock);
333333
mlir::cf::BranchOp::create(rewriter, condition_op.getLoc(), &condition_start);
334334

335-
if (mlir::isa<mlir::BlockArgument>(condition_op.getCond())) {
336-
rewriter.setInsertionPointToStart(condition_op.getCond().getParentBlock());
337-
} else {
338-
rewriter.setInsertionPointAfter(condition_op.getCond().getDefiningOp());
339-
}
340-
auto should_jump = mlir::py::CastToBoolOp::create(
341-
rewriter, condition_op.getLoc(), rewriter.getI1Type(), condition_op.getCond());
335+
rewriter.setInsertionPoint(condition_op);
336+
auto should_jump = rewriter.create<mlir::py::CastToBoolOp>(
337+
condition_op.getLoc(), rewriter.getI1Type(), condition_op.getCond());
342338
ASSERT(!op.getBody().empty());
343339
mlir::cf::CondBranchOp::create(rewriter,
344340
condition_op.getLoc(),

0 commit comments

Comments
 (0)