Skip to content

Commit 91e1b4a

Browse files
authored
[flang] add fir.box_offset operation (#73641)
This operation allows computing the address of descriptor fields. It is needed to help attaching descriptors in OpenMP/OpenACC target region. The pointers inside the descriptor structure must be mapped too, but the fir.box is abstract, so these fields cannot be computed with fir.coordinate_of. To preserve the abstraction of the descriptor layout in FIR, introduce an operation specifically to !fir.ref<fir.box<>> address fields based on field names (base_addr or derived_type).
1 parent c145e4c commit 91e1b4a

File tree

7 files changed

+219
-19
lines changed

7 files changed

+219
-19
lines changed

flang/include/flang/Optimizer/Dialect/FIRAttr.td

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,16 @@ def fir_FortranVariableFlagsAttr : fir_Attr<"FortranVariableFlags"> {
5858
"::fir::FortranVariableFlagsAttr::get($_builder.getContext(), $0)";
5959
}
6060

61+
def fir_BoxFieldAttr : I32EnumAttr<
62+
"BoxFieldAttr", "",
63+
[
64+
I32EnumAttrCase<"base_addr", 0>,
65+
I32EnumAttrCase<"derived_type", 1>
66+
]> {
67+
let cppNamespace = "fir";
68+
}
69+
70+
6171
// mlir::SideEffects::Resource for modelling operations which add debugging information
6272
def DebuggingResource : Resource<"::fir::DebuggingResource">;
6373

flang/include/flang/Optimizer/Dialect/FIROps.td

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3036,4 +3036,43 @@ def fir_DeclareOp : fir_Op<"declare", [AttrSizedOperandSegments,
30363036
let hasVerifier = 1;
30373037
}
30383038

3039+
def fir_BoxOffsetOp : fir_Op<"box_offset", [NoMemoryEffect]> {
3040+
3041+
let summary = "Get the address of a field in a fir.ref<fir.box>";
3042+
3043+
let description = [{
3044+
Given the address of a fir.box, compute the address of a field inside
3045+
the fir.box.
3046+
This allows keeping the actual runtime descriptor layout abstract in
3047+
FIR while providing access to the pointer addresses in the runtime
3048+
descriptor for OpenMP/OpenACC target mapping.
3049+
3050+
To avoid requiring too much information about the fields that the runtime
3051+
descriptor implementation must have, only the base_addr and derived_type
3052+
descriptor fields can be addressed.
3053+
3054+
```
3055+
%addr = fir.box_offset %box base_addr : (!fir.ref<!fir.box<!fir.array<?xi32>>>) -> !fir.llvm_ptr<!fir.ref<!fir.array<?xi32>>>
3056+
%tdesc = fir.box_offset %box derived_type : (!fir.ref<!fir.box<!fir.type<t>>>) -> !fir.llvm_ptr<!fir.tdesc<!fir.type<t>>>
3057+
3058+
```
3059+
}];
3060+
3061+
let arguments = (ins
3062+
AnyReferenceLike:$box_ref,
3063+
fir_BoxFieldAttr:$field
3064+
);
3065+
3066+
let results = (outs RefOrLLVMPtr);
3067+
let hasVerifier = 1;
3068+
3069+
let assemblyFormat = [{
3070+
$box_ref $field attr-dict `:` functional-type(operands, results)
3071+
}];
3072+
3073+
let builders = [
3074+
OpBuilder<(ins "mlir::Value":$boxRef, "fir::BoxFieldAttr":$field)>
3075+
];
3076+
}
3077+
30393078
#endif

flang/lib/Optimizer/CodeGen/CodeGen.cpp

Lines changed: 40 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3656,6 +3656,27 @@ struct NegcOpConversion : public FIROpConversion<fir::NegcOp> {
36563656
}
36573657
};
36583658

3659+
struct BoxOffsetOpConversion : public FIROpConversion<fir::BoxOffsetOp> {
3660+
using FIROpConversion::FIROpConversion;
3661+
3662+
mlir::LogicalResult
3663+
matchAndRewrite(fir::BoxOffsetOp boxOffset, OpAdaptor adaptor,
3664+
mlir::ConversionPatternRewriter &rewriter) const override {
3665+
3666+
mlir::Type pty = ::getLlvmPtrType(boxOffset.getContext());
3667+
mlir::Type boxType = fir::unwrapRefType(boxOffset.getBoxRef().getType());
3668+
mlir::Type llvmBoxTy =
3669+
lowerTy().convertBoxTypeAsStruct(mlir::cast<fir::BaseBoxType>(boxType));
3670+
unsigned fieldId = boxOffset.getField() == fir::BoxFieldAttr::derived_type
3671+
? getTypeDescFieldId(boxType)
3672+
: kAddrPosInBox;
3673+
rewriter.replaceOpWithNewOp<mlir::LLVM::GEPOp>(
3674+
boxOffset, pty, llvmBoxTy, adaptor.getBoxRef(),
3675+
llvm::ArrayRef<mlir::LLVM::GEPArg>{0, fieldId});
3676+
return mlir::success();
3677+
}
3678+
};
3679+
36593680
/// Conversion pattern for operation that must be dead. The information in these
36603681
/// operations is used by other operation. At this point they should not have
36613682
/// anymore uses.
@@ -3807,25 +3828,25 @@ class FIRToLLVMLowering
38073828
AllocaOpConversion, AllocMemOpConversion, BoxAddrOpConversion,
38083829
BoxCharLenOpConversion, BoxDimsOpConversion, BoxEleSizeOpConversion,
38093830
BoxIsAllocOpConversion, BoxIsArrayOpConversion, BoxIsPtrOpConversion,
3810-
BoxProcHostOpConversion, BoxRankOpConversion, BoxTypeCodeOpConversion,
3811-
BoxTypeDescOpConversion, CallOpConversion, CmpcOpConversion,
3812-
ConstcOpConversion, ConvertOpConversion, CoordinateOpConversion,
3813-
DTEntryOpConversion, DivcOpConversion, EmboxOpConversion,
3814-
EmboxCharOpConversion, EmboxProcOpConversion, ExtractValueOpConversion,
3815-
FieldIndexOpConversion, FirEndOpConversion, FreeMemOpConversion,
3816-
GlobalLenOpConversion, GlobalOpConversion, HasValueOpConversion,
3817-
InsertOnRangeOpConversion, InsertValueOpConversion,
3818-
IsPresentOpConversion, LenParamIndexOpConversion, LoadOpConversion,
3819-
MulcOpConversion, NegcOpConversion, NoReassocOpConversion,
3820-
SelectCaseOpConversion, SelectOpConversion, SelectRankOpConversion,
3821-
SelectTypeOpConversion, ShapeOpConversion, ShapeShiftOpConversion,
3822-
ShiftOpConversion, SliceOpConversion, StoreOpConversion,
3823-
StringLitOpConversion, SubcOpConversion, TypeDescOpConversion,
3824-
TypeInfoOpConversion, UnboxCharOpConversion, UnboxProcOpConversion,
3825-
UndefOpConversion, UnreachableOpConversion,
3826-
UnrealizedConversionCastOpConversion, XArrayCoorOpConversion,
3827-
XEmboxOpConversion, XReboxOpConversion, ZeroOpConversion>(typeConverter,
3828-
options);
3831+
BoxOffsetOpConversion, BoxProcHostOpConversion, BoxRankOpConversion,
3832+
BoxTypeCodeOpConversion, BoxTypeDescOpConversion, CallOpConversion,
3833+
CmpcOpConversion, ConstcOpConversion, ConvertOpConversion,
3834+
CoordinateOpConversion, DTEntryOpConversion, DivcOpConversion,
3835+
EmboxOpConversion, EmboxCharOpConversion, EmboxProcOpConversion,
3836+
ExtractValueOpConversion, FieldIndexOpConversion, FirEndOpConversion,
3837+
FreeMemOpConversion, GlobalLenOpConversion, GlobalOpConversion,
3838+
HasValueOpConversion, InsertOnRangeOpConversion,
3839+
InsertValueOpConversion, IsPresentOpConversion,
3840+
LenParamIndexOpConversion, LoadOpConversion, MulcOpConversion,
3841+
NegcOpConversion, NoReassocOpConversion, SelectCaseOpConversion,
3842+
SelectOpConversion, SelectRankOpConversion, SelectTypeOpConversion,
3843+
ShapeOpConversion, ShapeShiftOpConversion, ShiftOpConversion,
3844+
SliceOpConversion, StoreOpConversion, StringLitOpConversion,
3845+
SubcOpConversion, TypeDescOpConversion, TypeInfoOpConversion,
3846+
UnboxCharOpConversion, UnboxProcOpConversion, UndefOpConversion,
3847+
UnreachableOpConversion, UnrealizedConversionCastOpConversion,
3848+
XArrayCoorOpConversion, XEmboxOpConversion, XReboxOpConversion,
3849+
ZeroOpConversion>(typeConverter, options);
38293850
mlir::populateFuncToLLVMConversionPatterns(typeConverter, pattern);
38303851
mlir::populateOpenMPToLLVMConversionPatterns(typeConverter, pattern);
38313852
mlir::arith::populateArithToLLVMConversionPatterns(typeConverter, pattern);

flang/lib/Optimizer/Dialect/FIROps.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3584,6 +3584,39 @@ void fir::IfOp::resultToSourceOps(llvm::SmallVectorImpl<mlir::Value> &results,
35843584
results.push_back(term->getOperand(resultNum));
35853585
}
35863586

3587+
//===----------------------------------------------------------------------===//
3588+
// BoxOffsetOp
3589+
//===----------------------------------------------------------------------===//
3590+
3591+
mlir::LogicalResult fir::BoxOffsetOp::verify() {
3592+
auto boxType = mlir::dyn_cast_or_null<fir::BaseBoxType>(
3593+
fir::dyn_cast_ptrEleTy(getBoxRef().getType()));
3594+
if (!boxType)
3595+
return emitOpError("box_ref operand must have !fir.ref<!fir.box<T>> type");
3596+
if (getField() != fir::BoxFieldAttr::base_addr &&
3597+
getField() != fir::BoxFieldAttr::derived_type)
3598+
return emitOpError("cannot address provided field");
3599+
if (getField() == fir::BoxFieldAttr::derived_type)
3600+
if (!fir::boxHasAddendum(boxType))
3601+
return emitOpError("can only address derived_type field of derived type "
3602+
"or unlimited polymorphic fir.box");
3603+
return mlir::success();
3604+
}
3605+
3606+
void fir::BoxOffsetOp::build(mlir::OpBuilder &builder,
3607+
mlir::OperationState &result, mlir::Value boxRef,
3608+
fir::BoxFieldAttr field) {
3609+
mlir::Type valueType =
3610+
fir::unwrapPassByRefType(fir::unwrapRefType(boxRef.getType()));
3611+
mlir::Type resultType = valueType;
3612+
if (field == fir::BoxFieldAttr::base_addr)
3613+
resultType = fir::LLVMPointerType::get(fir::ReferenceType::get(valueType));
3614+
else if (field == fir::BoxFieldAttr::derived_type)
3615+
resultType = fir::LLVMPointerType::get(
3616+
fir::TypeDescType::get(fir::unwrapSequenceType(valueType)));
3617+
build(builder, result, {resultType}, boxRef, field);
3618+
}
3619+
35873620
//===----------------------------------------------------------------------===//
35883621

35893622
mlir::ParseResult fir::isValidCaseAttr(mlir::Attribute attr) {

flang/test/Fir/box-offset-codegen.fir

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Test fir.box_offset code generation.
2+
// RUN: tco %s | FileCheck %s
3+
// RUN: %flang_fc1 -emit-llvm %s -o - | FileCheck %s
4+
5+
func.func @scalar_addr(%scalar : !fir.ref<!fir.box<!fir.type<t>>>) -> !fir.llvm_ptr<!fir.ref<!fir.type<t>>> {
6+
%addr = fir.box_offset %scalar base_addr : (!fir.ref<!fir.box<!fir.type<t>>>) -> !fir.llvm_ptr<!fir.ref<!fir.type<t>>>
7+
return %addr : !fir.llvm_ptr<!fir.ref<!fir.type<t>>>
8+
}
9+
// CHECK-LABEL: define ptr @scalar_addr(
10+
// CHECK-SAME: ptr %[[BOX:.*]]) {
11+
// CHECK: %[[VAL_0:.*]] = getelementptr { ptr, i64, i32, i8, i8, i8, i8, ptr, [1 x i64] }, ptr %[[BOX]], i32 0, i32 0
12+
// CHECK: ret ptr %[[VAL_0]]
13+
14+
func.func @scalar_tdesc(%scalar : !fir.ref<!fir.box<!fir.type<t>>>) -> !fir.llvm_ptr<!fir.tdesc<!fir.type<t>>> {
15+
%tdesc = fir.box_offset %scalar derived_type : (!fir.ref<!fir.box<!fir.type<t>>>) -> !fir.llvm_ptr<!fir.tdesc<!fir.type<t>>>
16+
return %tdesc : !fir.llvm_ptr<!fir.tdesc<!fir.type<t>>>
17+
}
18+
// CHECK-LABEL: define ptr @scalar_tdesc(
19+
// CHECK-SAME: ptr %[[BOX:.*]]) {
20+
// CHECK: %[[VAL_0:.*]] = getelementptr { ptr, i64, i32, i8, i8, i8, i8, ptr, [1 x i64] }, ptr %[[BOX]], i32 0, i32 7
21+
// CHECK: ret ptr %[[VAL_0]]
22+
23+
func.func @array_addr(%array : !fir.ref<!fir.class<!fir.ptr<!fir.array<?x!fir.type<t>>>>>) -> !fir.llvm_ptr<!fir.ptr<!fir.array<?x!fir.type<t>>>> {
24+
%addr = fir.box_offset %array base_addr : (!fir.ref<!fir.class<!fir.ptr<!fir.array<?x!fir.type<t>>>>>) -> !fir.llvm_ptr<!fir.ptr<!fir.array<?x!fir.type<t>>>>
25+
return %addr : !fir.llvm_ptr<!fir.ptr<!fir.array<?x!fir.type<t>>>>
26+
}
27+
// CHECK-LABEL: define ptr @array_addr(
28+
// CHECK-SAME: ptr %[[BOX:.*]]) {
29+
// 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
30+
// CHECK: ret ptr %[[VAL_0]]
31+
32+
func.func @array_tdesc(%array : !fir.ref<!fir.class<!fir.ptr<!fir.array<?x!fir.type<t>>>>>) -> !fir.llvm_ptr<!fir.tdesc<!fir.type<t>>> {
33+
%tdesc = fir.box_offset %array derived_type : (!fir.ref<!fir.class<!fir.ptr<!fir.array<?x!fir.type<t>>>>>) -> !fir.llvm_ptr<!fir.tdesc<!fir.type<t>>>
34+
return %tdesc : !fir.llvm_ptr<!fir.tdesc<!fir.type<t>>>
35+
}
36+
// CHECK-LABEL: define ptr @array_tdesc(
37+
// CHECK-SAME: ptr %[[BOX:.*]]) {
38+
// 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
39+
// CHECK: ret ptr %[[VAL_0]]

flang/test/Fir/box-offset.fir

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Test fir.box_offset parse/print/parse/print identity.
2+
// RUN: fir-opt %s | fir-opt | FileCheck %s
3+
4+
func.func @test_box_offset(%unlimited : !fir.ref<!fir.class<none>>, %type_star : !fir.ref<!fir.box<!fir.array<?xnone>>>) {
5+
%box1 = fir.alloca !fir.box<i32>
6+
%addr1 = fir.box_offset %box1 base_addr : (!fir.ref<!fir.box<i32>>) -> !fir.llvm_ptr<!fir.ref<i32>>
7+
8+
%box2 = fir.alloca !fir.box<!fir.type<t>>
9+
%addr2 = fir.box_offset %box2 base_addr : (!fir.ref<!fir.box<!fir.type<t>>>) -> !fir.llvm_ptr<!fir.ref<!fir.type<t>>>
10+
%tdesc2 = fir.box_offset %box2 derived_type : (!fir.ref<!fir.box<!fir.type<t>>>) -> !fir.llvm_ptr<!fir.tdesc<!fir.type<t>>>
11+
12+
%box3 = fir.alloca !fir.box<!fir.array<?xi32>>
13+
%addr3 = fir.box_offset %box3 base_addr : (!fir.ref<!fir.box<!fir.array<?xi32>>>) -> !fir.llvm_ptr<!fir.ref<!fir.array<?xi32>>>
14+
15+
%box4 = fir.alloca !fir.box<!fir.ptr<!fir.array<?x!fir.type<t>>>>
16+
%addr4 = fir.box_offset %box4 base_addr : (!fir.ref<!fir.box<!fir.ptr<!fir.array<?x!fir.type<t>>>>>) -> !fir.llvm_ptr<!fir.ptr<!fir.array<?x!fir.type<t>>>>
17+
%tdesc4 = fir.box_offset %box4 derived_type : (!fir.ref<!fir.box<!fir.ptr<!fir.array<?x!fir.type<t>>>>>) -> !fir.llvm_ptr<!fir.tdesc<!fir.type<t>>>
18+
19+
%addr5 = fir.box_offset %unlimited base_addr : (!fir.ref<!fir.class<none>>) -> !fir.llvm_ptr<!fir.ref<none>>
20+
%tdesc5 = fir.box_offset %unlimited derived_type : (!fir.ref<!fir.class<none>>) -> !fir.llvm_ptr<!fir.tdesc<none>>
21+
22+
%addr6 = fir.box_offset %type_star base_addr : (!fir.ref<!fir.box<!fir.array<?xnone>>>) -> !fir.llvm_ptr<!fir.ref<!fir.array<?xnone>>>
23+
%tdesc6 = fir.box_offset %type_star derived_type : (!fir.ref<!fir.box<!fir.array<?xnone>>>) -> !fir.llvm_ptr<!fir.tdesc<none>>
24+
return
25+
}
26+
// CHECK-LABEL: func.func @test_box_offset(
27+
// CHECK-SAME: %[[VAL_0:.*]]: !fir.ref<!fir.class<none>>,
28+
// CHECK-SAME: %[[VAL_1:.*]]: !fir.ref<!fir.box<!fir.array<?xnone>>>) {
29+
// CHECK: %[[VAL_2:.*]] = fir.alloca !fir.box<i32>
30+
// CHECK: %[[VAL_3:.*]] = fir.box_offset %[[VAL_2]] base_addr : (!fir.ref<!fir.box<i32>>) -> !fir.llvm_ptr<!fir.ref<i32>>
31+
// CHECK: %[[VAL_4:.*]] = fir.alloca !fir.box<!fir.type<t>>
32+
// CHECK: %[[VAL_5:.*]] = fir.box_offset %[[VAL_4]] base_addr : (!fir.ref<!fir.box<!fir.type<t>>>) -> !fir.llvm_ptr<!fir.ref<!fir.type<t>>>
33+
// CHECK: %[[VAL_6:.*]] = fir.box_offset %[[VAL_4]] derived_type : (!fir.ref<!fir.box<!fir.type<t>>>) -> !fir.llvm_ptr<!fir.tdesc<!fir.type<t>>>
34+
// CHECK: %[[VAL_7:.*]] = fir.alloca !fir.box<!fir.array<?xi32>>
35+
// CHECK: %[[VAL_8:.*]] = fir.box_offset %[[VAL_7]] base_addr : (!fir.ref<!fir.box<!fir.array<?xi32>>>) -> !fir.llvm_ptr<!fir.ref<!fir.array<?xi32>>>
36+
// CHECK: %[[VAL_9:.*]] = fir.alloca !fir.box<!fir.ptr<!fir.array<?x!fir.type<t>>>>
37+
// CHECK: %[[VAL_10:.*]] = fir.box_offset %[[VAL_9]] base_addr : (!fir.ref<!fir.box<!fir.ptr<!fir.array<?x!fir.type<t>>>>>) -> !fir.llvm_ptr<!fir.ptr<!fir.array<?x!fir.type<t>>>>
38+
// CHECK: %[[VAL_11:.*]] = fir.box_offset %[[VAL_9]] derived_type : (!fir.ref<!fir.box<!fir.ptr<!fir.array<?x!fir.type<t>>>>>) -> !fir.llvm_ptr<!fir.tdesc<!fir.type<t>>>
39+
// CHECK: %[[VAL_12:.*]] = fir.box_offset %[[VAL_0]] base_addr : (!fir.ref<!fir.class<none>>) -> !fir.llvm_ptr<!fir.ref<none>>
40+
// CHECK: %[[VAL_13:.*]] = fir.box_offset %[[VAL_0]] derived_type : (!fir.ref<!fir.class<none>>) -> !fir.llvm_ptr<!fir.tdesc<none>>
41+
// CHECK: %[[VAL_14:.*]] = fir.box_offset %[[VAL_1]] base_addr : (!fir.ref<!fir.box<!fir.array<?xnone>>>) -> !fir.llvm_ptr<!fir.ref<!fir.array<?xnone>>>
42+
// CHECK: %[[VAL_15:.*]] = fir.box_offset %[[VAL_1]] derived_type : (!fir.ref<!fir.box<!fir.array<?xnone>>>) -> !fir.llvm_ptr<!fir.tdesc<none>>

flang/test/Fir/invalid.fir

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -962,3 +962,19 @@ func.func @fp_to_logical(%arg0: f32) -> !fir.logical<4> {
962962
%0 = fir.convert %arg0 : (f32) -> !fir.logical<4>
963963
return %0 : !fir.logical<4>
964964
}
965+
966+
// -----
967+
968+
func.func @bad_box_offset(%not_a_box : !fir.ref<i32>) {
969+
// expected-error@+1{{'fir.box_offset' op box_ref operand must have !fir.ref<!fir.box<T>> type}}
970+
%addr1 = fir.box_offset %not_a_box base_addr : (!fir.ref<i32>) -> !fir.llvm_ptr<!fir.ref<i32>>
971+
return
972+
}
973+
974+
// -----
975+
976+
func.func @bad_box_offset(%no_addendum : !fir.ref<!fir.box<i32>>) {
977+
// expected-error@+1{{'fir.box_offset' op can only address derived_type field of derived type or unlimited polymorphic fir.box}}
978+
%addr1 = fir.box_offset %no_addendum derived_type : (!fir.ref<!fir.box<i32>>) -> !fir.llvm_ptr<!fir.tdesc<!fir.type<none>>>
979+
return
980+
}

0 commit comments

Comments
 (0)