Skip to content

Commit 1d4453a

Browse files
authored
[flang][HLFIR] fix FORALL issue 120190 (#120236)
Fix #120190. The hlfir.forall lowering code was not properly checking for forall index reference in mask value computation before trying to hoist it: it was only looking at the ops directly nested in the hlfir.forall_mask region, but not the operation indirectly nested. This caused triggered bogus hoisting in #120190 leading to undefined behavior (reference to uinitialized data). The added regression test would die at compile time with a dominance error. Fix this by doing a deep walk of the region operation instead. Also clean-up the region cloning to use without_terminator.
1 parent 13107cb commit 1d4453a

File tree

2 files changed

+76
-15
lines changed

2 files changed

+76
-15
lines changed

flang/lib/Optimizer/HLFIR/Transforms/LowerHLFIROrderedAssignments.cpp

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -660,10 +660,7 @@ OrderedAssignmentRewriter::generateYieldedEntity(
660660
return castIfNeeded(loc, builder, {maskedValue, std::nullopt}, castToType);
661661
}
662662

663-
assert(region.hasOneBlock() && "region must contain one block");
664663
auto oldYield = getYield(region);
665-
mlir::Block::OpListType &ops = region.back().getOperations();
666-
667664
// Inside Forall, scalars that do not depend on forall indices can be hoisted
668665
// here because their evaluation is required to only call pure procedures, and
669666
// if they depend on a variable previously assigned to in a forall assignment,
@@ -674,24 +671,24 @@ OrderedAssignmentRewriter::generateYieldedEntity(
674671
bool hoistComputation = false;
675672
if (fir::isa_trivial(oldYield.getEntity().getType()) &&
676673
!constructStack.empty()) {
677-
hoistComputation = true;
678-
for (mlir::Operation &op : ops)
679-
if (llvm::any_of(op.getOperands(), [](mlir::Value value) {
680-
return isForallIndex(value);
681-
})) {
682-
hoistComputation = false;
683-
break;
684-
}
674+
mlir::WalkResult walkResult =
675+
region.walk([&](mlir::Operation *op) -> mlir::WalkResult {
676+
if (llvm::any_of(op->getOperands(), [](mlir::Value value) {
677+
return isForallIndex(value);
678+
}))
679+
return mlir::WalkResult::interrupt();
680+
return mlir::WalkResult::advance();
681+
});
682+
hoistComputation = !walkResult.wasInterrupted();
685683
}
686684
auto insertionPoint = builder.saveInsertionPoint();
687685
if (hoistComputation)
688686
builder.setInsertionPoint(constructStack[0]);
689687

690688
// Clone all operations except the final hlfir.yield.
691-
assert(!ops.empty() && "yield block cannot be empty");
692-
auto end = ops.end();
693-
for (auto opIt = ops.begin(); std::next(opIt) != end; ++opIt)
694-
(void)builder.clone(*opIt, mapper);
689+
assert(region.hasOneBlock() && "region must contain one block");
690+
for (auto &op : region.back().without_terminator())
691+
(void)builder.clone(op, mapper);
695692
// Get the value for the yielded entity, it may be the result of an operation
696693
// that was cloned, or it may be the same as the previous value if the yield
697694
// operand was created before the ordered assignment tree.
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// Regression test for https://github.com/llvm/llvm-project/issues/120190
2+
// Verify that hlfir.forall lowering does not try hoisting mask evaluation
3+
// that refer to the forall index inside nested regions only.
4+
// RUN: fir-opt %s --lower-hlfir-ordered-assignments | FileCheck %s
5+
6+
func.func @issue120190(%array: !fir.ref<!fir.array<100xf32>>, %cdt: i1) {
7+
%cst = arith.constant 0.000000e+00 : f32
8+
%c1 = arith.constant 1 : i64
9+
%c50 = arith.constant 50 : i64
10+
%c100 = arith.constant 100 : i64
11+
hlfir.forall lb {
12+
hlfir.yield %c1 : i64
13+
} ub {
14+
hlfir.yield %c100 : i64
15+
} (%forall_index: i64) {
16+
hlfir.forall_mask {
17+
%mask = fir.if %cdt -> i1 {
18+
// Reference to %forall_index is not directly in
19+
// hlfir.forall_mask region, but is nested.
20+
%res = arith.cmpi slt, %forall_index, %c50 : i64
21+
fir.result %res : i1
22+
} else {
23+
%res = arith.cmpi sgt, %forall_index, %c50 : i64
24+
fir.result %res : i1
25+
}
26+
hlfir.yield %mask : i1
27+
} do {
28+
hlfir.region_assign {
29+
hlfir.yield %cst : f32
30+
} to {
31+
%6 = hlfir.designate %array (%forall_index) : (!fir.ref<!fir.array<100xf32>>, i64) -> !fir.ref<f32>
32+
hlfir.yield %6 : !fir.ref<f32>
33+
}
34+
}
35+
}
36+
return
37+
}
38+
39+
// CHECK-LABEL: func.func @issue120190(
40+
// CHECK-SAME: %[[VAL_0:.*]]: !fir.ref<!fir.array<100xf32>>,
41+
// CHECK-SAME: %[[VAL_1:.*]]: i1) {
42+
// CHECK: %[[VAL_2:.*]] = arith.constant 0.000000e+00 : f32
43+
// CHECK: %[[VAL_3:.*]] = arith.constant 1 : i64
44+
// CHECK: %[[VAL_4:.*]] = arith.constant 50 : i64
45+
// CHECK: %[[VAL_5:.*]] = arith.constant 100 : i64
46+
// CHECK: %[[VAL_6:.*]] = fir.convert %[[VAL_3]] : (i64) -> index
47+
// CHECK: %[[VAL_7:.*]] = fir.convert %[[VAL_5]] : (i64) -> index
48+
// CHECK: %[[VAL_8:.*]] = arith.constant 1 : index
49+
// CHECK: fir.do_loop %[[VAL_9:.*]] = %[[VAL_6]] to %[[VAL_7]] step %[[VAL_8]] {
50+
// CHECK: %[[VAL_10:.*]] = fir.convert %[[VAL_9]] : (index) -> i64
51+
// CHECK: %[[VAL_11:.*]] = fir.if %[[VAL_1]] -> (i1) {
52+
// CHECK: %[[VAL_12:.*]] = arith.cmpi slt, %[[VAL_10]], %[[VAL_4]] : i64
53+
// CHECK: fir.result %[[VAL_12]] : i1
54+
// CHECK: } else {
55+
// CHECK: %[[VAL_13:.*]] = arith.cmpi sgt, %[[VAL_10]], %[[VAL_4]] : i64
56+
// CHECK: fir.result %[[VAL_13]] : i1
57+
// CHECK: }
58+
// CHECK: fir.if %[[VAL_11]] {
59+
// CHECK: %[[VAL_14:.*]] = hlfir.designate %[[VAL_0]] (%[[VAL_10]]) : (!fir.ref<!fir.array<100xf32>>, i64) -> !fir.ref<f32>
60+
// CHECK: hlfir.assign %[[VAL_2]] to %[[VAL_14]] : f32, !fir.ref<f32>
61+
// CHECK: }
62+
// CHECK: }
63+
// CHECK: return
64+
// CHECK: }

0 commit comments

Comments
 (0)