diff --git a/flang/lib/Optimizer/CodeGen/CodeGen.cpp b/flang/lib/Optimizer/CodeGen/CodeGen.cpp --- a/flang/lib/Optimizer/CodeGen/CodeGen.cpp +++ b/flang/lib/Optimizer/CodeGen/CodeGen.cpp @@ -23,6 +23,7 @@ #include "mlir/IR/Matchers.h" #include "mlir/Pass/Pass.h" #include "llvm/ADT/ArrayRef.h" +#include #define DEBUG_TYPE "flang-codegen" @@ -52,6 +53,10 @@ mlir::Region::iterator(insertBefore)); } +inline mlir::Type getVoidPtrType(mlir::MLIRContext *context) { + return mlir::LLVM::LLVMPointerType::get(mlir::IntegerType::get(context, 8)); +} + namespace { /// FIR conversion pattern template template @@ -64,6 +69,9 @@ mlir::Type convertType(mlir::Type ty) const { return lowerTy().convertType(ty); } + mlir::Type voidPtrTy() const { + return getVoidPtrType(&lowerTy().getContext()); + } mlir::LLVM::ConstantOp genI32Constant(mlir::Location loc, mlir::ConversionPatternRewriter &rewriter, @@ -187,6 +195,12 @@ return type; } + // Return LLVM type of the base address given the LLVM type + // of the related descriptor (lowered fir.box type). + static mlir::Type getBaseAddrTypeFromBox(mlir::Type type) { + return getBoxEleTy(type, {0}); + } + template mlir::LLVM::GEPOp genGEP(mlir::Location loc, mlir::Type ty, mlir::ConversionPatternRewriter &rewriter, @@ -216,6 +230,16 @@ return val; } + mlir::Value + loadStrideFromBox(mlir::Location loc, mlir::Value box, unsigned dim, + mlir::ConversionPatternRewriter &rewriter) const { + auto idxTy = lowerTy().indexType(); + auto c0 = genConstantOffset(loc, rewriter, 0); + auto c7 = genConstantOffset(loc, rewriter, 7); + auto dimValue = genConstantIndex(loc, idxTy, rewriter, dim); + return loadFromOffset(loc, box, c0, c7, dimValue, 2, idxTy, rewriter); + } + fir::LLVMTypeConverter &lowerTy() const { return *static_cast(this->getTypeConverter()); } @@ -2089,6 +2113,205 @@ } }; +/// Convert to (memory) reference to a reference to a subobject. +/// The coordinate_of op is a Swiss army knife operation that can be used on +/// (memory) references to records, arrays, complex, etc. as well as boxes. +/// With unboxed arrays, there is the restriction that the array have a static +/// shape in all but the last column. +struct CoordinateOpConversion + : public FIROpAndTypeConversion { + using FIROpAndTypeConversion::FIROpAndTypeConversion; + + mlir::LogicalResult + doRewrite(fir::CoordinateOp coor, mlir::Type ty, OpAdaptor adaptor, + mlir::ConversionPatternRewriter &rewriter) const override { + auto operands = adaptor.getOperands(); + + mlir::Location loc = coor.getLoc(); + mlir::Value base = operands[0]; + mlir::Type baseObjectTy = coor.getBaseType(); + mlir::Type objectTy = fir::dyn_cast_ptrOrBoxEleTy(baseObjectTy); + assert(objectTy && "`fir.coordinate_of` expects a reference type"); + + if (fir::hasDynamicSize(fir::unwrapSequenceType(objectTy))) + return mlir::emitError( + loc, + "`fir.coordinate_of` with a dynamic element size is unsupported"); + + // Complex type - basically, extract the real or imaginary part + if (fir::isa_complex(objectTy)) { + mlir::LLVM::ConstantOp c0 = + genConstantIndex(loc, lowerTy().indexType(), rewriter, 0); + SmallVector offs = {c0, operands[1]}; + mlir::Value gep = genGEP(loc, ty, rewriter, base, offs); + rewriter.replaceOp(coor, gep); + return success(); + } + + // Box type - get the base pointer from the box + if (auto boxTy = baseObjectTy.dyn_cast()) { + doRewriteBox(coor, ty, operands, loc, rewriter); + return success(); + } + + // Sequence type (e.g. fir.array + if (auto arrTy = objectTy.dyn_cast()) { + doRewriteSequence(loc); + return success(); + } + + return mlir::emitError( + loc, "fir.coordinate_of base operand has unsupported type"); + } + + unsigned getFieldNumber(fir::RecordType ty, mlir::Value op) const { + return fir::hasDynamicSize(ty) + ? op.getDefiningOp() + ->getAttrOfType("field") + .getInt() + : getIntValue(op); + } + + int64_t getIntValue(mlir::Value val) const { + if (val) { + if (auto *defop = val.getDefiningOp()) { + if (auto constOp = dyn_cast(defop)) + return constOp.value(); + if (auto llConstOp = dyn_cast(defop)) + if (auto attr = llConstOp.value().dyn_cast()) + return attr.getValue().getSExtValue(); + } + fir::emitFatalError(val.getLoc(), "must be a constant"); + } + llvm_unreachable("must not be null value"); + } + +private: + void doRewriteBox(fir::CoordinateOp coor, mlir::Type ty, + mlir::ValueRange operands, mlir::Location loc, + mlir::ConversionPatternRewriter &rewriter) const { + mlir::Type baseObjectTy = coor.getBaseType(); + mlir::Value boxBaseAddr = operands[0]; + + assert(baseObjectTy.dyn_cast() && "This is not a `fir.box`"); + + // 1. SPECIAL CASE (uses `fir.len_param_index`): + // %box = ... : !fir.box> + // %lenp = fir.len_param_index len1, !fir.type + // %addr = coordinate_of %box, %lenp + if (coor.getNumOperands() == 2) { + auto *coordinateDef = (*coor.coor().begin()).getDefiningOp(); + if (isa_and_nonnull(coordinateDef)) { + // With the coordinate coming from `fir.len_param_index`, we know that + // the 2nd operand is a valid byte offset. + mlir::Value lenParam = operands[1]; + + auto baseAddrAsVoidPtr = rewriter.create( + loc, voidPtrTy(), boxBaseAddr); + auto gep = genGEP(loc, ty, rewriter, baseAddrAsVoidPtr, lenParam); + rewriter.replaceOpWithNewOp(coor, ty, gep); + + return; + } + } + + // 2. GENERAL CASE: + // %box = ... : !fix.box> + // %idx = ... : index + // %addr = coodinate_of %box, %idx : !fir.ref + mlir::Type objectTy = fir::dyn_cast_ptrOrBoxEleTy(baseObjectTy); + mlir::Value objectBaseAddr = + loadBaseAddrFromBox(loc, getBaseAddrTypeFromBox(boxBaseAddr.getType()), + boxBaseAddr, rewriter); + mlir::Type voidPtrTy = ::getVoidPtrType(coor.getContext()); + + // `fir.array` + if (auto arrTy = objectTy.dyn_cast()) { + unsigned dims = arrTy.getDimension(); + assert(operands.size() - 1 == dims && "Wrong number of indices"); + // Applies byte strides from the box. Ignore lower bound from box + // since fir.coordinate_of indexes are zero based. Lowering takes care + // of lower bound aspects. This both accounts for dynamically sized + // types and non contiguous arrays. + mlir::Type idxTy = lowerTy().indexType(); + mlir::Value off = genConstantIndex(loc, idxTy, rewriter, 0); + for (unsigned dim = 0; dim < dims; ++dim) { + mlir::Value stride = loadStrideFromBox(loc, boxBaseAddr, dim, rewriter); + auto sc = rewriter.create(loc, idxTy, + operands[1 + dim], stride); + off = rewriter.create(loc, idxTy, sc, off); + } + auto voidPtrBase = rewriter.create(loc, voidPtrTy, + objectBaseAddr); + SmallVector args{voidPtrBase, off}; + objectBaseAddr = rewriter.create(loc, voidPtrTy, args); + + rewriter.replaceOpWithNewOp(coor, ty, + objectBaseAddr); + return; + } + + // `fir.type` + if (auto seqTy = objectTy.dyn_cast()) { + mlir::LLVM::ConstantOp c0 = + genConstantIndex(loc, lowerTy().indexType(), rewriter, 0); + for (unsigned i = 1, last = operands.size(); i < last; ++i) { + auto seqRefTy = + mlir::LLVM::LLVMPointerType::get(lowerTy().convertType(seqTy)); + auto nxtOpnd = operands[i]; + auto memObj = rewriter.create(loc, seqRefTy, + objectBaseAddr); + llvm::SmallVector args = {memObj, c0, nxtOpnd}; + objectTy = seqTy.getType(getFieldNumber(seqTy, nxtOpnd)); + auto llvmCty = lowerTy().convertType(objectTy); + auto gep = rewriter.create( + loc, mlir::LLVM::LLVMPointerType::get(llvmCty), args); + objectBaseAddr = + rewriter.create(loc, voidPtrTy, gep); + } + + rewriter.replaceOpWithNewOp(coor, ty, + objectBaseAddr); + return; + } + + fir::emitFatalError(loc, "unexpected type in coordinate_of"); + } + void doRewriteSequence(mlir::Location loc) const { + TODO(loc, "`fir.coordinate_of` codegen for sequence types"); + } +}; + +// ----> DELETEME +struct LenParamIndexOpConversion + : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + // FIXME: this should be specialized by the runtime target + mlir::LogicalResult + matchAndRewrite(fir::LenParamIndexOp lenp, OpAdaptor adaptor, + mlir::ConversionPatternRewriter &rewriter) const override { + auto ity = lowerTy().indexType(); + auto onty = lenp.getOnType(); + // size of portable descriptor + const unsigned boxsize = 24; // FIXME + unsigned offset = boxsize; + // add the size of the rows of triples + if (auto arr = onty.dyn_cast()) + offset += 3 * arr.getDimension(); + + // advance over some addendum fields + const unsigned addendumOffset{sizeof(void *) + sizeof(uint64_t)}; + offset += addendumOffset; + // add the offset into the LENs + offset += 0; // FIXME + auto attr = rewriter.getI64IntegerAttr(offset); + rewriter.replaceOpWithNewOp(lenp, ity, attr); + return success(); + } +}; +// <---- DELETEME + } // namespace namespace { @@ -2117,6 +2340,7 @@ BoxDimsOpConversion, BoxEleSizeOpConversion, BoxIsAllocOpConversion, BoxIsArrayOpConversion, BoxIsPtrOpConversion, BoxProcHostOpConversion, BoxRankOpConversion, BoxTypeDescOpConversion, CallOpConversion, + CoordinateOpConversion, /*DELETEME*/ LenParamIndexOpConversion, CmpcOpConversion, ConstcOpConversion, ConvertOpConversion, DispatchOpConversion, DispatchTableOpConversion, DTEntryOpConversion, DivcOpConversion, EmboxOpConversion, EmboxCharOpConversion, diff --git a/flang/test/Fir/convert-to-llvm-invalid.fir b/flang/test/Fir/convert-to-llvm-invalid.fir --- a/flang/test/Fir/convert-to-llvm-invalid.fir +++ b/flang/test/Fir/convert-to-llvm-invalid.fir @@ -180,3 +180,12 @@ // expected-error@+1{{failed to legalize operation 'fir.end'}} "fir.end"() : () -> () + +// ----- + +func @bad_coordinate_of(%arg0: !fir.box>>, %arg1: index) { +// expected-error@+2{{`fir.coordinate_of` with a dynamic element size is unsupported}} +// expected-error@+1{{failed to legalize operation 'fir.coordinate_of'}} + %p = fir.coordinate_of %arg0, %arg1 : (!fir.box>>, index) -> !fir.ref + return +} diff --git a/flang/test/Fir/convert-to-llvm.fir b/flang/test/Fir/convert-to-llvm.fir --- a/flang/test/Fir/convert-to-llvm.fir +++ b/flang/test/Fir/convert-to-llvm.fir @@ -1530,3 +1530,146 @@ // CHECK: %[[TDESC:.*]] = llvm.mlir.addressof @_QMtest_dinitE.dt.tseq : !llvm.ptr // CHECK: %[[TDESC_CAST:.*]] = llvm.bitcast %22 : !llvm.ptr to !llvm.ptr // CHECK: %{{.*}} = llvm.insertvalue %[[TDESC_CAST]], %{{.*}}[7 : i32] : !llvm.struct<(ptr>, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, ptr, array<1 x i{{.*}}>)> + +// ----- + +// Test `fir.coordinate_of` conversion + +// 1. COMPLEX TYPE (`fir.complex` is a special case) +// Complex type wrapped in `fir.ref` +func @coordinate_ref_complex(%arg0: !fir.ref>, %arg1: index) { + %p = fir.coordinate_of %arg0, %arg1 : (!fir.ref>, index) -> !fir.ref + return +} +// CHECK-LABEL: llvm.func @coordinate_ref_complex +// CHECK-SAME: %[[ARG0:.*]]: !llvm.ptr> +// CHECK-SAME: %[[COORDINATE:.*]]: i64) { +// CHECK: %[[C0:.*]] = llvm.mlir.constant(0 : i64) : i64 +// CHECK: %{{.*}} = llvm.getelementptr %[[ARG0]][%[[C0]], %[[COORDINATE]]] : (!llvm.ptr>, i64, i64) -> !llvm.ptr +// CHECK-NEXT: llvm.return + +// Complex type wrapped in `fir.box` +func @coordinate_box_complex(%arg0: !fir.box>, %arg1: index) { + %p = fir.coordinate_of %arg0, %arg1 : (!fir.box>, index) -> !fir.ref + return +} +// CHECK-LABEL: llvm.func @coordinate_box_complex +// CHECK-SAME: %[[BOX:.*]]: !llvm.ptr>, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}})>> +// CHECK-SAME: %[[COORDINATE:.*]]: i64) { +// CHECK: %[[C0:.*]] = llvm.mlir.constant(0 : i64) : i64 +// CHECK: %{{.*}} = llvm.getelementptr %[[BOX]][%[[C0]], %[[COORDINATE]]] : (!llvm.ptr>, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}})>>, i64, i64) -> !llvm.ptr +// CHECK-NEXT: llvm.return + +// 2. BOX TYPE (objects wrapped in `fir.box`) +// Derived type - basic case +func @coordinate_box_derived(%arg0: !fir.box>) { + %idx = arith.constant 1 : i32 + %q = fir.coordinate_of %arg0, %idx : (!fir.box>, i32) -> !fir.ref + return +} +// CHECK-LABEL: llvm.func @coordinate_box_derived +// CHECK-SAME: %[[BOX:.*]]: !llvm.ptr>, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, ptr, array<1 x i64>)>>) +// CHECK: %[[COORDINATE:.*]] = llvm.mlir.constant(1 : i32) : i32 +// CHECK: %[[C0_1:.*]] = llvm.mlir.constant(0 : i32) : i32 +// CHECK: %[[C0_2:.*]] = llvm.mlir.constant(0 : i32) : i32 +// CHECK: %[[DERRIVED_ADDR:.*]] = llvm.getelementptr %[[BOX]][%[[C0_1]], %[[C0_2]]] : (!llvm.ptr>, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, ptr, array<1 x i64>)>>, i32, i32) -> !llvm.ptr>> +// CHECK: %[[DERRIVED_VAL:.*]] = llvm.load %[[DERRIVED_ADDR]] : !llvm.ptr>> +// CHECK: %[[C0_3:.*]] = llvm.mlir.constant(0 : i64) : i64 +// FIXME: This cast seems redundant +// CHECK: %[[DERRIVED_CAST:.*]] = llvm.bitcast %[[DERRIVED_VAL]] : !llvm.ptr> to !llvm.ptr> +// CHECK: %[[SUBOBJECT_ADDR:.*]] = llvm.getelementptr %[[DERRIVED_CAST]][%[[C0_3]], %[[COORDINATE]]] : (!llvm.ptr>, i64, i32) -> !llvm.ptr +// FIXMR: These casts seem redundant +// CHECK: %[[CAST_1:.*]] = llvm.bitcast %[[SUBOBJECT_ADDR]] : !llvm.ptr to !llvm.ptr +// CHECK: %{{.*}} = llvm.bitcast %[[CAST_1]] : !llvm.ptr to !llvm.ptr +// CHECK-NEXT: llvm.return + +// Derived type via `fir.len_param_index` +func @coordinate_box_derived_with_fir_len(%arg0: !fir.box>) { + %e = fir.len_param_index len1, !fir.type + %q = fir.coordinate_of %arg0, %e : (!fir.box>, !fir.len) -> !fir.ref + return +} +// CHECK-LABEL: llvm.func @coordinate_box_derived_with_fir_len +// CHECK-SAME: %[[ARG0:.*]]: !llvm.ptr>, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, ptr, array<1 x i64>)>>) +// TODO: Where is 40 coming from? +// CHECK: %[[C40:.*]] = llvm.mlir.constant(40 : i64) : i64 +// CHECK: %[[ARG0_AS_VOID_PTR:.*]] = llvm.bitcast %[[ARG0]] : !llvm.ptr>, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, ptr, array<1 x i64>)>> to !llvm.ptr +// CHECK: %[[SUBOBJECT_ADDR:.*]] = llvm.getelementptr %[[ARG0_AS_VOID_PTR]][%[[C40]]] : (!llvm.ptr, i64) -> !llvm.ptr +// FIXME: This cast seems redundant +// CHECK: %{{.*}} = llvm.bitcast %[[SUBOBJECT_ADDR]] : !llvm.ptr to !llvm.ptr +// CHECK-NEXT: llvm.return + +// 3. BOX TYPE - `fir.array` wrapped in `fir.box` +// `fir.array` inside a `fir.box` (1d) +func @coordinate_box_array_1d(%arg0: !fir.box>, %arg1: index) { + %p = fir.coordinate_of %arg0, %arg1 : (!fir.box>, index) -> !fir.ref + return +} +// CHECK-LABEL: llvm.func @coordinate_box_array_1d +// CHECK-SAME: %[[BOX:.*]]: !llvm.ptr>, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, array<1 x array<3 x i64>>)>> +// CHECK-SAME: %[[COORDINATE:.*]]: i64 +// There's only one box here. It's index is `0`. Generate it. +// CHECK-NEXT: %[[BOX_IDX:.*]] = llvm.mlir.constant(0 : i32) : i32 +// CHECK-NEXT: %[[BOX_1ST_ELEM_IDX:.*]] = llvm.mlir.constant(0 : i32) : i32 +// CHECK-NEXT: %[[ARRAY_ADDR:.*]] = llvm.getelementptr %[[BOX]][%[[BOX_IDX]], %[[BOX_1ST_ELEM_IDX]]] : (!llvm.ptr>, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, array<1 x array<3 x i64>>)>>, i32, i32) -> !llvm.ptr>> +// CHECK-NEXT: %[[ARRAY_OBJECT:.*]] = llvm.load %[[ARRAY_ADDR]] : !llvm.ptr>> +// CHECK-NEXT: %[[OFFSET_INIT:.*]] = llvm.mlir.constant(0 : i64) : i64 +// Same as [[BOX_IDX]], just recreated. +// CHECK-NEXT: %[[BOX_IDX_1:.*]] = llvm.mlir.constant(0 : i32) : i32 +// Index of the array that contains the CFI_dim_t objects +// CHECK-NEXT: %[[CFI_DIM_IDX:.*]] = llvm.mlir.constant(7 : i32) : i32 +// Index of the 1st CFI_dim_t object (corresonds the the 1st dimension) +// CHECK-NEXT: %[[DIM_1_IDX:.*]] = llvm.mlir.constant(0 : i64) : i64 +// Index of the memory stride within a CFI_dim_t object +// CHECK-NEXT: %[[DIM_1_MEM_STRIDE:.*]] = llvm.mlir.constant(2 : i32) : i32 +// CHECK-NEXT: %[[DIM_1_MEM_STRIDE_ADDR:.*]] = llvm.getelementptr %[[BOX]][%[[BOX_IDX_1]], %[[CFI_DIM_IDX]], %[[DIM_1_IDX]], %[[DIM_1_MEM_STRIDE]]] : (!llvm.ptr>, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, array<1 x array<3 x i64>>)>>, i32, i32, i64, i32) -> !llvm.ptr +// CHECK-NEXT: %[[DIM_1_MEM_STRIDE_VAL:.*]] = llvm.load %[[DIM_1_MEM_STRIDE_ADDR]] : !llvm.ptr +// CHECK-NEXT: %[[BYTE_OFFSET:.*]] = llvm.mul %[[COORDINATE]], %[[DIM_1_MEM_STRIDE_VAL]] : i64 +// CHECK-NEXT: %[[SUBOJECT_OFFSET:.*]] = llvm.add %[[BYTE_OFFSET]], %[[OFFSET_INIT]] : i64 +// CHECK-NEXT: %[[ARRAY_ADDR_AS_VOID_PTR:.*]] = llvm.bitcast %[[ARRAY_OBJECT]] : !llvm.ptr> to !llvm.ptr +// CHECK-NEXT: %[[SUBOBJECT_ADDR:.*]] = llvm.getelementptr %[[ARRAY_ADDR_AS_VOID_PTR]][%[[SUBOJECT_OFFSET]]] : (!llvm.ptr, i64) -> !llvm.ptr +// CHECK-NEXT: %[[RETURN_VAL:.*]] = llvm.bitcast %[[SUBOBJECT_ADDR]] : !llvm.ptr to !llvm.ptr +// CHECK-NEXT: llvm.return + +// `fir.array` inside a `fir.box` (2d) +func @coordinate_box_array_2d(%arg0: !fir.box>, %arg1: index, %arg2: index) { + %p = fir.coordinate_of %arg0, %arg1, %arg2 : (!fir.box>, index, index) -> !fir.ref + return +} +// CHECK-LABEL: llvm.func @coordinate_box_array_2d +// CHECK-SAME: %[[BOX:.*]]: !llvm.ptr>>, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, array<2 x array<3 x i64>>)>> +// CHECK-SAME: %[[COORDINATE_1:.*]]: i64, %[[COORDINATE_2:.*]]: i64) +// There's only one box here. It's index is `0`. Generate it. +// CHECK-NEXT: %[[BOX_IDX:.*]] = llvm.mlir.constant(0 : i32) : i32 +// CHECK-NEXT: %[[BOX_1ST_ELEM_IDX:.*]] = llvm.mlir.constant(0 : i32) : i32 +// CHECK-NEXT: %[[ARRAY_ADDR:.*]] = llvm.getelementptr %[[BOX]][%[[BOX_IDX]], %[[BOX_1ST_ELEM_IDX]]] : (!llvm.ptr>>, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, array<2 x array<3 x i64>>)>>, i32, i32) -> !llvm.ptr>>> +// CHECK-NEXT: %[[ARRAY_OBJECT:.*]] = llvm.load %[[ARRAY_ADDR]] : !llvm.ptr>>> +// CHECK-NEXT: %[[OFFSET_INIT:.*]] = llvm.mlir.constant(0 : i64) : i64 +// Same as [[BOX_IDX]], just recreated. +// CHECK-NEXT: %[[BOX_IDX_1:.*]] = llvm.mlir.constant(0 : i32) : i32 +// Index of the array that contains the CFI_dim_t objects +// CHECK-NEXT: %[[CFI_DIM_IDX:.*]] = llvm.mlir.constant(7 : i32) : i32 +// Index of the 1st CFI_dim_t object (corresonds the the 1st dimension) +// CHECK-NEXT: %[[DIM_1_IDX:.*]] = llvm.mlir.constant(0 : i64) : i64 +// Index of the memory stride within a CFI_dim_t object +// CHECK-NEXT: %[[DIM_1_MEM_STRIDE:.*]] = llvm.mlir.constant(2 : i32) : i32 +// CHECK-NEXT: %[[DIM_1_MEM_STRIDE_ADDR:.*]] = llvm.getelementptr %[[BOX]][%[[BOX_IDX_1]], %[[CFI_DIM_IDX]], %[[DIM_1_IDX]], %[[DIM_1_MEM_STRIDE]]] : (!llvm.ptr>>, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, array<2 x array<3 x i64>>)>>, i32, i32, i64, i32) -> !llvm.ptr +// CHECK-NEXT: %[[DIM_1_MEM_STRIDE_VAL:.*]] = llvm.load %[[DIM_1_MEM_STRIDE_ADDR]] : !llvm.ptr +// CHECK-NEXT: %[[BYTE_OFFSET_1:.*]] = llvm.mul %[[COORDINATE_1]], %[[DIM_1_MEM_STRIDE_VAL]] : i64 +// CHECK-NEXT: %[[SUBOBJECT_OFFSET_1:.*]] = llvm.add %[[BYTE_OFFSET]], %[[OFFSET_INIT]] : i64 +// Same as [[BOX_IDX]], just recreated. +// CHECK-NEXT: %[[BOX_IDX_2:.*]] = llvm.mlir.constant(0 : i32) : i32 +// Index of the array that contains the CFI_dim_t objects (same as CFI_DIM_IDX, just recreated) +// CHECK-NEXT: %[[CFI_DIM_IDX_1:.*]] = llvm.mlir.constant(7 : i32) : i32 +// Index of the 1st CFI_dim_t object (corresonds the the 2nd dimension) +// CHECK-NEXT: %[[DIM_2_IDX:.*]] = llvm.mlir.constant(1 : i64) : i64 +// Index of the memory stride within a CFI_dim_t object +// CHECK-NEXT: %[[DIM_2_MEM_STRIDE:.*]] = llvm.mlir.constant(2 : i32) : i32 +// CHECK-NEXT: %[[DIM_2_MEM_STRIDE_ADDR:.*]] = llvm.getelementptr %[[BOX]][%[[BOX_IDX_2]], %[[CFI_DIM_IDX_1]], %[[DIM_2_IDX]], %[[DIM_2_MEM_STRIDE]]] : (!llvm.ptr>>, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, array<2 x array<3 x i64>>)>>, i32, i32, i64, i32) -> !llvm.ptr +// CHECK-NEXT: %[[DIM_2_MEM_STRIDE_VAL:.*]] = llvm.load %[[DIM_2_MEM_STRIDE_ADDR]] : !llvm.ptr +// CHECK-NEXT: %[[BYTE_OFFSET_2:.*]] = llvm.mul %[[COORDINATE_2]], %[[DIM_2_MEM_STRIDE_VAL]] : i64 +// CHECK-NEXT: %[[SUBOBJECT_OFFSET_2:.*]] = llvm.add %[[BYTE_OFFSET_2]], %[[SUBOBJECT_OFFSET_1]] : i64 +// CHECK-NEXT: %[[ARRAY_ADDR_AS_VOID_PTR:.*]] = llvm.bitcast %[[ARRAY_OBJECT]] : !llvm.ptr>> to !llvm.ptr +// CHECK-NEXT: %[[SUBOBJECT_ADDR:.*]] = llvm.getelementptr %[[ARRAY_ADDR_AS_VOID_PTR]][%[[SUBOBJECT_OFFSET_2]]] : (!llvm.ptr, i64) -> !llvm.ptr +// CHECK-NEXT: %[[RETURN_VAL:.*]] = llvm.bitcast %[[SUBOBJECT_ADDR]] : !llvm.ptr to !llvm.ptr +// CHECK-NEXT: llvm.return