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 @@ -52,6 +52,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 @@ -187,6 +191,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, {kAddrPosInBox}); + } + template mlir::LLVM::GEPOp genGEP(mlir::Location loc, mlir::Type ty, mlir::ConversionPatternRewriter &rewriter, @@ -216,6 +226,20 @@ return val; } + mlir::Value + loadStrideFromBox(mlir::Location loc, mlir::Value box, unsigned dim, + mlir::ConversionPatternRewriter &rewriter) const { + using ConstOp = typename mlir::LLVM::ConstantOp; + + mlir::Type idxTy = lowerTy().indexType(); + ConstOp c0 = genConstantOffset(loc, rewriter, 0); + ConstOp cDimsIdx = genConstantOffset(loc, rewriter, kDimsPosInBox); + ConstOp dimValue = genConstantIndex(loc, idxTy, rewriter, dim); + + return loadFromOffset(loc, box, c0, cDimsIdx, dimValue, kStridePosInDim, + idxTy, rewriter); + } + fir::LLVMTypeConverter &lowerTy() const { return *static_cast(this->getTypeConverter()); } @@ -2157,6 +2181,172 @@ } }; +/// 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 { + mlir::ValueRange 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 rewriter.notifyMatchFailure( + coor, "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 rewriter.notifyMatchFailure( + coor, "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 { + assert(val && val.dyn_cast() && "must not be null value"); + mlir::Operation *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"); + } + +private: + void doRewriteBox(fir::CoordinateOp coor, mlir::Type ty, + mlir::ValueRange operands, mlir::Location loc, + mlir::ConversionPatternRewriter &rewriter) const { + mlir::Type boxObjTy = coor.getBaseType(); + assert(boxObjTy.dyn_cast() && "This is not a `fir.box`"); + + mlir::Value boxBaseAddr = operands[0]; + + // 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) { + mlir::Operation *coordinateDef = (*coor.coor().begin()).getDefiningOp(); + if (isa_and_nonnull(coordinateDef)) { + TODO(loc, + "fir.coordinate_of - fir.len_param_index is not supported yet"); + } + } + + // 2. GENERAL CASE: + // 2.1. (`fir.array`) + // %box = ... : !fix.box> + // %idx = ... : index + // %resultAddr = coordinate_of %box, %idx : !fir.ref + // 2.2 (`fir.derived`) + // %box = ... : !fix.box> + // %idx = ... : i32 + // %resultAddr = coordinate_of %box, %idx : !fir.ref + // 2.3 (`fir.derived` inside `fir.array`) + // %box = ... : !fir.box>> + // %idx1 = ... : index + // %idx2 = ... : i32 + // %resultAddr = coordinate_of %box, %idx1, %idx2 : !fir.ref + // 2.4. TODO: Either document or disable any other case that the following + // implementation might convert. + mlir::LLVM::ConstantOp c0 = + genConstantIndex(loc, lowerTy().indexType(), rewriter, 0); + mlir::Value resultAddr = + loadBaseAddrFromBox(loc, getBaseAddrTypeFromBox(boxBaseAddr.getType()), + boxBaseAddr, rewriter); + auto currentObjTy = fir::dyn_cast_ptrOrBoxEleTy(boxObjTy); + mlir::Type voidPtrTy = ::getVoidPtrType(coor.getContext()); + + for (unsigned i = 1, last = operands.size(); i < last; ++i) { + if (auto arrTy = currentObjTy.dyn_cast()) { + if (i != 1) + TODO(loc, "fir.array nested inside other array and/or derived type"); + // 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. + auto idxTy = lowerTy().indexType(); + mlir::Value off = genConstantIndex(loc, idxTy, rewriter, 0); + for (unsigned index = i, lastIndex = i + arrTy.getDimension(); + index < lastIndex; ++index) { + mlir::Value stride = + loadStrideFromBox(loc, operands[0], index - i, rewriter); + auto sc = rewriter.create(loc, idxTy, + operands[index], stride); + off = rewriter.create(loc, idxTy, sc, off); + } + auto voidPtrBase = + rewriter.create(loc, voidPtrTy, resultAddr); + SmallVector args{voidPtrBase, off}; + resultAddr = rewriter.create(loc, voidPtrTy, args); + i += arrTy.getDimension() - 1; + currentObjTy = arrTy.getEleTy(); + } else if (auto seqTy = currentObjTy.dyn_cast()) { + auto seqRefTy = + mlir::LLVM::LLVMPointerType::get(lowerTy().convertType(seqTy)); + mlir::Value nxtOpnd = operands[i]; + auto memObj = + rewriter.create(loc, seqRefTy, resultAddr); + llvm::SmallVector args = {memObj, c0, nxtOpnd}; + currentObjTy = seqTy.getType(getFieldNumber(seqTy, nxtOpnd)); + auto llvmCurrentObjTy = lowerTy().convertType(currentObjTy); + auto gep = rewriter.create( + loc, mlir::LLVM::LLVMPointerType::get(llvmCurrentObjTy), args); + resultAddr = + rewriter.create(loc, voidPtrTy, gep); + } else { + fir::emitFatalError(loc, "unexpected type in coordinate_of"); + } + } + + rewriter.replaceOpWithNewOp(coor, ty, resultAddr); + return; + } + + void doRewriteSequence(mlir::Location loc) const { + TODO(loc, "fir.coordinate_of codegen for sequence types"); + } +}; + } // namespace namespace { @@ -2186,20 +2376,20 @@ BoxIsArrayOpConversion, BoxIsPtrOpConversion, BoxProcHostOpConversion, BoxRankOpConversion, BoxTypeDescOpConversion, CallOpConversion, CmpcOpConversion, ConstcOpConversion, ConvertOpConversion, - DispatchOpConversion, DispatchTableOpConversion, DTEntryOpConversion, - DivcOpConversion, EmboxOpConversion, EmboxCharOpConversion, - EmboxProcOpConversion, ExtractValueOpConversion, FieldIndexOpConversion, - FirEndOpConversion, HasValueOpConversion, GenTypeDescOpConversion, - GlobalLenOpConversion, GlobalOpConversion, InsertOnRangeOpConversion, - InsertValueOpConversion, IsPresentOpConversion, - LenParamIndexOpConversion, LoadOpConversion, NegcOpConversion, - NoReassocOpConversion, MulcOpConversion, SelectCaseOpConversion, - SelectOpConversion, SelectRankOpConversion, SelectTypeOpConversion, - ShapeOpConversion, ShapeShiftOpConversion, ShiftOpConversion, - SliceOpConversion, StoreOpConversion, StringLitOpConversion, - SubcOpConversion, UnboxCharOpConversion, UnboxProcOpConversion, - UndefOpConversion, UnreachableOpConversion, ZeroOpConversion>( - typeConverter); + CoordinateOpConversion, DispatchOpConversion, DispatchTableOpConversion, + DTEntryOpConversion, DivcOpConversion, EmboxOpConversion, + EmboxCharOpConversion, EmboxProcOpConversion, ExtractValueOpConversion, + FieldIndexOpConversion, FirEndOpConversion, HasValueOpConversion, + GenTypeDescOpConversion, GlobalLenOpConversion, GlobalOpConversion, + InsertOnRangeOpConversion, InsertValueOpConversion, + IsPresentOpConversion, LenParamIndexOpConversion, LoadOpConversion, + NegcOpConversion, NoReassocOpConversion, MulcOpConversion, + SelectCaseOpConversion, SelectOpConversion, SelectRankOpConversion, + SelectTypeOpConversion, ShapeOpConversion, ShapeShiftOpConversion, + ShiftOpConversion, SliceOpConversion, StoreOpConversion, + StringLitOpConversion, SubcOpConversion, UnboxCharOpConversion, + UnboxProcOpConversion, UndefOpConversion, UnreachableOpConversion, + ZeroOpConversion>(typeConverter); mlir::populateStdToLLVMConversionPatterns(typeConverter, pattern); mlir::arith::populateArithmeticToLLVMConversionPatterns(typeConverter, pattern); diff --git a/flang/lib/Optimizer/CodeGen/TypeConverter.h b/flang/lib/Optimizer/CodeGen/TypeConverter.h --- a/flang/lib/Optimizer/CodeGen/TypeConverter.h +++ b/flang/lib/Optimizer/CodeGen/TypeConverter.h @@ -31,6 +31,7 @@ static constexpr unsigned kAttributePosInBox = 5; static constexpr unsigned kF18AddendumPosInBox = 6; static constexpr unsigned kDimsPosInBox = 7; +static constexpr unsigned kStridePosInDim = 2; static constexpr unsigned kOptTypePtrPosInBox = 8; static constexpr unsigned kOptRowTypePosInBox = 9; diff --git a/flang/test/Fir/Todo/cordinate_of_1.fir b/flang/test/Fir/Todo/cordinate_of_1.fir new file mode 100644 --- /dev/null +++ b/flang/test/Fir/Todo/cordinate_of_1.fir @@ -0,0 +1,12 @@ +// RUN: %not_todo_cmd fir-opt --fir-to-llvm-ir="target=x86_64-unknown-linux-gnu" %s 2>&1 | FileCheck %s + +// `fir.coordinate_of` - derived type with `fir.len_param_index`. As +// `fir.len_param_index` is not implemented yet, that's the error that's +// currently being generated (this error is generated before trying to convert +// `fir.coordinate_of`) +func @coordinate_box_derived_with_fir_len(%arg0: !fir.box>) { +// CHECK: not yet implemented fir.len_param_index codegen + %e = fir.len_param_index len1, !fir.type + %q = fir.coordinate_of %arg0, %e : (!fir.box>, !fir.len) -> !fir.ref + return +} diff --git a/flang/test/Fir/Todo/cordinate_of_2.fir b/flang/test/Fir/Todo/cordinate_of_2.fir new file mode 100644 --- /dev/null +++ b/flang/test/Fir/Todo/cordinate_of_2.fir @@ -0,0 +1,10 @@ +// RUN: %not_todo_cmd fir-opt --fir-to-llvm-ir="target=x86_64-unknown-linux-gnu" %s 2>&1 | FileCheck %s + +// CHECK: not yet implemented fir.array nested inside other array and/or derived type + +// `fir.coordinate_of` - `fir.array` inside "boxed" `fir.derived` +func @coordinate_box_array_inside_derived(%arg0: !fir.box, field_2:i32}>>, %arg1 : index) { + %idx0 = arith.constant 0 : i32 + %q = fir.coordinate_of %arg0, %idx0, %arg1 : (!fir.box, field_2:i32}>>, i32, index) -> !fir.ref + return +} 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 @@ -88,3 +88,12 @@ %zero = arith.constant 0 : i32 return %zero : i32 } + +// ----- + +// `fir.coordinate_of` - dynamically sized arrays are not supported +func @coordinate_of_dynamic_array(%arg0: !fir.box>>, %arg1: index) { +// 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 @@ -1589,3 +1589,202 @@ // CHECK: %[[LOAD:.*]] = llvm.load %[[ARG0]] : !llvm.ptr // CHECK: llvm.store %[[LOAD]], %[[ALLOC]] : !llvm.ptr // CHECK: llvm.return + +// ----- + +// 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 + +// ----- + +// Test `fir.coordinate_of` conversion + +// 2. BOX TYPE (objects wrapped in `fir.box`) +// Derived type - basic case (1 index) +func @coordinate_box_derived_1(%arg0: !fir.box>) { + %idx = fir.field_index field_2, !fir.type + %q = fir.coordinate_of %arg0, %idx : (!fir.box>, !fir.field) -> !fir.ref + return +} +// CHECK-LABEL: llvm.func @coordinate_box_derived_1 +// 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_3:.*]] = llvm.mlir.constant(0 : i64) : i64 +// CHECK: %[[C0_1:.*]] = llvm.mlir.constant(0 : i32) : i32 +// CHECK: %[[C0_2:.*]] = llvm.mlir.constant(0 : i32) : i32 +// CHECK: %[[DERIVED_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: %[[DERIVED_VAL:.*]] = llvm.load %[[DERIVED_ADDR]] : !llvm.ptr>> +// CHECK: %[[DERIVED_CAST:.*]] = llvm.bitcast %[[DERIVED_VAL]] : !llvm.ptr> to !llvm.ptr> +// CHECK: %[[SUBOBJECT_ADDR:.*]] = llvm.getelementptr %[[DERIVED_CAST]][%[[C0_3]], %[[COORDINATE]]] : (!llvm.ptr>, i64, i32) -> !llvm.ptr +// CHECK: %[[CAST_TO_I8_PTR:.*]] = llvm.bitcast %7 : !llvm.ptr to !llvm.ptr +// CHECK: %{{.*}} = llvm.bitcast %[[CAST_TO_I8_PTR]] : !llvm.ptr to !llvm.ptr +// CHECK-NEXT: llvm.return + +// Derived type - basic case (2 indices) +func @coordinate_box_derived_2(%arg0: !fir.box, field_2:i32}>>) { + %idx0 = fir.field_index field_1, !fir.type, field_2:i32}> + %idx1 = fir.field_index inner2, !fir.type + %q = fir.coordinate_of %arg0, %idx0, %idx1 : (!fir.box, field_2:i32}>>, !fir.field, !fir.field) -> !fir.ref + return +} + +// CHECK-LABEL: llvm.func @coordinate_box_derived_2 +// CHECK-SAME: (%[[BOX:.*]]: !llvm.ptr, i32)>>, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, ptr, array<1 x i64>)>>) +// CHECK-NEXT: %[[C0_0:.*]] = llvm.mlir.constant(0 : i32) : i32 +// CHECK-NEXT: %[[C1:.*]] = llvm.mlir.constant(1 : i32) : i32 +// CHECK-NEXT: %[[C0_3:.*]] = llvm.mlir.constant(0 : i64) : i64 +// CHECK-NEXT: %[[C0_1:.*]] = llvm.mlir.constant(0 : i32) : i32 +// CHECK-NEXT: %[[C0_2:.*]] = llvm.mlir.constant(0 : i32) : i32 +// CHECK-NEXT: %[[DERIVED_ADDR:.*]] = llvm.getelementptr %[[BOX]][%[[C0_1]], %[[C0_2]]] : (!llvm.ptr, i32)>>, i{{.*}}, i{{.*}}32, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, ptr, array<1 x i64>)>>, i32, i32) -> !llvm.ptr, i32)>>> +// CHECK-NEXT: %[[DERIVED_VAL:.*]] = llvm.load %[[DERIVED_ADDR]] : !llvm.ptr, i32)>>> +// CHECK-NEXT: %[[DERIVED_CAST_I8_PTR:.*]] = llvm.bitcast %[[DERIVED_VAL]] : !llvm.ptr, i32)>> to !llvm.ptr, i32)>> +// CHECK-NEXT: %[[ANNOTHER_DERIVED_ADDR:.*]] = llvm.getelementptr %[[DERIVED_CAST_I8_PTR]][%[[C0_3]], %[[C0_0]]] : (!llvm.ptr, i32)>>, i64, i32) -> !llvm.ptr> +// CHECK-NEXT: %[[ANOTHER_DERIVED_ADDR_AS_VOID_PTR:.*]] = llvm.bitcast %[[ANNOTHER_DERIVED_ADDR]] : !llvm.ptr> to !llvm.ptr +// CHECK-NEXT: %[[ANOTHER_DERIVED_RECAST:.*]] = llvm.bitcast %[[ANOTHER_DERIVED_ADDR_AS_VOID_PTR]] : !llvm.ptr to !llvm.ptr> +// CHECK-NEXT: %[[SUBOBJECT_ADDR:.*]] = llvm.getelementptr %[[ANOTHER_DERIVED_RECAST]][%[[C0_3]], %[[C1]]] : (!llvm.ptr>, i64, i32) -> !llvm.ptr +// CHECK-NEXT: %[[SUBOBJECT_AS_VOID_PTR:.*]] = llvm.bitcast %[[SUBOBJECT_ADDR]] : !llvm.ptr to !llvm.ptr +// CHECK-NEXT: %{{.*}} = llvm.bitcast %[[SUBOBJECT_AS_VOID_PTR]] : !llvm.ptr to !llvm.ptr +// CHECK-NEXT: llvm.return + +// TODO: Derived type - special case with `fir.len_param_index` + +// ----- + +// Test `fir.coordinate_of` conversion + +// 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 +// CHECK-NEXT: %{{.*}} = llvm.mlir.constant(0 : i64) : i64 +// There's only one box here. Its 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_OBJECT_AS_VOID_PTR:.*]] = llvm.bitcast %[[ARRAY_OBJECT]] : !llvm.ptr> to !llvm.ptr +// CHECK-NEXT: %[[SUBOBJECT_ADDR:.*]] = llvm.getelementptr %[[ARRAY_OBJECT_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) +// CHECK-NEXT: %{{.*}} = llvm.mlir.constant(0 : i64) : i64 +// There's only one box here. Its 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_OBJECT_AS_VOID_PTR:.*]] = llvm.bitcast %[[ARRAY_OBJECT]] : !llvm.ptr>> to !llvm.ptr +// CHECK-NEXT: %[[SUBOBJECT_ADDR:.*]] = llvm.getelementptr %[[ARRAY_OBJECT_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 + +// ----- + +// Test `fir.coordinate_of` conversion + +// 4. BOX TYPE - `fir.derived` inside `fir.array` +func @coordinate_box_derived_inside_array(%arg0: !fir.box>>, %arg1 : index) { + %idx0 = fir.field_index field_2, !fir.type + %q = fir.coordinate_of %arg0, %arg1, %idx0 : (!fir.box>>, index, !fir.field) -> !fir.ref + return +} +// CHECK-LABEL: llvm.func @coordinate_box_derived_inside_array( +// CHECK-SAME: %[[BOX:.*]]: !llvm.ptr>>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr, array<1 x i64>)>>, +// CHECK-SAME: %[[COORDINATE_1:.*]]: i64) { +// CHECK: %[[COORDINATE_2:.*]] = llvm.mlir.constant(1 : i32) : i32 +// CHECK: %[[VAL_3:.*]] = llvm.mlir.constant(0 : i64) : i64 +// CHECK: %[[VAL_4:.*]] = llvm.mlir.constant(0 : i32) : i32 +// CHECK: %[[VAL_5:.*]] = llvm.mlir.constant(0 : i32) : i32 +// CHECK: %[[VAL_6:.*]] = llvm.getelementptr %[[BOX]]{{\[}}%[[VAL_4]], %[[VAL_5]]] : (!llvm.ptr>>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr, array<1 x i64>)>>, i32, i32) -> !llvm.ptr>>> +// CHECK: %[[ARRAY:.*]] = llvm.load %[[VAL_6]] : !llvm.ptr>>> +// CHECK: %[[VAL_8:.*]] = llvm.mlir.constant(0 : i64) : i64 +// CHECK: %[[VAL_9:.*]] = llvm.mlir.constant(0 : i32) : i32 +// CHECK: %[[CFI_DIM_IDX:.*]] = llvm.mlir.constant(7 : i32) : i32 +// CHECK: %[[DIM_IDX:.*]] = llvm.mlir.constant(0 : i64) : i64 +// CHECK: %[[DIM_MEM_STRIDE:.*]] = llvm.mlir.constant(2 : i32) : i32 +// CHECK: %[[VAL_13:.*]] = llvm.getelementptr %[[BOX]][%[[VAL_9]], %[[CFI_DIM_IDX]], %[[DIM_IDX]], %[[DIM_MEM_STRIDE]]] : (!llvm.ptr>>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr, array<1 x i64>)>>, i32, i32, i64, i32) -> !llvm.ptr +// CHECK: %[[VAL_14:.*]] = llvm.load %[[VAL_13]] : !llvm.ptr +// CHECK: %[[VAL_15:.*]] = llvm.mul %[[COORDINATE_1]], %[[VAL_14]] : i64 +// CHECK: %[[OFFSET:.*]] = llvm.add %[[VAL_15]], %[[VAL_8]] : i64 +// CHECK: %[[VAL_17:.*]] = llvm.bitcast %[[ARRAY]] : !llvm.ptr>> to !llvm.ptr +// CHECK: %[[VAL_18:.*]] = llvm.getelementptr %[[VAL_17]][%[[OFFSET]]] : (!llvm.ptr, i64) -> !llvm.ptr +// CHECK: %[[DERIVED:.*]] = llvm.bitcast %[[VAL_18]] : !llvm.ptr to !llvm.ptr> +// CHECK: %[[VAL_20:.*]] = llvm.getelementptr %[[DERIVED]][%[[VAL_3]], %[[COORDINATE_2]]] : (!llvm.ptr>, i64, i32) -> !llvm.ptr +// CHECK: %[[VAL_21:.*]] = llvm.bitcast %[[VAL_20]] : !llvm.ptr to !llvm.ptr +// CHECK: %[[VAL_22:.*]] = llvm.bitcast %[[VAL_21]] : !llvm.ptr to !llvm.ptr +// CHECK: llvm.return