[flang-commits] [flang] bdc9914 - [flang][hlfir] Add hlfir.elemental_addr for vector subscripted assignment
Jean Perier via flang-commits
flang-commits at lists.llvm.org
Wed May 3 00:17:16 PDT 2023
Author: Jean Perier
Date: 2023-05-03T09:16:17+02:00
New Revision: bdc9914bb94edd164fa67da265915e7c99473ffa
URL: https://github.com/llvm/llvm-project/commit/bdc9914bb94edd164fa67da265915e7c99473ffa
DIFF: https://github.com/llvm/llvm-project/commit/bdc9914bb94edd164fa67da265915e7c99473ffa.diff
LOG: [flang][hlfir] Add hlfir.elemental_addr for vector subscripted assignment
See the operation description in HLFIROps.td.
Depends on D149442
Differential Revision: https://reviews.llvm.org/D149449
Added:
flang/test/HLFIR/element-addr.fir
Modified:
flang/include/flang/Optimizer/HLFIR/HLFIROps.td
flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp
flang/test/HLFIR/invalid.fir
Removed:
################################################################################
diff --git a/flang/include/flang/Optimizer/HLFIR/HLFIROps.td b/flang/include/flang/Optimizer/HLFIR/HLFIROps.td
index 495d72863db56..6e6af7126bf37 100644
--- a/flang/include/flang/Optimizer/HLFIR/HLFIROps.td
+++ b/flang/include/flang/Optimizer/HLFIR/HLFIROps.td
@@ -868,7 +868,7 @@ def hlfir_RegionAssignOp : hlfir_Op<"region_assign", []> {
let hasVerifier = 1;
}
-def hlfir_YieldOp : hlfir_Op<"yield", [Terminator, ParentOneOf<["RegionAssignOp"]>,
+def hlfir_YieldOp : hlfir_Op<"yield", [Terminator, ParentOneOf<["RegionAssignOp", "ElementalAddrOp"]>,
SingleBlockImplicitTerminator<"fir::FirEndOp">]> {
let summary = "Yield a value or variable inside a forall, where or region assignment";
@@ -898,4 +898,68 @@ def hlfir_YieldOp : hlfir_Op<"yield", [Terminator, ParentOneOf<["RegionAssignOp"
let assemblyFormat = "$entity attr-dict `:` type($entity) custom<YieldOpCleanup>($cleanup)";
}
+def hlfir_ElementalAddrOp : hlfir_Op<"elemental_addr", [Terminator, HasParent<"RegionAssignOp">, RecursiveMemoryEffects, RecursivelySpeculatable]> {
+ let summary = "Yield the address of a vector subscripted variable inside an hlfir.region_assign";
+ let description = [{
+ Special terminator node for the left-hand side region of an hlfir.region_assign
+ to a vector subscripted entity.
+
+ It represents how the address of an element of such entity is computed given
+ one based indices.
+
+ It is very similar to hlfir.elemental, except that it does not produce an SSA
+ value because there is no hlfir type to describe a vector subscripted entity
+ (the codegen of such type would be problematic). Hence, it is tightly linked
+ to an hlfir.region_assign by its terminator property.
+
+ An optional cleanup region may be provided if any of the subscript expressions
+ of the designator require a cleanup.
+ This allows documenting cleanups that cannot be generated after the vector
+ subscripted designator usage (that has not been materizaled yet). The cleanups
+ will be evaluated after the assignment once the related
+ hlfir.region_assign is lowered.
+
+ Example: "X(VECTOR) = Y"
+
+ ```
+ hlfir.region_assign {
+ hlfir.yield %y : !fir.ref<!fir.array<20xf32>>
+ } to {
+ hlfir.elemental_addr %vector_shape : !fir.shape<1> {
+ ^bb0(%i: index):
+ %0 = hlfir.designate %vector (%i) : (!fir.ref<!fir.array<20xi32>>, index) -> !fir.ref<i32>
+ %1 = fir.load %0 : !fir.ref<i32>
+ %x_element_addr = hlfir.designate %x (%1) : (!fir.ref<!fir.array<100xf32>>, i32) -> !fir.ref<f32>
+ hlfir.yield %x_element_addr : !fir.ref<f32>
+ }
+ }
+ ```
+ }];
+
+ let arguments = (ins
+ fir_ShapeType:$shape,
+ Variadic<AnyIntegerType>:$typeparams);
+
+ let regions = (region SizedRegion<1>:$body,
+ MaxSizedRegion<1>:$cleanup);
+
+ let builders = [
+ OpBuilder<(ins "mlir::Value":$shape)>
+ ];
+
+ let assemblyFormat = [{
+ $shape (`typeparams` $typeparams^)?
+ attr-dict `:` type(operands) $body
+ custom<YieldOpCleanup>($cleanup)}];
+
+ let extraClassDeclaration = [{
+ mlir::Region::BlockArgListType getIndices() {
+ return getBody().getArguments();
+ }
+ }];
+
+ let hasVerifier = 1;
+}
+
+
#endif // FORTRAN_DIALECT_HLFIR_OPS
diff --git a/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp b/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp
index 51d18def03478..5b10ecee75f41 100644
--- a/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp
+++ b/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp
@@ -1032,8 +1032,8 @@ mlir::LogicalResult hlfir::RegionAssignOp::verify() {
if (!mlir::isa_and_nonnull<hlfir::YieldOp>(getTerminator(getRhsRegion())))
return emitOpError(
"right-hand side region must be terminated by an hlfir.yield");
- // TODO: allow hlfir.elemental_addr.
- if (!mlir::isa_and_nonnull<hlfir::YieldOp>(getTerminator(getLhsRegion())))
+ if (!mlir::isa_and_nonnull<hlfir::YieldOp, hlfir::ElementalAddrOp>(
+ getTerminator(getLhsRegion())))
return emitOpError("left-hand side region must be terminated by an "
"hlfir.yield or hlfir.elemental_addr");
return mlir::success();
@@ -1052,7 +1052,6 @@ static mlir::ParseResult parseYieldOpCleanup(mlir::OpAsmParser &parser,
hlfir::YieldOp::ensureTerminator(cleanup, parser.getBuilder(),
parser.getBuilder().getUnknownLoc());
}
-
return mlir::success();
}
@@ -1066,5 +1065,41 @@ static void printYieldOpCleanup(mlir::OpAsmPrinter &p, YieldOp yieldOp,
}
}
+//===----------------------------------------------------------------------===//
+// ElementalAddrOp
+//===----------------------------------------------------------------------===//
+
+void hlfir::ElementalAddrOp::build(mlir::OpBuilder &builder,
+ mlir::OperationState &odsState,
+ mlir::Value shape) {
+ odsState.addOperands(shape);
+ mlir::Region *bodyRegion = odsState.addRegion();
+ bodyRegion->push_back(new mlir::Block{});
+ if (auto shapeType = shape.getType().dyn_cast<fir::ShapeType>()) {
+ unsigned dim = shapeType.getRank();
+ mlir::Type indexType = builder.getIndexType();
+ for (unsigned d = 0; d < dim; ++d)
+ bodyRegion->front().addArgument(indexType, odsState.location);
+ }
+ // Push cleanUp region.
+ odsState.addRegion();
+}
+
+mlir::LogicalResult hlfir::ElementalAddrOp::verify() {
+ hlfir::YieldOp yieldOp =
+ mlir::dyn_cast_or_null<hlfir::YieldOp>(getTerminator(getBody()));
+ if (!yieldOp)
+ return emitOpError("body region must be terminated by an hlfir.yield");
+ mlir::Type elementAddrType = yieldOp.getEntity().getType();
+ if (!hlfir::isFortranVariableType(elementAddrType) ||
+ hlfir::getFortranElementOrSequenceType(elementAddrType)
+ .isa<fir::SequenceType>())
+ return emitOpError("body must compute the address of a scalar entity");
+ unsigned shapeRank = getShape().getType().cast<fir::ShapeType>().getRank();
+ if (shapeRank != getIndices().size())
+ return emitOpError("body number of indices must match shape rank");
+ return mlir::success();
+}
+
#define GET_OP_CLASSES
#include "flang/Optimizer/HLFIR/HLFIROps.cpp.inc"
diff --git a/flang/test/HLFIR/element-addr.fir b/flang/test/HLFIR/element-addr.fir
new file mode 100644
index 0000000000000..c6f2b06b9f688
--- /dev/null
+++ b/flang/test/HLFIR/element-addr.fir
@@ -0,0 +1,83 @@
+// Test hlfir.element_addr operation parse, verify (no errors), and unparse.
+// RUN: fir-opt %s | fir-opt | FileCheck %s
+
+func.func @test_element_addr(%x: !fir.ref<!fir.array<100xf32>>, %vector: !fir.ref<!fir.array<20xi32>>, %y: !fir.ref<!fir.array<20xf32>>) {
+ %c20 = arith.constant 20 : index
+ %vector_shape = fir.shape %c20 : (index) -> !fir.shape<1>
+ hlfir.region_assign {
+ hlfir.yield %y : !fir.ref<!fir.array<20xf32>>
+ } to {
+ hlfir.elemental_addr %vector_shape : !fir.shape<1> {
+ ^bb0(%i: index):
+ %0 = hlfir.designate %vector (%i) : (!fir.ref<!fir.array<20xi32>>, index) -> !fir.ref<i32>
+ %1 = fir.load %0 : !fir.ref<i32>
+ %x_element_addr = hlfir.designate %x (%1) : (!fir.ref<!fir.array<100xf32>>, i32) -> !fir.ref<f32>
+ hlfir.yield %x_element_addr : !fir.ref<f32>
+ }
+ }
+ return
+}
+// CHECK-LABEL: func.func @test_element_addr(
+// CHECK-SAME: %[[VAL_0:.*]]: !fir.ref<!fir.array<100xf32>>,
+// CHECK-SAME: %[[VAL_1:.*]]: !fir.ref<!fir.array<20xi32>>,
+// CHECK-SAME: %[[VAL_2:.*]]: !fir.ref<!fir.array<20xf32>>) {
+// CHECK: %[[VAL_3:.*]] = arith.constant 20 : index
+// CHECK: %[[VAL_4:.*]] = fir.shape %[[VAL_3]] : (index) -> !fir.shape<1>
+// CHECK: hlfir.region_assign {
+// CHECK: hlfir.yield %[[VAL_2]] : !fir.ref<!fir.array<20xf32>>
+// CHECK: } to {
+// CHECK: hlfir.elemental_addr %[[VAL_4]] : !fir.shape<1> {
+// CHECK: ^bb0(%[[VAL_5:.*]]: index):
+// CHECK: %[[VAL_6:.*]] = hlfir.designate %[[VAL_1]] (%[[VAL_5]]) : (!fir.ref<!fir.array<20xi32>>, index) -> !fir.ref<i32>
+// CHECK: %[[VAL_7:.*]] = fir.load %[[VAL_6]] : !fir.ref<i32>
+// CHECK: %[[VAL_8:.*]] = hlfir.designate %[[VAL_0]] (%[[VAL_7]]) : (!fir.ref<!fir.array<100xf32>>, i32) -> !fir.ref<f32>
+// CHECK: hlfir.yield %[[VAL_8]] : !fir.ref<f32>
+// CHECK: }
+// CHECK: }
+
+
+
+func.func @test_element_addr_cleanup(%x: !fir.box<!fir.array<?x!fir.char<1,?>>>, %y: !fir.box<!fir.array<?x!fir.char<1,?>>>) {
+ hlfir.region_assign {
+ hlfir.yield %y : !fir.box<!fir.array<?x!fir.char<1,?>>>
+ } to {
+ %c0 = arith.constant 0 : index
+ %len = fir.box_elesize %x : (!fir.box<!fir.array<?x!fir.char<1,?>>>) -> index
+ %vector = fir.call @returns_alloc() : () -> !fir.box<!fir.heap<!fir.array<?xi32>>>
+ %vector_dim:3 = fir.box_dims %x, %c0 : (!fir.box<!fir.array<?x!fir.char<1,?>>>, index) -> (index, index, index)
+ %vector_shape = fir.shape %vector_dim#1 : (index) -> !fir.shape<1>
+ hlfir.elemental_addr %vector_shape typeparams %len : !fir.shape<1>, index {
+ ^bb0(%i: index):
+ %0 = hlfir.designate %vector (%i) : (!fir.box<!fir.heap<!fir.array<?xi32>>>, index) -> !fir.ref<i32>
+ %1 = fir.load %0 : !fir.ref<i32>
+ %x_element_addr = hlfir.designate %x (%1) typeparams %len : (!fir.box<!fir.array<?x!fir.char<1,?>>>, i32, index) -> !fir.boxchar<1>
+ hlfir.yield %x_element_addr : !fir.boxchar<1>
+ } cleanup {
+ %addr = fir.box_addr %vector : (!fir.box<!fir.heap<!fir.array<?xi32>>>) -> !fir.heap<!fir.array<?xi32>>
+ fir.freemem %addr : !fir.heap<!fir.array<?xi32>>
+ }
+ }
+ return
+}
+// CHECK-LABEL: func.func @test_element_addr_cleanup(
+// CHECK-SAME: %[[VAL_0:[^:]*]]: !fir.box<!fir.array<?x!fir.char<1,?>>>,
+// CHECK-SAME: %[[VAL_1:.*]]: !fir.box<!fir.array<?x!fir.char<1,?>>>) {
+// CHECK: hlfir.region_assign {
+// CHECK: hlfir.yield %[[VAL_1]] : !fir.box<!fir.array<?x!fir.char<1,?>>>
+// CHECK: } to {
+// CHECK: %[[VAL_2:.*]] = arith.constant 0 : index
+// CHECK: %[[VAL_3:.*]] = fir.box_elesize %[[VAL_0]] : (!fir.box<!fir.array<?x!fir.char<1,?>>>) -> index
+// CHECK: %[[VAL_4:.*]] = fir.call @returns_alloc() : () -> !fir.box<!fir.heap<!fir.array<?xi32>>>
+// CHECK: %[[VAL_5:.*]]:3 = fir.box_dims %[[VAL_0]], %[[VAL_2]] : (!fir.box<!fir.array<?x!fir.char<1,?>>>, index) -> (index, index, index)
+// CHECK: %[[VAL_6:.*]] = fir.shape %[[VAL_5]]#1 : (index) -> !fir.shape<1>
+// CHECK: hlfir.elemental_addr %[[VAL_6]] typeparams %[[VAL_3]] : !fir.shape<1>, index {
+// CHECK: ^bb0(%[[VAL_7:.*]]: index):
+// CHECK: %[[VAL_8:.*]] = hlfir.designate %[[VAL_4]] (%[[VAL_7]]) : (!fir.box<!fir.heap<!fir.array<?xi32>>>, index) -> !fir.ref<i32>
+// CHECK: %[[VAL_9:.*]] = fir.load %[[VAL_8]] : !fir.ref<i32>
+// CHECK: %[[VAL_10:.*]] = hlfir.designate %[[VAL_0]] (%[[VAL_9]]) typeparams %[[VAL_3]] : (!fir.box<!fir.array<?x!fir.char<1,?>>>, i32, index) -> !fir.boxchar<1>
+// CHECK: hlfir.yield %[[VAL_10]] : !fir.boxchar<1>
+// CHECK: } cleanup {
+// CHECK: %[[VAL_11:.*]] = fir.box_addr %[[VAL_4]] : (!fir.box<!fir.heap<!fir.array<?xi32>>>) -> !fir.heap<!fir.array<?xi32>>
+// CHECK: fir.freemem %[[VAL_11]] : !fir.heap<!fir.array<?xi32>>
+// CHECK: }
+// CHECK: }
diff --git a/flang/test/HLFIR/invalid.fir b/flang/test/HLFIR/invalid.fir
index a1e532563b4da..5eb772b4aad5e 100644
--- a/flang/test/HLFIR/invalid.fir
+++ b/flang/test/HLFIR/invalid.fir
@@ -541,3 +541,69 @@ func.func @bad_region_assign_2(%x: !fir.box<!fir.array<?xf32>>) {
}
return
}
+
+// -----
+func.func @bad_element_addr_1(%x: !fir.ref<!fir.array<20xf32>>) {
+ %c20 = arith.constant 20 : index
+ %vector_shape = fir.shape %c20 : (index) -> !fir.shape<1>
+ hlfir.region_assign {
+ hlfir.yield %x : !fir.ref<!fir.array<20xf32>>
+ } to {
+ // expected-error at +1 {{'hlfir.elemental_addr' op body must compute the address of a scalar entity}}
+ hlfir.elemental_addr %vector_shape : !fir.shape<1> {
+ ^bb0(%i: index):
+ %c42 = arith.constant 42.0 : f32
+ hlfir.yield %c42 : f32
+ }
+ }
+ return
+}
+
+// -----
+func.func @bad_element_addr_2(%x: !fir.ref<!fir.array<20xf32>>) {
+ %c20 = arith.constant 20 : index
+ %vector_shape = fir.shape %c20 : (index) -> !fir.shape<1>
+ hlfir.region_assign {
+ hlfir.yield %x : !fir.ref<!fir.array<20xf32>>
+ } to {
+ // expected-error at +1 {{'hlfir.elemental_addr' op body must compute the address of a scalar entity}}
+ hlfir.elemental_addr %vector_shape : !fir.shape<1> {
+ ^bb0(%i: index):
+ hlfir.yield %x : !fir.ref<!fir.array<20xf32>>
+ }
+ }
+ return
+}
+
+// -----
+func.func @bad_element_addr_3(%x: !fir.ref<!fir.array<20xf32>>) {
+ %c20 = arith.constant 20 : index
+ %vector_shape = fir.shape %c20 : (index) -> !fir.shape<1>
+ hlfir.region_assign {
+ hlfir.yield %x : !fir.ref<!fir.array<20xf32>>
+ } to {
+ // expected-error at +1 {{'hlfir.elemental_addr' op body region must be terminated by an hlfir.yield}}
+ hlfir.elemental_addr %vector_shape : !fir.shape<1> {
+ ^bb0(%i: index):
+ %c42 = arith.constant 42.0 : f32
+ }
+ }
+ return
+}
+
+// -----
+func.func @bad_element_addr_4(%x: !fir.ref<!fir.array<20xf32>>, %y: !fir.ref<!fir.array<20x20xf32>>) {
+ %c20 = arith.constant 20 : index
+ %vector_shape = fir.shape %c20 : (index) -> !fir.shape<1>
+ hlfir.region_assign {
+ hlfir.yield %x : !fir.ref<!fir.array<20xf32>>
+ } to {
+ // expected-error at +1 {{'hlfir.elemental_addr' op body number of indices must match shape rank}}
+ hlfir.elemental_addr %vector_shape : !fir.shape<1> {
+ ^bb0(%i: index, %j: index):
+ %elt = hlfir.designate %y(%i, %j) : (!fir.ref<!fir.array<20x20xf32>>, index, index) -> !fir.ref<f32>
+ hlfir.yield %elt : !fir.ref<f32>
+ }
+ }
+ return
+}
More information about the flang-commits
mailing list