[flang-commits] [flang] [flang] Do not hoist fir.field_index out of loops (PR #222891)
via flang-commits
flang-commits at lists.llvm.org
Fri Sep 11 02:04:21 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-flang-fir-hlfir
Author: Kareem Ergawy (ergawy)
<details>
<summary>Changes</summary>
Lowering a consumer of a !fir.field value inspects its defining operation: for a record whose layout is known at compile time the field becomes an LLVM GEP struct index, which must be a constant, and otherwise the `field` attribute is read off the defining op. A field value therefore may not be a block argument.
LICM broke that. fir.field_index is Pure and takes no operands, so it is trivially loop-invariant and was hoisted out of the loop. Lowering emits one inside each arm of a construct -- for example the CASEs of a SELECT CASE that each pass a different component of the same derived type as an actual argument -- so hoisting them left those arms as otherwise-identical blocks differing only in that operand. Block merging then merged them and threaded the field through a new block argument, and codegen aborted with "must be a constant".
Leave producers of a !fir.field where they are. The arms then differ by an operation rather than by an operand, so they are no longer merge candidates. Consumers are unaffected and still hoist: fir.coordinate_of uses the attribute form of the field, so it does not depend on the fir.field_index value.
The check keys on the result type rather than on FieldIndexOp so that any operation producing a !fir.field is covered, and sits in the predicate shared by the direct-loop and nested-region hoisting paths.
---
Full diff: https://github.com/llvm/llvm-project/pull/222891.diff
2 Files Affected:
- (modified) flang/lib/Optimizer/Transforms/LoopInvariantCodeMotion.cpp (+15)
- (modified) flang/test/Transforms/licm.fir (+10-2)
``````````diff
diff --git a/flang/lib/Optimizer/Transforms/LoopInvariantCodeMotion.cpp b/flang/lib/Optimizer/Transforms/LoopInvariantCodeMotion.cpp
index 97958003ddfd2..4003f1cb30b18 100644
--- a/flang/lib/Optimizer/Transforms/LoopInvariantCodeMotion.cpp
+++ b/flang/lib/Optimizer/Transforms/LoopInvariantCodeMotion.cpp
@@ -321,6 +321,21 @@ void LoopInvariantCodeMotion::runOnOperation() {
std::function<bool(Operation *, LoopLikeOpInterface, bool)>
shouldMoveOutOfLoop = [&](Operation *op, LoopLikeOpInterface loopLike,
bool maybeConditionallyExecuted) {
+ // Never hoist a producer of a !fir.field. Lowering a consumer of a
+ // field value inspects its defining operation: for a record whose
+ // layout is known at compile time the field becomes an LLVM GEP struct
+ // index, which must be a constant. Hoisting fir.field_index out of the
+ // arms of a construct (e.g. the CASEs of a SELECT CASE, each passing a
+ // different component of the same derived type) leaves those arms as
+ // otherwise-identical blocks differing only in this operand, which lets
+ // block merging thread it through a new block argument -- destroying
+ // the defining operation that codegen needs.
+ if (llvm::any_of(op->getResultTypes(),
+ [](mlir::Type t) { return isa<fir::FieldType>(t); })) {
+ LDBG() << "Not hoisting producer of a field value: " << *op;
+ return false;
+ }
+
if (isPure(op)) {
LDBG() << "Pure operation: " << *op;
return true;
diff --git a/flang/test/Transforms/licm.fir b/flang/test/Transforms/licm.fir
index b695ea2db184f..b841c5e93cb06 100644
--- a/flang/test/Transforms/licm.fir
+++ b/flang/test/Transforms/licm.fir
@@ -2015,13 +2015,20 @@ func.func @test_acc_loop_private2_hoisting() {
}
// -----
-// Test hoisting of fir.field_index and fir.coordinate_of.
+// Test hoisting of fir.coordinate_of.
+// fir.field_index is deliberately NOT hoisted: lowering a consumer of a field
+// value inspects its defining operation (for a record whose layout is known at
+// compile time the field becomes a constant LLVM GEP struct index). Hoisting it
+// out of the arms of a construct leaves otherwise-identical blocks differing
+// only in that operand, which lets block merging thread it through a block
+// argument and destroy the defining operation codegen needs. Its consumers are
+// unaffected and still hoist -- fir.coordinate_of below uses the attribute form
+// of the field, so it does not depend on the fir.field_index value.
// CHECK-LABEL: func.func @_QMmPtest(
// CHECK-SAME: %[[ARG0:[0-9]+|[a-zA-Z$._-][a-zA-Z0-9$._-]*]]: !fir.ref<i32> {fir.bindc_name = "n"}) {
// CHECK: %[[DUMMY_SCOPE_0:.*]] = fir.dummy_scope : !fir.dscope
// CHECK: %[[ADDRESS_OF_0:.*]] = fir.address_of(@_QMmEglob) : !fir.ref<!fir.type<_QMmTt{a:!fir.box<!fir.heap<!fir.array<?xf32>>>}>>
// CHECK: %[[DECLARE_0:.*]] = fir.declare %[[ADDRESS_OF_0]] {uniq_name = "_QMmEglob"} : (!fir.ref<!fir.type<_QMmTt{a:!fir.box<!fir.heap<!fir.array<?xf32>>>}>>) -> !fir.ref<!fir.type<_QMmTt{a:!fir.box<!fir.heap<!fir.array<?xf32>>>}>>
-// CHECK: %[[FIELD_INDEX_0:.*]] = fir.field_index a, !fir.type<_QMmTt{a:!fir.box<!fir.heap<!fir.array<?xf32>>>}>
// CHECK: %[[COORDINATE_OF_0:.*]] = fir.coordinate_of %[[DECLARE_0]], a : (!fir.ref<!fir.type<_QMmTt{a:!fir.box<!fir.heap<!fir.array<?xf32>>>}>>) -> !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>
// CHECK: %[[LOAD_1:.*]] = fir.load %[[COORDINATE_OF_0]] : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>
// CHECK: %[[BOX_ADDR_0:.*]] = fir.box_addr %[[LOAD_1]] : (!fir.box<!fir.heap<!fir.array<?xf32>>>) -> !fir.heap<!fir.array<?xf32>>
@@ -2029,6 +2036,7 @@ func.func @test_acc_loop_private2_hoisting() {
// CHECK: %[[BOX_DIMS_0:.*]]:3 = fir.box_dims %[[LOAD_1]], %[[CONSTANT_2]] : (!fir.box<!fir.heap<!fir.array<?xf32>>>, index) -> (index, index, index)
// CHECK: %[[SHAPE_SHIFT_0:.*]] = fir.shape_shift %[[BOX_DIMS_0]]#0, %[[BOX_DIMS_0]]#1 : (index, index) -> !fir.shapeshift<1>
// CHECK: %[[DO_LOOP_0:.*]] = fir.do_loop
+// CHECK: %[[FIELD_INDEX_0:.*]] = fir.field_index a, !fir.type<_QMmTt{a:!fir.box<!fir.heap<!fir.array<?xf32>>>}>
func.func @_QMmPtest(%arg0: !fir.ref<i32> {fir.bindc_name = "n"}) {
%cst = arith.constant 1.000000e+00 : f32
%c1 = arith.constant 1 : index
``````````
</details>
https://github.com/llvm/llvm-project/pull/222891
More information about the flang-commits
mailing list