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 @@ -2089,6 +2089,48 @@ } }; +/// Convert `fir.field_index`. The conversion depends on whether the size of +/// the record is static or dynamic. +struct FieldIndexOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + // NB: most field references should be resolved by this point + mlir::LogicalResult + matchAndRewrite(fir::FieldIndexOp field, OpAdaptor adaptor, + mlir::ConversionPatternRewriter &rewriter) const override { + auto recTy = field.on_type().cast(); + unsigned index = recTy.getFieldIndex(field.field_id()); + + if (!fir::hasDynamicSize(recTy)) { + // Derived type has compile-time constant layout. Return index of the + // component type in the parent type (to be used in GEP). + rewriter.replaceOp(field, mlir::ValueRange{genConstantOffset( + field.getLoc(), rewriter, index)}); + return success(); + } + + // Derived type has compile-time constant layout. Call the compiler + // generated function to determine the byte offset of the field at runtime. + // This returns a non-constant. + FlatSymbolRefAttr symAttr = mlir::SymbolRefAttr::get( + field.getContext(), getOffsetMethodName(recTy, field.field_id())); + NamedAttribute callAttr = rewriter.getNamedAttr("callee", symAttr); + NamedAttribute fieldAttr = rewriter.getNamedAttr( + "field", mlir::IntegerAttr::get(lowerTy().indexType(), index)); + rewriter.replaceOpWithNewOp( + field, lowerTy().offsetType(), adaptor.getOperands(), + llvm::ArrayRef{callAttr, fieldAttr}); + return success(); + } + + // Re-Construct the name of the compiler generated method that calculates the + // offset + inline static std::string getOffsetMethodName(fir::RecordType recTy, + llvm::StringRef field) { + return recTy.getName().str() + "P." + field.str() + ".offset"; + } +}; + } // namespace namespace { @@ -2120,16 +2162,17 @@ CmpcOpConversion, ConstcOpConversion, ConvertOpConversion, DispatchOpConversion, DispatchTableOpConversion, DTEntryOpConversion, DivcOpConversion, EmboxOpConversion, EmboxCharOpConversion, - EmboxProcOpConversion, ExtractValueOpConversion, FirEndOpConversion, - HasValueOpConversion, GenTypeDescOpConversion, GlobalLenOpConversion, - GlobalOpConversion, InsertOnRangeOpConversion, InsertValueOpConversion, - IsPresentOpConversion, LoadOpConversion, NegcOpConversion, - MulcOpConversion, SelectCaseOpConversion, SelectOpConversion, - SelectRankOpConversion, SelectTypeOpConversion, ShapeOpConversion, - ShapeShiftOpConversion, ShiftOpConversion, SliceOpConversion, - StoreOpConversion, StringLitOpConversion, SubcOpConversion, - UnboxCharOpConversion, UnboxProcOpConversion, UndefOpConversion, - UnreachableOpConversion, ZeroOpConversion>(typeConverter); + EmboxProcOpConversion, ExtractValueOpConversion, FieldIndexOpConversion, + FirEndOpConversion, HasValueOpConversion, GenTypeDescOpConversion, + GlobalLenOpConversion, GlobalOpConversion, InsertOnRangeOpConversion, + InsertValueOpConversion, IsPresentOpConversion, LoadOpConversion, + NegcOpConversion, 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/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,42 @@ // 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.field_index` + +func @field_index_static_size_1_elem() -> () { + %1 = fir.field_index i, !fir.type + return +} + +// CHECK-LABEL: @field_index_static_size_1_elem +// CHECK-NEXT: %{{.*}} = llvm.mlir.constant(0 : i32) : i32 +// CHECK-NEXT: llvm.return + +func @field_index_static_size_3_elems() -> () { + %1 = fir.field_index k, !fir.type + return +} + +// CHECK-LABEL: @field_index_static_size_3_elems +// CHECK-NEXT: %{{.*}} = llvm.mlir.constant(2 : i32) : i32 +// CHECK-NEXT: llvm.return + +// When converting `fir.field_index` for a dynamically sized record, the +// offset will be calculated at runtime by calling methods like the ones +// below. Note that these methods would normally be generated by the compiler. +func private @custom_typeP.field_1.offset() -> i32 +func private @custom_typeP.field_2.offset() -> i32 + +func @field_index_dynamic_size() -> () { + %1 = fir.field_index field_1, !fir.type}> + %2 = fir.field_index field_2, !fir.type}> + return +} + +// CHECK-LABEL: @field_index_dynamic_size +// CHECK-NEXT: %{{.*}} = llvm.call @custom_typeP.field_1.offset() {field = 0 : i64} : () -> i32 +// CHECK-NEXT: %{{.*}} = llvm.call @custom_typeP.field_2.offset() {field = 1 : i64} : () -> i32 +// CHECK-NEXT: llvm.return