diff --git a/flang/include/flang/Optimizer/Dialect/FIRAttr.td b/flang/include/flang/Optimizer/Dialect/FIRAttr.td index f153cf15f9af0..114bf7d1df913 100644 --- a/flang/include/flang/Optimizer/Dialect/FIRAttr.td +++ b/flang/include/flang/Optimizer/Dialect/FIRAttr.td @@ -58,6 +58,16 @@ def fir_FortranVariableFlagsAttr : fir_Attr<"FortranVariableFlags"> { "::fir::FortranVariableFlagsAttr::get($_builder.getContext(), $0)"; } +def fir_BoxFieldAttr : I32EnumAttr< + "BoxFieldAttr", "", + [ + I32EnumAttrCase<"base_addr", 0>, + I32EnumAttrCase<"derived_type", 1> + ]> { + let cppNamespace = "fir"; +} + + // mlir::SideEffects::Resource for modelling operations which add debugging information def DebuggingResource : Resource<"::fir::DebuggingResource">; diff --git a/flang/include/flang/Optimizer/Dialect/FIROps.td b/flang/include/flang/Optimizer/Dialect/FIROps.td index 6e8064a63b7ae..14a3879906186 100644 --- a/flang/include/flang/Optimizer/Dialect/FIROps.td +++ b/flang/include/flang/Optimizer/Dialect/FIROps.td @@ -3036,4 +3036,43 @@ def fir_DeclareOp : fir_Op<"declare", [AttrSizedOperandSegments, let hasVerifier = 1; } +def fir_BoxOffsetOp : fir_Op<"box_offset", [NoMemoryEffect]> { + + let summary = "Get the address of a field in a fir.ref"; + + let description = [{ + Given the address of a fir.box, compute the address of a field inside + the fir.box. + This allows keeping the actual runtime descriptor layout abstract in + FIR while providing access to the pointer addresses in the runtime + descriptor for OpenMP/OpenACC target mapping. + + To avoid requiring too much information about the fields that the runtime + descriptor implementation must have, only the base_addr and derived_type + descriptor fields can be addressed. + + ``` + %addr = fir.box_offset %box base_addr : (!fir.ref>>) -> !fir.llvm_ptr>> + %tdesc = fir.box_offset %box derived_type : (!fir.ref>>) -> !fir.llvm_ptr>> + + ``` + }]; + + let arguments = (ins + AnyReferenceLike:$box_ref, + fir_BoxFieldAttr:$field + ); + + let results = (outs RefOrLLVMPtr); + let hasVerifier = 1; + + let assemblyFormat = [{ + $box_ref $field attr-dict `:` functional-type(operands, results) + }]; + + let builders = [ + OpBuilder<(ins "mlir::Value":$boxRef, "fir::BoxFieldAttr":$field)> + ]; +} + #endif diff --git a/flang/lib/Optimizer/CodeGen/CodeGen.cpp b/flang/lib/Optimizer/CodeGen/CodeGen.cpp index 9eabacdc818f6..38227f5c02a7c 100644 --- a/flang/lib/Optimizer/CodeGen/CodeGen.cpp +++ b/flang/lib/Optimizer/CodeGen/CodeGen.cpp @@ -3656,6 +3656,27 @@ struct NegcOpConversion : public FIROpConversion { } }; +struct BoxOffsetOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + mlir::LogicalResult + matchAndRewrite(fir::BoxOffsetOp boxOffset, OpAdaptor adaptor, + mlir::ConversionPatternRewriter &rewriter) const override { + + mlir::Type pty = ::getLlvmPtrType(boxOffset.getContext()); + mlir::Type boxType = fir::unwrapRefType(boxOffset.getBoxRef().getType()); + mlir::Type llvmBoxTy = + lowerTy().convertBoxTypeAsStruct(mlir::cast(boxType)); + unsigned fieldId = boxOffset.getField() == fir::BoxFieldAttr::derived_type + ? getTypeDescFieldId(boxType) + : kAddrPosInBox; + rewriter.replaceOpWithNewOp( + boxOffset, pty, llvmBoxTy, adaptor.getBoxRef(), + llvm::ArrayRef{0, fieldId}); + return mlir::success(); + } +}; + /// Conversion pattern for operation that must be dead. The information in these /// operations is used by other operation. At this point they should not have /// anymore uses. @@ -3807,25 +3828,25 @@ class FIRToLLVMLowering AllocaOpConversion, AllocMemOpConversion, BoxAddrOpConversion, BoxCharLenOpConversion, BoxDimsOpConversion, BoxEleSizeOpConversion, BoxIsAllocOpConversion, BoxIsArrayOpConversion, BoxIsPtrOpConversion, - BoxProcHostOpConversion, BoxRankOpConversion, BoxTypeCodeOpConversion, - BoxTypeDescOpConversion, CallOpConversion, CmpcOpConversion, - ConstcOpConversion, ConvertOpConversion, CoordinateOpConversion, - DTEntryOpConversion, DivcOpConversion, EmboxOpConversion, - EmboxCharOpConversion, EmboxProcOpConversion, ExtractValueOpConversion, - FieldIndexOpConversion, FirEndOpConversion, FreeMemOpConversion, - GlobalLenOpConversion, GlobalOpConversion, HasValueOpConversion, - InsertOnRangeOpConversion, InsertValueOpConversion, - IsPresentOpConversion, LenParamIndexOpConversion, LoadOpConversion, - MulcOpConversion, NegcOpConversion, NoReassocOpConversion, - SelectCaseOpConversion, SelectOpConversion, SelectRankOpConversion, - SelectTypeOpConversion, ShapeOpConversion, ShapeShiftOpConversion, - ShiftOpConversion, SliceOpConversion, StoreOpConversion, - StringLitOpConversion, SubcOpConversion, TypeDescOpConversion, - TypeInfoOpConversion, UnboxCharOpConversion, UnboxProcOpConversion, - UndefOpConversion, UnreachableOpConversion, - UnrealizedConversionCastOpConversion, XArrayCoorOpConversion, - XEmboxOpConversion, XReboxOpConversion, ZeroOpConversion>(typeConverter, - options); + BoxOffsetOpConversion, BoxProcHostOpConversion, BoxRankOpConversion, + BoxTypeCodeOpConversion, BoxTypeDescOpConversion, CallOpConversion, + CmpcOpConversion, ConstcOpConversion, ConvertOpConversion, + CoordinateOpConversion, DTEntryOpConversion, DivcOpConversion, + EmboxOpConversion, EmboxCharOpConversion, EmboxProcOpConversion, + ExtractValueOpConversion, FieldIndexOpConversion, FirEndOpConversion, + FreeMemOpConversion, GlobalLenOpConversion, GlobalOpConversion, + HasValueOpConversion, InsertOnRangeOpConversion, + InsertValueOpConversion, IsPresentOpConversion, + LenParamIndexOpConversion, LoadOpConversion, MulcOpConversion, + NegcOpConversion, NoReassocOpConversion, SelectCaseOpConversion, + SelectOpConversion, SelectRankOpConversion, SelectTypeOpConversion, + ShapeOpConversion, ShapeShiftOpConversion, ShiftOpConversion, + SliceOpConversion, StoreOpConversion, StringLitOpConversion, + SubcOpConversion, TypeDescOpConversion, TypeInfoOpConversion, + UnboxCharOpConversion, UnboxProcOpConversion, UndefOpConversion, + UnreachableOpConversion, UnrealizedConversionCastOpConversion, + XArrayCoorOpConversion, XEmboxOpConversion, XReboxOpConversion, + ZeroOpConversion>(typeConverter, options); mlir::populateFuncToLLVMConversionPatterns(typeConverter, pattern); mlir::populateOpenMPToLLVMConversionPatterns(typeConverter, pattern); mlir::arith::populateArithToLLVMConversionPatterns(typeConverter, pattern); diff --git a/flang/lib/Optimizer/Dialect/FIROps.cpp b/flang/lib/Optimizer/Dialect/FIROps.cpp index 9641b46d4725c..ab1e9c0ec7adc 100644 --- a/flang/lib/Optimizer/Dialect/FIROps.cpp +++ b/flang/lib/Optimizer/Dialect/FIROps.cpp @@ -3584,6 +3584,39 @@ void fir::IfOp::resultToSourceOps(llvm::SmallVectorImpl &results, results.push_back(term->getOperand(resultNum)); } +//===----------------------------------------------------------------------===// +// BoxOffsetOp +//===----------------------------------------------------------------------===// + +mlir::LogicalResult fir::BoxOffsetOp::verify() { + auto boxType = mlir::dyn_cast_or_null( + fir::dyn_cast_ptrEleTy(getBoxRef().getType())); + if (!boxType) + return emitOpError("box_ref operand must have !fir.ref> type"); + if (getField() != fir::BoxFieldAttr::base_addr && + getField() != fir::BoxFieldAttr::derived_type) + return emitOpError("cannot address provided field"); + if (getField() == fir::BoxFieldAttr::derived_type) + if (!fir::boxHasAddendum(boxType)) + return emitOpError("can only address derived_type field of derived type " + "or unlimited polymorphic fir.box"); + return mlir::success(); +} + +void fir::BoxOffsetOp::build(mlir::OpBuilder &builder, + mlir::OperationState &result, mlir::Value boxRef, + fir::BoxFieldAttr field) { + mlir::Type valueType = + fir::unwrapPassByRefType(fir::unwrapRefType(boxRef.getType())); + mlir::Type resultType = valueType; + if (field == fir::BoxFieldAttr::base_addr) + resultType = fir::LLVMPointerType::get(fir::ReferenceType::get(valueType)); + else if (field == fir::BoxFieldAttr::derived_type) + resultType = fir::LLVMPointerType::get( + fir::TypeDescType::get(fir::unwrapSequenceType(valueType))); + build(builder, result, {resultType}, boxRef, field); +} + //===----------------------------------------------------------------------===// mlir::ParseResult fir::isValidCaseAttr(mlir::Attribute attr) { diff --git a/flang/test/Fir/box-offset-codegen.fir b/flang/test/Fir/box-offset-codegen.fir new file mode 100644 index 0000000000000..600555cd94cea --- /dev/null +++ b/flang/test/Fir/box-offset-codegen.fir @@ -0,0 +1,39 @@ +// Test fir.box_offset code generation. +// RUN: tco %s | FileCheck %s +// RUN: %flang_fc1 -emit-llvm %s -o - | FileCheck %s + +func.func @scalar_addr(%scalar : !fir.ref>>) -> !fir.llvm_ptr>> { + %addr = fir.box_offset %scalar base_addr : (!fir.ref>>) -> !fir.llvm_ptr>> + return %addr : !fir.llvm_ptr>> +} +// CHECK-LABEL: define ptr @scalar_addr( +// CHECK-SAME: ptr %[[BOX:.*]]) { +// CHECK: %[[VAL_0:.*]] = getelementptr { ptr, i64, i32, i8, i8, i8, i8, ptr, [1 x i64] }, ptr %[[BOX]], i32 0, i32 0 +// CHECK: ret ptr %[[VAL_0]] + +func.func @scalar_tdesc(%scalar : !fir.ref>>) -> !fir.llvm_ptr>> { + %tdesc = fir.box_offset %scalar derived_type : (!fir.ref>>) -> !fir.llvm_ptr>> + return %tdesc : !fir.llvm_ptr>> +} +// CHECK-LABEL: define ptr @scalar_tdesc( +// CHECK-SAME: ptr %[[BOX:.*]]) { +// CHECK: %[[VAL_0:.*]] = getelementptr { ptr, i64, i32, i8, i8, i8, i8, ptr, [1 x i64] }, ptr %[[BOX]], i32 0, i32 7 +// CHECK: ret ptr %[[VAL_0]] + +func.func @array_addr(%array : !fir.ref>>>>) -> !fir.llvm_ptr>>> { + %addr = fir.box_offset %array base_addr : (!fir.ref>>>>) -> !fir.llvm_ptr>>> + return %addr : !fir.llvm_ptr>>> +} +// CHECK-LABEL: define ptr @array_addr( +// CHECK-SAME: ptr %[[BOX:.*]]) { +// CHECK: %[[VAL_0:.*]] = getelementptr { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]], ptr, [1 x i64] }, ptr %[[BOX]], i32 0, i32 0 +// CHECK: ret ptr %[[VAL_0]] + +func.func @array_tdesc(%array : !fir.ref>>>>) -> !fir.llvm_ptr>> { + %tdesc = fir.box_offset %array derived_type : (!fir.ref>>>>) -> !fir.llvm_ptr>> + return %tdesc : !fir.llvm_ptr>> +} +// CHECK-LABEL: define ptr @array_tdesc( +// CHECK-SAME: ptr %[[BOX:.*]]) { +// CHECK: %[[VAL_0:.*]] = getelementptr { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]], ptr, [1 x i64] }, ptr %[[BOX]], i32 0, i32 8 +// CHECK: ret ptr %[[VAL_0]] diff --git a/flang/test/Fir/box-offset.fir b/flang/test/Fir/box-offset.fir new file mode 100644 index 0000000000000..98c2eaefb8d6b --- /dev/null +++ b/flang/test/Fir/box-offset.fir @@ -0,0 +1,42 @@ +// Test fir.box_offset parse/print/parse/print identity. +// RUN: fir-opt %s | fir-opt | FileCheck %s + +func.func @test_box_offset(%unlimited : !fir.ref>, %type_star : !fir.ref>>) { + %box1 = fir.alloca !fir.box + %addr1 = fir.box_offset %box1 base_addr : (!fir.ref>) -> !fir.llvm_ptr> + + %box2 = fir.alloca !fir.box> + %addr2 = fir.box_offset %box2 base_addr : (!fir.ref>>) -> !fir.llvm_ptr>> + %tdesc2 = fir.box_offset %box2 derived_type : (!fir.ref>>) -> !fir.llvm_ptr>> + + %box3 = fir.alloca !fir.box> + %addr3 = fir.box_offset %box3 base_addr : (!fir.ref>>) -> !fir.llvm_ptr>> + + %box4 = fir.alloca !fir.box>>> + %addr4 = fir.box_offset %box4 base_addr : (!fir.ref>>>>) -> !fir.llvm_ptr>>> + %tdesc4 = fir.box_offset %box4 derived_type : (!fir.ref>>>>) -> !fir.llvm_ptr>> + + %addr5 = fir.box_offset %unlimited base_addr : (!fir.ref>) -> !fir.llvm_ptr> + %tdesc5 = fir.box_offset %unlimited derived_type : (!fir.ref>) -> !fir.llvm_ptr> + + %addr6 = fir.box_offset %type_star base_addr : (!fir.ref>>) -> !fir.llvm_ptr>> + %tdesc6 = fir.box_offset %type_star derived_type : (!fir.ref>>) -> !fir.llvm_ptr> + return +} +// CHECK-LABEL: func.func @test_box_offset( +// CHECK-SAME: %[[VAL_0:.*]]: !fir.ref>, +// CHECK-SAME: %[[VAL_1:.*]]: !fir.ref>>) { +// CHECK: %[[VAL_2:.*]] = fir.alloca !fir.box +// CHECK: %[[VAL_3:.*]] = fir.box_offset %[[VAL_2]] base_addr : (!fir.ref>) -> !fir.llvm_ptr> +// CHECK: %[[VAL_4:.*]] = fir.alloca !fir.box> +// CHECK: %[[VAL_5:.*]] = fir.box_offset %[[VAL_4]] base_addr : (!fir.ref>>) -> !fir.llvm_ptr>> +// CHECK: %[[VAL_6:.*]] = fir.box_offset %[[VAL_4]] derived_type : (!fir.ref>>) -> !fir.llvm_ptr>> +// CHECK: %[[VAL_7:.*]] = fir.alloca !fir.box> +// CHECK: %[[VAL_8:.*]] = fir.box_offset %[[VAL_7]] base_addr : (!fir.ref>>) -> !fir.llvm_ptr>> +// CHECK: %[[VAL_9:.*]] = fir.alloca !fir.box>>> +// CHECK: %[[VAL_10:.*]] = fir.box_offset %[[VAL_9]] base_addr : (!fir.ref>>>>) -> !fir.llvm_ptr>>> +// CHECK: %[[VAL_11:.*]] = fir.box_offset %[[VAL_9]] derived_type : (!fir.ref>>>>) -> !fir.llvm_ptr>> +// CHECK: %[[VAL_12:.*]] = fir.box_offset %[[VAL_0]] base_addr : (!fir.ref>) -> !fir.llvm_ptr> +// CHECK: %[[VAL_13:.*]] = fir.box_offset %[[VAL_0]] derived_type : (!fir.ref>) -> !fir.llvm_ptr> +// CHECK: %[[VAL_14:.*]] = fir.box_offset %[[VAL_1]] base_addr : (!fir.ref>>) -> !fir.llvm_ptr>> +// CHECK: %[[VAL_15:.*]] = fir.box_offset %[[VAL_1]] derived_type : (!fir.ref>>) -> !fir.llvm_ptr> diff --git a/flang/test/Fir/invalid.fir b/flang/test/Fir/invalid.fir index 824aeec28b417..049e108ba992d 100644 --- a/flang/test/Fir/invalid.fir +++ b/flang/test/Fir/invalid.fir @@ -962,3 +962,19 @@ func.func @fp_to_logical(%arg0: f32) -> !fir.logical<4> { %0 = fir.convert %arg0 : (f32) -> !fir.logical<4> return %0 : !fir.logical<4> } + +// ----- + +func.func @bad_box_offset(%not_a_box : !fir.ref) { + // expected-error@+1{{'fir.box_offset' op box_ref operand must have !fir.ref> type}} + %addr1 = fir.box_offset %not_a_box base_addr : (!fir.ref) -> !fir.llvm_ptr> + return +} + +// ----- + +func.func @bad_box_offset(%no_addendum : !fir.ref>) { + // expected-error@+1{{'fir.box_offset' op can only address derived_type field of derived type or unlimited polymorphic fir.box}} + %addr1 = fir.box_offset %no_addendum derived_type : (!fir.ref>) -> !fir.llvm_ptr>> + return +}