diff --git a/flang/include/flang/Optimizer/Builder/FIRBuilder.h b/flang/include/flang/Optimizer/Builder/FIRBuilder.h --- a/flang/include/flang/Optimizer/Builder/FIRBuilder.h +++ b/flang/include/flang/Optimizer/Builder/FIRBuilder.h @@ -182,6 +182,13 @@ return createTemporary(loc, type, name, {}, {}, attrs); } + /// Create a temporary on the heap. + mlir::Value + createHeapTemporary(mlir::Location loc, mlir::Type type, + llvm::StringRef name = {}, mlir::ValueRange shape = {}, + mlir::ValueRange lenParams = {}, + llvm::ArrayRef attrs = {}); + /// Create a global value. fir::GlobalOp createGlobal(mlir::Location loc, mlir::Type type, llvm::StringRef name, @@ -425,16 +432,16 @@ /// Dump the current function. (debug) LLVM_DUMP_METHOD void dumpFunc(); -private: - /// Set attributes (e.g. FastMathAttr) to \p op operation - /// based on the current attributes setting. - void setCommonAttributes(mlir::Operation *op) const; - /// FirOpBuilder hook for creating new operation. void notifyOperationInserted(mlir::Operation *op) override { setCommonAttributes(op); } +private: + /// Set attributes (e.g. FastMathAttr) to \p op operation + /// based on the current attributes setting. + void setCommonAttributes(mlir::Operation *op) const; + const KindMapping &kindMap; /// FastMathFlags that need to be set for operations that support diff --git a/flang/include/flang/Optimizer/Builder/HLFIRTools.h b/flang/include/flang/Optimizer/Builder/HLFIRTools.h --- a/flang/include/flang/Optimizer/Builder/HLFIRTools.h +++ b/flang/include/flang/Optimizer/Builder/HLFIRTools.h @@ -14,6 +14,7 @@ #define FORTRAN_OPTIMIZER_BUILDER_HLFIRTOOLS_H #include "flang/Optimizer/Builder/BoxValue.h" +#include "flang/Optimizer/Dialect/FIROps.h" #include "flang/Optimizer/Dialect/FortranVariableInterface.h" #include "flang/Optimizer/HLFIR/HLFIRDialect.h" @@ -25,6 +26,7 @@ class AssociateOp; class ElementalOp; +class YieldElementOp; /// Is this an SSA value type for the value of a Fortran expression? inline bool isFortranValueType(mlir::Type type) { @@ -253,6 +255,22 @@ mlir::ValueRange typeParams, const ElementalKernelGenerator &genKernel); +/// Generate a fir.do_loop nest looping from 1 to extents[i]. +/// Return the inner fir.do_loop and the indices of the loops. +std::pair> +genLoopNest(mlir::Location loc, fir::FirOpBuilder &builder, + mlir::ValueRange extents); + +/// Inline the body of an hlfir.elemental at the current insertion point +/// given a list of one based indices. This generate the computation +/// of one element of the elemental expression. Return the YieldElementOp +/// whose value argument is the element value. +/// The original hlfir::ElementalOp is left untouched. +hlfir::YieldElementOp inlineElementalOp(mlir::Location loc, + fir::FirOpBuilder &builder, + hlfir::ElementalOp elemental, + mlir::ValueRange oneBasedIndices); + } // namespace hlfir #endif // FORTRAN_OPTIMIZER_BUILDER_HLFIRTOOLS_H diff --git a/flang/lib/Optimizer/Builder/FIRBuilder.cpp b/flang/lib/Optimizer/Builder/FIRBuilder.cpp --- a/flang/lib/Optimizer/Builder/FIRBuilder.cpp +++ b/flang/lib/Optimizer/Builder/FIRBuilder.cpp @@ -232,6 +232,20 @@ return ae; } +mlir::Value fir::FirOpBuilder::createHeapTemporary( + mlir::Location loc, mlir::Type type, llvm::StringRef name, + mlir::ValueRange shape, mlir::ValueRange lenParams, + llvm::ArrayRef attrs) { + llvm::SmallVector dynamicShape = + elideExtentsAlreadyInType(type, shape); + llvm::SmallVector dynamicLength = + elideLengthsAlreadyInType(type, lenParams); + + assert(!type.isa() && "cannot be a reference"); + return create(loc, type, /*unique_name=*/llvm::StringRef{}, + name, dynamicLength, dynamicShape, attrs); +} + /// Create a global variable in the (read-only) data section. A global variable /// must have a unique name to identify and reference it. fir::GlobalOp fir::FirOpBuilder::createGlobal(mlir::Location loc, diff --git a/flang/lib/Optimizer/Builder/HLFIRTools.cpp b/flang/lib/Optimizer/Builder/HLFIRTools.cpp --- a/flang/lib/Optimizer/Builder/HLFIRTools.cpp +++ b/flang/lib/Optimizer/Builder/HLFIRTools.cpp @@ -15,6 +15,7 @@ #include "flang/Optimizer/Builder/MutableBox.h" #include "flang/Optimizer/Builder/Todo.h" #include "flang/Optimizer/HLFIR/HLFIROps.h" +#include "mlir/IR/BlockAndValueMapping.h" // Return explicit extents. If the base is a fir.box, this won't read it to // return the extents and will instead return an empty vector. @@ -484,3 +485,42 @@ builder.restoreInsertionPoint(insertPt); return elementalOp; } + +hlfir::YieldElementOp +hlfir::inlineElementalOp(mlir::Location loc, fir::FirOpBuilder &builder, + hlfir::ElementalOp elemental, + mlir::ValueRange oneBasedIndices) { + // hlfir.elemental region is a SizedRegion<1>. + assert(elemental.getRegion().hasOneBlock() && + "expect elemental region to have one block"); + mlir::BlockAndValueMapping mapper; + mapper.map(elemental.getIndices(), oneBasedIndices); + mlir::Operation *newOp; + for (auto &op : elemental.getRegion().back().getOperations()) + newOp = builder.clone(op, mapper); + auto yield = mlir::dyn_cast_or_null(newOp); + assert(yield && "last ElementalOp operation must be am hlfir.yield_element"); + return yield; +} + +std::pair> +hlfir::genLoopNest(mlir::Location loc, fir::FirOpBuilder &builder, + mlir::ValueRange extents) { + assert(!extents.empty() && "must have at least one extent"); + auto insPt = builder.saveInsertionPoint(); + llvm::SmallVector indices(extents.size()); + // Build loop nest from column to row. + auto one = builder.create(loc, 1); + mlir::Type indexType = builder.getIndexType(); + unsigned dim = extents.size() - 1; + fir::DoLoopOp innerLoop; + for (auto extent : llvm::reverse(extents)) { + auto ub = builder.createConvert(loc, indexType, extent); + innerLoop = builder.create(loc, one, ub, one); + builder.setInsertionPointToStart(innerLoop.getBody()); + // Reverse the indices so they are in column-major order. + indices[dim--] = innerLoop.getInductionVar(); + } + builder.restoreInsertionPoint(insPt); + return {innerLoop, indices}; +} diff --git a/flang/lib/Optimizer/HLFIR/Transforms/BufferizeHLFIR.cpp b/flang/lib/Optimizer/HLFIR/Transforms/BufferizeHLFIR.cpp --- a/flang/lib/Optimizer/HLFIR/Transforms/BufferizeHLFIR.cpp +++ b/flang/lib/Optimizer/HLFIR/Transforms/BufferizeHLFIR.cpp @@ -95,6 +95,26 @@ TODO(bufferizedExpr.getLoc(), "general extract storage case"); } +static llvm::SmallVector +getIndexExtents(mlir::Location loc, fir::FirOpBuilder &builder, + mlir::Value shape) { + llvm::SmallVector extents; + if (auto s = shape.getDefiningOp()) { + auto e = s.getExtents(); + extents.insert(extents.end(), e.begin(), e.end()); + } else if (auto s = shape.getDefiningOp()) { + auto e = s.getExtents(); + extents.insert(extents.end(), e.begin(), e.end()); + } else { + // TODO: add fir.get_extent ops on fir.shape<> ops. + TODO(loc, "get extents from fir.shape without fir::ShapeOp parent op"); + } + mlir::Type indexType = builder.getIndexType(); + for (auto &extent : extents) + extent = builder.createConvert(loc, indexType, extent); + return extents; +} + static std::pair createTempFromMold(mlir::Location loc, fir::FirOpBuilder &builder, hlfir::Entity mold) { @@ -113,6 +133,21 @@ return {hlfir::Entity{declareOp.getBase()}, falseVal}; } +static std::pair +createArrayTemp(mlir::Location loc, fir::FirOpBuilder &builder, + mlir::Type exprType, mlir::Value shape, + mlir::ValueRange extents, mlir::ValueRange lenParams) { + mlir::Type sequenceType = hlfir::getFortranElementOrSequenceType(exprType); + llvm::StringRef tmpName{".tmp.array"}; + mlir::Value allocmem = builder.createHeapTemporary(loc, sequenceType, tmpName, + extents, lenParams); + auto declareOp = + builder.create(loc, allocmem, tmpName, shape, lenParams, + fir::FortranVariableFlagsAttr{}); + mlir::Value trueVal = builder.createBool(loc, true); + return {hlfir::Entity{declareOp.getBase()}, trueVal}; +} + struct AsExprOpConversion : public mlir::OpConversionPattern { using mlir::OpConversionPattern::OpConversionPattern; explicit AsExprOpConversion(mlir::MLIRContext *ctx) @@ -236,11 +271,20 @@ matchAndRewrite(hlfir::EndAssociateOp endAssociate, OpAdaptor adaptor, mlir::ConversionPatternRewriter &rewriter) const override { mlir::Value mustFree = adaptor.getMustFree(); - if (auto cstMustFree = fir::factory::getIntIfConstant(mustFree)) - if (*cstMustFree == 0) { - rewriter.eraseOp(endAssociate); - return mlir::success(); // nothing to do. - } + mlir::Location loc = endAssociate->getLoc(); + rewriter.eraseOp(endAssociate); + auto genFree = [&]() { + mlir::Value var = adaptor.getVar(); + if (var.getType().isa()) + TODO(loc, "unbox"); + rewriter.create(loc, var); + }; + if (auto cstMustFree = fir::factory::getIntIfConstant(mustFree)) { + if (*cstMustFree != 0) + genFree(); + // else, nothing to do. + return mlir::success(); + } TODO(endAssociate.getLoc(), "conditional free"); } }; @@ -259,6 +303,79 @@ } }; +/// This Listener allows setting both the builder and the rewriter as +/// listeners. This is required when a patterns uses a firBuilder helper that +/// may create illegal operations that will need to be translated and required +/// notifying the rewriter. +struct HLFIRListener : public mlir::OpBuilder::Listener { + HLFIRListener(fir::FirOpBuilder &builder, + mlir::ConversionPatternRewriter &rewriter) + : builder{builder}, rewriter{rewriter} {} + void notifyOperationInserted(mlir::Operation *op) override { + builder.notifyOperationInserted(op); + rewriter.notifyOperationInserted(op); + } + virtual void notifyBlockCreated(mlir::Block *block) override { + builder.notifyBlockCreated(block); + rewriter.notifyBlockCreated(block); + } + fir::FirOpBuilder &builder; + mlir::ConversionPatternRewriter &rewriter; +}; + +struct ElementalOpConversion + : public mlir::OpConversionPattern { + using mlir::OpConversionPattern::OpConversionPattern; + explicit ElementalOpConversion(mlir::MLIRContext *ctx) + : mlir::OpConversionPattern{ctx} {} + mlir::LogicalResult + matchAndRewrite(hlfir::ElementalOp elemental, OpAdaptor adaptor, + mlir::ConversionPatternRewriter &rewriter) const override { + mlir::Location loc = elemental->getLoc(); + auto module = elemental->getParentOfType(); + fir::FirOpBuilder builder(rewriter, fir::getKindMapping(module)); + // The body of the elemental op may contain operation that will require + // to be translated. Notify the rewriter about the cloned operations. + HLFIRListener listener{builder, rewriter}; + builder.setListener(&listener); + + mlir::Value shape = adaptor.getShape(); + auto extents = getIndexExtents(loc, builder, shape); + auto [temp, cleanup] = + createArrayTemp(loc, builder, elemental.getType(), shape, extents, + adaptor.getTypeparams()); + // Generate a loop nest looping around the fir.elemental shape and clone + // fir.elemental region inside the inner loop. + auto [innerLoop, oneBasedLoopIndices] = + hlfir::genLoopNest(loc, builder, extents); + auto insPt = builder.saveInsertionPoint(); + builder.setInsertionPointToStart(innerLoop.getBody()); + auto yield = + hlfir::inlineElementalOp(loc, builder, elemental, oneBasedLoopIndices); + hlfir::Entity elementValue(yield.getElementValue()); + // Skip final AsExpr if any. It would create an element temporary, + // which is no needed since the element will be assigned right away in + // the array temporary. An hlfir.as_expr may have been added if the + // elemental is a "view" over a variable (e.g parentheses or transpose). + if (auto asExpr = elementValue.getDefiningOp()) { + elementValue = hlfir::Entity{asExpr.getVar()}; + if (asExpr->hasOneUse()) + rewriter.eraseOp(asExpr); + } + rewriter.eraseOp(yield); + // Assign the element value to the temp element for this iteration. + auto tempElement = + hlfir::getElementAt(loc, builder, temp, oneBasedLoopIndices); + builder.create(loc, elementValue, tempElement); + builder.restoreInsertionPoint(insPt); + + mlir::Value bufferizedExpr = + packageBufferizedExpr(loc, builder, temp, cleanup); + rewriter.replaceOp(elemental, bufferizedExpr); + return mlir::success(); + } +}; + class BufferizeHLFIR : public hlfir::impl::BufferizeHLFIRBase { public: void runOnOperation() override { @@ -272,11 +389,13 @@ auto module = this->getOperation(); auto *context = &getContext(); mlir::RewritePatternSet patterns(context); - patterns.insert(context); + patterns + .insert(context); mlir::ConversionTarget target(*context); - target.addIllegalOp(); + target.addIllegalOp(); target.markUnknownOpDynamicallyLegal([](mlir::Operation *op) { return llvm::all_of( op->getResultTypes(), diff --git a/flang/test/HLFIR/elemental-codegen.fir b/flang/test/HLFIR/elemental-codegen.fir new file mode 100644 --- /dev/null +++ b/flang/test/HLFIR/elemental-codegen.fir @@ -0,0 +1,109 @@ +// Test hlfir.elemental code generation +// RUN: fir-opt %s --bufferize-hlfir | FileCheck %s + +func.func @numeric_type(%arg0: !fir.ref>, %arg1: !fir.ref>) { + %c10 = arith.constant 10 : index + %c20 = arith.constant 20 : index + %0 = fir.shape %c10, %c20 : (index, index) -> !fir.shape<2> + %1 = hlfir.elemental %0 : (!fir.shape<2>) -> !hlfir.expr<10x20xi32> { + ^bb0(%arg2: index, %arg3: index): + %2 = hlfir.designate %arg0 (%arg2, %arg3) : (!fir.ref>, index, index) -> !fir.ref + %3 = hlfir.designate %arg1 (%arg2, %arg3) : (!fir.ref>, index, index) -> !fir.ref + %4 = fir.load %2 : !fir.ref + %5 = fir.load %3 : !fir.ref + %6 = arith.addi %4, %5 : i32 + hlfir.yield_element %6 : i32 + } + return +} +// CHECK-LABEL: func.func @numeric_type( +// CHECK-SAME: %[[VAL_0:[^:]*]]: !fir.ref>, +// CHECK-SAME: %[[VAL_1:.*]]: !fir.ref>) { +// CHECK: %[[VAL_2:.*]] = arith.constant 10 : index +// CHECK: %[[VAL_3:.*]] = arith.constant 20 : index +// CHECK: %[[VAL_4:.*]] = fir.shape %[[VAL_2]], %[[VAL_3]] : (index, index) -> !fir.shape<2> +// CHECK: %[[VAL_5:.*]] = fir.allocmem !fir.array<10x20xi32> {bindc_name = ".tmp.array", uniq_name = ""} +// CHECK: %[[VAL_6:.*]]:2 = hlfir.declare %[[VAL_5]](%[[VAL_4]]) {uniq_name = ".tmp.array"} : (!fir.heap>, !fir.shape<2>) -> (!fir.heap>, !fir.heap>) +// CHECK: %[[VAL_7:.*]] = arith.constant true +// CHECK: %[[VAL_8:.*]] = arith.constant 1 : index +// CHECK: fir.do_loop %[[VAL_9:.*]] = %[[VAL_8]] to %[[VAL_3]] step %[[VAL_8]] { +// CHECK: fir.do_loop %[[VAL_10:.*]] = %[[VAL_8]] to %[[VAL_2]] step %[[VAL_8]] { +// CHECK: %[[VAL_11:.*]] = hlfir.designate %[[VAL_0]] (%[[VAL_10]], %[[VAL_9]]) : (!fir.ref>, index, index) -> !fir.ref +// CHECK: %[[VAL_12:.*]] = hlfir.designate %[[VAL_1]] (%[[VAL_10]], %[[VAL_9]]) : (!fir.ref>, index, index) -> !fir.ref +// CHECK: %[[VAL_13:.*]] = fir.load %[[VAL_11]] : !fir.ref +// CHECK: %[[VAL_14:.*]] = fir.load %[[VAL_12]] : !fir.ref +// CHECK: %[[VAL_15:.*]] = arith.addi %[[VAL_13]], %[[VAL_14]] : i32 +// CHECK: %[[VAL_16:.*]] = hlfir.designate %[[VAL_6]]#0 (%[[VAL_10]], %[[VAL_9]]) : (!fir.heap>, index, index) -> !fir.ref +// CHECK: hlfir.assign %[[VAL_15]] to %[[VAL_16]] : i32, !fir.ref +// CHECK: } +// CHECK: } +// CHECK: %[[VAL_17:.*]] = fir.undefined tuple>, i1> +// CHECK: %[[VAL_18:.*]] = fir.insert_value %[[VAL_17]], %[[VAL_7]], [1 : index] : (tuple>, i1>, i1) -> tuple>, i1> +// CHECK: %[[VAL_19:.*]] = fir.insert_value %[[VAL_18]], %[[VAL_6]]#0, [0 : index] : (tuple>, i1>, !fir.heap>) -> tuple>, i1> + + +func.func @char_type(%arg0: !fir.box>>, %arg1: index, %arg2: index, %arg3: index) { + %0 = fir.shape %arg1 : (index) -> !fir.shape<1> + %1 = hlfir.elemental %0 typeparams %arg2 : (!fir.shape<1>, index) -> !hlfir.expr> { + ^bb0(%arg4: index): + %2 = hlfir.designate %arg0 (%arg4) typeparams %arg3 : (!fir.box>>, index, index) -> !fir.boxchar<1> + %3 = hlfir.concat %2, %2 len %arg2 : (!fir.boxchar<1>, !fir.boxchar<1>, index) -> !hlfir.expr> + hlfir.yield_element %3 : !hlfir.expr> + } + return +} +// CHECK-LABEL: func.func @char_type( +// CHECK-SAME: %[[VAL_0:.*]]: !fir.box>>, +// CHECK-SAME: %[[VAL_1:[^:]*]]: index, +// CHECK-SAME: %[[VAL_2:[^:]*]]: index, +// CHECK-SAME: %[[VAL_3:[^:]*]]: index) { +// CHECK: %[[VAL_4:.*]] = fir.shape %[[VAL_1]] : (index) -> !fir.shape<1> +// CHECK: %[[VAL_5:.*]] = fir.allocmem !fir.array>(%[[VAL_2]] : index), %[[VAL_1]] {bindc_name = ".tmp.array", uniq_name = ""} +// CHECK: %[[VAL_6:.*]]:2 = hlfir.declare %[[VAL_5]](%[[VAL_4]]) typeparams %[[VAL_2]] {uniq_name = ".tmp.array"} : (!fir.heap>>, !fir.shape<1>, index) -> (!fir.box>>, !fir.heap>>) +// CHECK: %[[VAL_7:.*]] = arith.constant true +// CHECK: %[[VAL_8:.*]] = arith.constant 1 : index +// CHECK: fir.do_loop %[[VAL_9:.*]] = %[[VAL_8]] to %[[VAL_1]] step %[[VAL_8]] { +// CHECK: %[[VAL_10:.*]] = hlfir.designate %[[VAL_0]] (%[[VAL_9]]) typeparams %[[VAL_3]] : (!fir.box>>, index, index) -> !fir.boxchar<1> + // concatenation +// CHECK: %[[VAL_30:.*]]:2 = hlfir.declare %[[VAL_14:.*]] typeparams %[[VAL_13:.*]] {uniq_name = "tmp"} : (!fir.ref>, index) -> (!fir.boxchar<1>, !fir.ref>) +// CHECK: %[[VAL_31:.*]] = arith.constant false +// CHECK: %[[VAL_32:.*]] = fir.undefined tuple, i1> +// CHECK: %[[VAL_33:.*]] = fir.insert_value %[[VAL_32]], %[[VAL_31]], [1 : index] : (tuple, i1>, i1) -> tuple, i1> +// CHECK: %[[VAL_34:.*]] = fir.insert_value %[[VAL_33]], %[[VAL_30]]#0, [0 : index] : (tuple, i1>, !fir.boxchar<1>) -> tuple, i1> +// CHECK: %[[VAL_35:.*]] = hlfir.designate %[[VAL_6]]#0 (%[[VAL_9]]) typeparams %[[VAL_2]] : (!fir.box>>, index, index) -> !fir.boxchar<1> +// CHECK: hlfir.assign %[[VAL_30]]#0 to %[[VAL_35]] : !fir.boxchar<1>, !fir.boxchar<1> +// CHECK: } +// CHECK: %[[VAL_36:.*]] = fir.undefined tuple>>, i1> +// CHECK: %[[VAL_37:.*]] = fir.insert_value %[[VAL_36]], %[[VAL_7]], [1 : index] : (tuple>>, i1>, i1) -> tuple>>, i1> +// CHECK: %[[VAL_38:.*]] = fir.insert_value %[[VAL_37]], %[[VAL_6]]#0, [0 : index] : (tuple>>, i1>, !fir.box>>) -> tuple>>, i1> + + +func.func @derived_transpose(%arg0: !fir.box>>, %arg1: index, %arg2: index) { + %0 = fir.shape %arg2, %arg1 : (index, index) -> !fir.shape<2> + %1 = hlfir.elemental %0 : (!fir.shape<2>) -> !hlfir.expr> { + ^bb0(%arg4: index, %arg5: index): + %2 = hlfir.designate %arg0 (%arg4, %arg5) : (!fir.box>>, index, index) -> !fir.box> + %3 = hlfir.as_expr %2 : (!fir.box>) -> !hlfir.expr> + hlfir.yield_element %3 : !hlfir.expr> + } + return +} +// CHECK-LABEL: func.func @derived_transpose( +// CHECK-SAME: %[[VAL_0:.*]]: !fir.box>>, +// CHECK-SAME: %[[VAL_1:[^:]*]]: index, +// CHECK-SAME: %[[VAL_2:.*]]: index) { +// CHECK: %[[VAL_3:.*]] = fir.shape %[[VAL_2]], %[[VAL_1]] : (index, index) -> !fir.shape<2> +// CHECK: %[[VAL_4:.*]] = fir.allocmem !fir.array>, %[[VAL_2]], %[[VAL_1]] {bindc_name = ".tmp.array", uniq_name = ""} +// CHECK: %[[VAL_5:.*]]:2 = hlfir.declare %[[VAL_4]](%[[VAL_3]]) {uniq_name = ".tmp.array"} : (!fir.heap>>, !fir.shape<2>) -> (!fir.box>>, !fir.heap>>) +// CHECK: %[[VAL_6:.*]] = arith.constant true +// CHECK: %[[VAL_7:.*]] = arith.constant 1 : index +// CHECK: fir.do_loop %[[VAL_8:.*]] = %[[VAL_7]] to %[[VAL_1]] step %[[VAL_7]] { +// CHECK: fir.do_loop %[[VAL_9:.*]] = %[[VAL_7]] to %[[VAL_2]] step %[[VAL_7]] { +// CHECK: %[[VAL_10:.*]] = hlfir.designate %[[VAL_0]] (%[[VAL_9]], %[[VAL_8]]) : (!fir.box>>, index, index) -> !fir.box> +// CHECK: %[[VAL_11:.*]] = hlfir.designate %[[VAL_5]]#0 (%[[VAL_9]], %[[VAL_8]]) : (!fir.box>>, index, index) -> !fir.ref> +// CHECK: hlfir.assign %[[VAL_10]] to %[[VAL_11]] : !fir.box>, !fir.ref> +// CHECK: } +// CHECK: } +// CHECK: %[[VAL_12:.*]] = fir.undefined tuple>>, i1> +// CHECK: %[[VAL_13:.*]] = fir.insert_value %[[VAL_12]], %[[VAL_6]], [1 : index] : (tuple>>, i1>, i1) -> tuple>>, i1> +// CHECK: %[[VAL_14:.*]] = fir.insert_value %[[VAL_13]], %[[VAL_5]]#0, [0 : index] : (tuple>>, i1>, !fir.box>>) -> tuple>>, i1>