Skip to content

Commit 6e2f068

Browse files
committed
[mlir][OpenMP] - MLIR to LLVMIR translation support for delayed privatization of allocatables in omp.target ops
This PR adds support to translate the `private` clause from MLIR to LLVMIR when used on allocatables in the context of an `omp.target` op.
1 parent 8d770fd commit 6e2f068

File tree

7 files changed

+369
-35
lines changed

7 files changed

+369
-35
lines changed

flang/lib/Optimizer/OpenMP/MapsForPrivatizedSymbols.cpp

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,6 @@ class MapsForPrivatizedSymbolsPass
4949
: public flangomp::impl::MapsForPrivatizedSymbolsPassBase<
5050
MapsForPrivatizedSymbolsPass> {
5151

52-
bool privatizerNeedsMap(omp::PrivateClauseOp &privatizer) {
53-
Region &allocRegion = privatizer.getAllocRegion();
54-
Value blockArg0 = allocRegion.getArgument(0);
55-
if (blockArg0.use_empty())
56-
return false;
57-
return true;
58-
}
5952
omp::MapInfoOp createMapInfo(Location loc, Value var,
6053
fir::FirOpBuilder &builder) {
6154
uint64_t mapTypeTo = static_cast<
@@ -134,7 +127,7 @@ class MapsForPrivatizedSymbolsPass
134127
omp::PrivateClauseOp privatizer =
135128
SymbolTable::lookupNearestSymbolFrom<omp::PrivateClauseOp>(
136129
targetOp, privatizerName);
137-
if (!privatizerNeedsMap(privatizer)) {
130+
if (!privatizer.needsMap()) {
138131
privVarMapIdx.push_back(-1);
139132
continue;
140133
}

mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,16 @@ def PrivateClauseOp : OpenMP_Op<"private", [IsolatedFromAbove, RecipeInterface]>
135135
auto &region = getDeallocRegion();
136136
return region.empty() ? nullptr : region.getArgument(0);
137137
}
138+
/// privatizerNeedsMap returns true if the value being privatized in an
139+
/// omp.target p should additionally be mapped to the target region
140+
/// using a MapInfoOp. This is most common when an allocatable is privatized.
141+
/// In such cases, the descriptor is use in privatization and needs to be
142+
/// mapped on to the device.
143+
bool needsMap() {
144+
Value blockArg0 = getAllocRegion().getArgument(0);
145+
return !blockArg0.use_empty();
146+
}
147+
138148
}];
139149

140150
let hasRegionVerifier = 1;

mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp

Lines changed: 125 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -305,10 +305,6 @@ static LogicalResult checkImplementationStatus(Operation &op) {
305305
if (privatizer.getDataSharingType() ==
306306
omp::DataSharingClauseType::FirstPrivate)
307307
result = todo("firstprivate");
308-
309-
if (!privatizer.getDeallocRegion().empty())
310-
result = op.emitError("not yet implemented: privatization of "
311-
"structures in omp.target operation");
312308
}
313309
}
314310
checkThreadLimit(op, result);
@@ -3814,6 +3810,43 @@ createDeviceArgumentAccessor(MapInfoData &mapData, llvm::Argument &arg,
38143810
return builder.saveIP();
38153811
}
38163812

3813+
/// Return the llvm::Value * corresponding to the `privateVar` that
3814+
/// is being privatized. It isn't always as simple as looking up
3815+
/// moduleTranslation with privateVar. For instance, in case of
3816+
/// an allocatable, the descriptor for the allocatable is privatized.
3817+
/// This descriptor is mapped using an MapInfoOp. So, this function
3818+
/// will return a pointer to the llvm::Value corresponding to the
3819+
/// block argument for the mapped descriptor.
3820+
static llvm::Value *
3821+
findHostAssociatedValue(Value privateVar, omp::TargetOp targetOp,
3822+
llvm::DenseMap<Value, int> &mappedPrivateVars,
3823+
llvm::IRBuilderBase &builder,
3824+
LLVM::ModuleTranslation &moduleTranslation) {
3825+
if (mappedPrivateVars.contains(privateVar)) {
3826+
int blockArgIndex = mappedPrivateVars[privateVar];
3827+
Value blockArg = targetOp.getRegion().getArgument(blockArgIndex);
3828+
Type privVarType = privateVar.getType();
3829+
Type blockArgType = blockArg.getType();
3830+
assert(isa<LLVM::LLVMPointerType>(blockArgType) &&
3831+
"A block argument corresponding to a mapped var should have "
3832+
"!llvm.ptr type");
3833+
3834+
if (privVarType == blockArg.getType())
3835+
return moduleTranslation.lookupValue(blockArg);
3836+
3837+
if (!isa<LLVM::LLVMPointerType>(privVarType)) {
3838+
// This typically happens when the privatized type is lowered from
3839+
// boxchar<KIND> and gets lowered to !llvm.struct<(ptr, i64)>. That is the
3840+
// struct/pair is passed by value. But, mapped values are passed only as
3841+
// pointers, so before we privatize, we must load the pointer.
3842+
llvm::Value *load =
3843+
builder.CreateLoad(moduleTranslation.convertType(privVarType),
3844+
moduleTranslation.lookupValue(blockArg));
3845+
return load;
3846+
}
3847+
}
3848+
return moduleTranslation.lookupValue(privateVar);
3849+
}
38173850
static LogicalResult
38183851
convertOmpTarget(Operation &opInst, llvm::IRBuilderBase &builder,
38193852
LLVM::ModuleTranslation &moduleTranslation) {
@@ -3825,6 +3858,19 @@ convertOmpTarget(Operation &opInst, llvm::IRBuilderBase &builder,
38253858
bool isTargetDevice = ompBuilder->Config.isTargetDevice();
38263859
auto parentFn = opInst.getParentOfType<LLVM::LLVMFuncOp>();
38273860
auto &targetRegion = targetOp.getRegion();
3861+
// Holds the private vars that have been mapped along with
3862+
// the block argument that corresponds to the MapInfoOp
3863+
// corresponding to the private var in question.
3864+
// So, for instance
3865+
//
3866+
// %10 = omp.map.info var_ptr(%6#0 : !fir.ref<!fir.box<!fir.heap<i32>>>, ..)
3867+
// omp.target map_entries(%10 -> %arg0) private(@box.privatizer %6#0-> %arg1)
3868+
//
3869+
// Then, %10 has been created so that the descriptor can be used by the
3870+
// privatizer
3871+
// @box.privatizer on the device side. Here we'd record {%6#0, 0} in the
3872+
// mappedPrivateVars map.
3873+
llvm::DenseMap<Value, int> mappedPrivateVars;
38283874
DataLayout dl = DataLayout(opInst.getParentOfType<ModuleOp>());
38293875
SmallVector<Value> mapVars = targetOp.getMapVars();
38303876
ArrayRef<BlockArgument> mapBlockArgs =
@@ -3836,6 +3882,55 @@ convertOmpTarget(Operation &opInst, llvm::IRBuilderBase &builder,
38363882
bool isOffloadEntry =
38373883
isTargetDevice || !ompBuilder->Config.TargetTriples.empty();
38383884

3885+
// For some private variables, the MapsForPrivatizedVariablesPass
3886+
// creates MapInfoOp instances. Go through the private variables and
3887+
// the mapped variables so that during codegeneration we are able
3888+
// to quickly look up the corresponding map variable, if any for each
3889+
// private variable.
3890+
if (!targetOp.getPrivateVars().empty() && !targetOp.getMapVars().empty()) {
3891+
auto argIface = llvm::cast<omp::BlockArgOpenMPOpInterface>(*targetOp);
3892+
OperandRange privateVars = targetOp.getPrivateVars();
3893+
std::optional<ArrayAttr> privateSyms = targetOp.getPrivateSyms();
3894+
std::optional<DenseI64ArrayAttr> privateMapIndices =
3895+
targetOp.getPrivateMapsAttr();
3896+
3897+
for (auto [privVarIdx, privVarSymPair] :
3898+
llvm::enumerate(llvm::zip_equal(privateVars, *privateSyms))) {
3899+
auto privVar = std::get<0>(privVarSymPair);
3900+
auto privSym = std::get<1>(privVarSymPair);
3901+
3902+
SymbolRefAttr privatizerName = llvm::cast<SymbolRefAttr>(privSym);
3903+
omp::PrivateClauseOp privatizer =
3904+
findPrivatizer(targetOp, privatizerName);
3905+
3906+
if (!privatizer.needsMap())
3907+
continue;
3908+
3909+
mlir::Value mappedValue =
3910+
targetOp.getMappedValueForPrivateVar(privVarIdx);
3911+
assert(mappedValue);
3912+
3913+
// The MapInfoOp defining the map var isn't really needed later.
3914+
// So, we don't store it in any datastructure. Instead, we just
3915+
// do some sanity checks on it right now.
3916+
auto mapInfoOp = mappedValue.getDefiningOp<omp::MapInfoOp>();
3917+
Type varType = mapInfoOp.getVarType();
3918+
3919+
// Check #1: Check that the type of the private variable matches
3920+
// the type of the variable being mapped.
3921+
if (!isa<LLVM::LLVMPointerType>(privVar.getType()))
3922+
assert(
3923+
varType == privVar.getType() &&
3924+
"Type of private var doesn't match the type of the mapped value");
3925+
3926+
// Ok, only 1 sanity check for now.
3927+
// Record the index of the block argument corresponding to this
3928+
// mapvar.
3929+
mappedPrivateVars.insert({privVar, argIface.getMapBlockArgsStart() +
3930+
(*privateMapIndices)[privVarIdx]});
3931+
}
3932+
}
3933+
38393934
using InsertPointTy = llvm::OpenMPIRBuilder::InsertPointTy;
38403935
auto bodyCB = [&](InsertPointTy allocaIP, InsertPointTy codeGenIP)
38413936
-> llvm::OpenMPIRBuilder::InsertPointOrErrorTy {
@@ -3862,9 +3957,10 @@ convertOmpTarget(Operation &opInst, llvm::IRBuilderBase &builder,
38623957
moduleTranslation.lookupValue(mapInfoOp.getVarPtr());
38633958
moduleTranslation.mapValue(arg, mapOpValue);
38643959
}
3865-
38663960
// Do privatization after moduleTranslation has already recorded
38673961
// mapped values.
3962+
SmallVector<llvm::Value *> llvmPrivateVars;
3963+
SmallVector<Region *> privateCleanupRegions;
38683964
if (!targetOp.getPrivateVars().empty()) {
38693965
builder.restoreIP(allocaIP);
38703966

@@ -3880,11 +3976,13 @@ convertOmpTarget(Operation &opInst, llvm::IRBuilderBase &builder,
38803976
omp::PrivateClauseOp privatizer = findPrivatizer(&opInst, privSym);
38813977
assert(privatizer.getDataSharingType() !=
38823978
omp::DataSharingClauseType::FirstPrivate &&
3883-
privatizer.getDeallocRegion().empty() &&
38843979
"unsupported privatizer");
3885-
moduleTranslation.mapValue(privatizer.getAllocMoldArg(),
3886-
moduleTranslation.lookupValue(privVar));
38873980
Region &allocRegion = privatizer.getAllocRegion();
3981+
BlockArgument allocRegionArg = allocRegion.getArgument(0);
3982+
moduleTranslation.mapValue(
3983+
allocRegionArg,
3984+
findHostAssociatedValue(privVar, targetOp, mappedPrivateVars,
3985+
builder, moduleTranslation));
38883986
SmallVector<llvm::Value *, 1> yieldedValues;
38893987
if (failed(inlineConvertOmpRegions(
38903988
allocRegion, "omp.targetop.privatizer", builder,
@@ -3893,7 +3991,12 @@ convertOmpTarget(Operation &opInst, llvm::IRBuilderBase &builder,
38933991
"failed to inline `alloc` region of `omp.private`");
38943992
}
38953993
assert(yieldedValues.size() == 1);
3896-
moduleTranslation.mapValue(privBlockArg, yieldedValues.front());
3994+
llvm::Value *llvmReplacementValue = yieldedValues.front();
3995+
moduleTranslation.mapValue(privBlockArg, llvmReplacementValue);
3996+
if (!privatizer.getDeallocRegion().empty()) {
3997+
llvmPrivateVars.push_back(llvmReplacementValue);
3998+
privateCleanupRegions.push_back(&privatizer.getDeallocRegion());
3999+
}
38974000
moduleTranslation.forgetMapping(allocRegion);
38984001
builder.restoreIP(builder.saveIP());
38994002
}
@@ -3905,6 +4008,19 @@ convertOmpTarget(Operation &opInst, llvm::IRBuilderBase &builder,
39054008
return exitBlock.takeError();
39064009

39074010
builder.SetInsertPoint(*exitBlock);
4011+
if (!llvmPrivateVars.empty()) {
4012+
assert(llvmPrivateVars.size() == privateCleanupRegions.size() &&
4013+
"Number of private variables needing cleanup not equal to number"
4014+
"of privatizers with dealloc regions");
4015+
if (failed(inlineOmpRegionCleanup(
4016+
privateCleanupRegions, llvmPrivateVars, moduleTranslation,
4017+
builder, "omp.targetop.private.cleanup",
4018+
/*shouldLoadCleanupRegionArg=*/false))) {
4019+
return llvm::createStringError(
4020+
"failed to inline `dealloc` region of `omp.private` "
4021+
"op in the target region");
4022+
}
4023+
}
39084024
return builder.saveIP();
39094025
};
39104026

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
// RUN: mlir-translate -mlir-to-llvmir %s | FileCheck %s
2+
3+
llvm.func @dealloc_foo_0(!llvm.ptr)
4+
5+
omp.private {type = private} @box.heap_privatizer0 : !llvm.ptr alloc {
6+
^bb0(%arg0: !llvm.ptr):
7+
%0 = llvm.mlir.constant(1 : i32) : i32
8+
%7 = llvm.alloca %0 x !llvm.struct<(ptr, i64, i32, i8, i8, i8, i8)> : (i32) -> !llvm.ptr
9+
omp.yield(%7 : !llvm.ptr)
10+
} dealloc {
11+
^bb0(%arg0: !llvm.ptr):
12+
llvm.call @dealloc_foo_0(%arg0) : (!llvm.ptr) -> ()
13+
omp.yield
14+
}
15+
16+
llvm.func @alloc_foo_1(!llvm.ptr)
17+
llvm.func @dealloc_foo_1(!llvm.ptr)
18+
19+
omp.private {type = private} @box.heap_privatizer1 : !llvm.ptr alloc {
20+
^bb0(%arg0: !llvm.ptr):
21+
%0 = llvm.mlir.constant(1 : i32) : i32
22+
%7 = llvm.alloca %0 x !llvm.struct<(ptr, i64, i32, i8, i8, i8, i8)> : (i32) -> !llvm.ptr
23+
llvm.call @alloc_foo_1(%arg0) : (!llvm.ptr) -> ()
24+
omp.yield(%7 : !llvm.ptr)
25+
} dealloc {
26+
^bb0(%arg0: !llvm.ptr):
27+
llvm.call @dealloc_foo_1(%arg0) : (!llvm.ptr) -> ()
28+
omp.yield
29+
}
30+
31+
llvm.func @target_allocatable_(%arg0: !llvm.ptr {fir.bindc_name = "lb"}, %arg1: !llvm.ptr {fir.bindc_name = "ub"}, %arg2: !llvm.ptr {fir.bindc_name = "l"}) attributes {fir.internal_name = "_QPtarget_allocatable"} {
32+
%6 = llvm.mlir.constant(1 : i64) : i64
33+
%7 = llvm.alloca %6 x i32 {bindc_name = "mapped_var"} : (i64) -> !llvm.ptr
34+
%13 = llvm.alloca %6 x !llvm.struct<(ptr, i64, i32, i8, i8, i8, i8)> {bindc_name = "alloc_var0"} : (i64) -> !llvm.ptr
35+
%14 = llvm.alloca %6 x !llvm.struct<(ptr, i64, i32, i8, i8, i8, i8)> {bindc_name = "alloc_var1"} : (i64) -> !llvm.ptr
36+
%53 = omp.map.info var_ptr(%7 : !llvm.ptr, i32) map_clauses(implicit, exit_release_or_enter_alloc) capture(ByCopy) -> !llvm.ptr {name = "mapped_var"}
37+
%54 = omp.map.info var_ptr(%13 : !llvm.ptr, !llvm.struct<(ptr, i64, i32, i8, i8, i8, i8)>) map_clauses(to) capture(ByRef) -> !llvm.ptr
38+
%55 = omp.map.info var_ptr(%14 : !llvm.ptr, !llvm.struct<(ptr, i64, i32, i8, i8, i8, i8)>) map_clauses(to) capture(ByRef) -> !llvm.ptr
39+
omp.target map_entries(%53 -> %arg3, %54 -> %arg4, %55 ->%arg5 : !llvm.ptr, !llvm.ptr, !llvm.ptr) private(@box.heap_privatizer0 %13 -> %arg6 [map_idx=1], @box.heap_privatizer1 %14 -> %arg7 [map_idx=2]: !llvm.ptr, !llvm.ptr) {
40+
llvm.call @use_private_var0(%arg6) : (!llvm.ptr) -> ()
41+
llvm.call @use_private_var1(%arg7) : (!llvm.ptr) -> ()
42+
omp.terminator
43+
}
44+
llvm.return
45+
}
46+
47+
48+
llvm.func @use_private_var0(!llvm.ptr) -> ()
49+
llvm.func @use_private_var1(!llvm.ptr) -> ()
50+
51+
// The first set of checks ensure that we are calling the offloaded function
52+
// with the right arguments, especially the second argument which needs to
53+
// be a memory reference to the descriptor for the privatized allocatable
54+
// CHECK: define void @target_allocatable_
55+
// CHECK-NOT: define internal void
56+
// CHECK: %[[DESC_ALLOC0:.*]] = alloca { ptr, i64, i32, i8, i8, i8, i8 }, i64 1
57+
// CHECK: %[[DESC_ALLOC1:.*]] = alloca { ptr, i64, i32, i8, i8, i8, i8 }, i64 1
58+
// CHECK: call void @__omp_offloading_[[OFFLOADED_FUNCTION:.*]](ptr {{[^,]+}},
59+
// CHECK-SAME: ptr %[[DESC_ALLOC0]], ptr %[[DESC_ALLOC1]])
60+
61+
// CHECK: define internal void @__omp_offloading_[[OFFLOADED_FUNCTION]]
62+
// CHECK-SAME: (ptr {{[^,]+}}, ptr %[[DESCRIPTOR_ARG0:[^,]+]],
63+
// CHECK-SAME: ptr %[[DESCRIPTOR_ARG1:.*]]) {
64+
65+
// `var0` privatrizer `alloc`
66+
// CHECK: %[[PRIV_DESC0:.*]] = alloca { ptr, i64, i32, i8, i8, i8, i8 }
67+
68+
// `var1` privatrizer `alloc`
69+
// CHECK: %[[PRIV_DESC1:.*]] = alloca { ptr, i64, i32, i8, i8, i8, i8 }
70+
// CHECK: call void @alloc_foo_1(ptr %[[DESCRIPTOR_ARG1]])
71+
72+
// target op body
73+
// CHECK: call void @use_private_var0(ptr %[[PRIV_DESC0]]
74+
// CHECK: call void @use_private_var1(ptr %[[PRIV_DESC1]]
75+
76+
// `var0` privatrizer `dealloc`
77+
// CHECK: call void @dealloc_foo_0(ptr %[[PRIV_DESC0]])
78+
79+
// `var1` privatrizer `dealloc`
80+
// CHECK: call void @dealloc_foo_1(ptr %[[PRIV_DESC1]])
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// RUN: mlir-translate -mlir-to-llvmir %s | FileCheck %s
2+
3+
llvm.func @alloc_foo_1(!llvm.ptr)
4+
llvm.func @dealloc_foo_1(!llvm.ptr)
5+
6+
omp.private {type = private} @box.heap_privatizer : !llvm.ptr alloc {
7+
^bb0(%arg0: !llvm.ptr):
8+
%0 = llvm.mlir.constant(1 : i32) : i32
9+
%7 = llvm.alloca %0 x !llvm.struct<(ptr, i64, i32, i8, i8, i8, i8)> : (i32) -> !llvm.ptr
10+
llvm.call @alloc_foo_1(%arg0) : (!llvm.ptr) -> ()
11+
omp.yield(%7 : !llvm.ptr)
12+
} dealloc {
13+
^bb0(%arg0: !llvm.ptr):
14+
llvm.call @dealloc_foo_1(%arg0) : (!llvm.ptr) -> ()
15+
omp.yield
16+
}
17+
18+
llvm.func @target_allocatable_(%arg0: !llvm.ptr {fir.bindc_name = "lb"}, %arg1: !llvm.ptr {fir.bindc_name = "ub"}, %arg2: !llvm.ptr {fir.bindc_name = "l"}) attributes {fir.internal_name = "_QPtarget_allocatable"} {
19+
%0 = llvm.mlir.constant(1 : i32) : i32
20+
%1 = llvm.alloca %0 x !llvm.struct<(ptr, i64, i32, i8, i8, i8, i8)> {alignment = 8 : i64} : (i32) -> !llvm.ptr
21+
%3 = llvm.alloca %0 x !llvm.struct<(ptr, i64, i32, i8, i8, i8, i8)> {alignment = 8 : i64} : (i32) -> !llvm.ptr
22+
%4 = llvm.mlir.constant(1 : i64) : i64
23+
%5 = llvm.alloca %4 x f32 {bindc_name = "real_var"} : (i64) -> !llvm.ptr
24+
%7 = llvm.alloca %4 x i32 {bindc_name = "mapped_var"} : (i64) -> !llvm.ptr
25+
%9 = llvm.alloca %4 x !llvm.struct<(f32, f32)> {bindc_name = "comp_var"} : (i64) -> !llvm.ptr
26+
%11 = llvm.alloca %0 x !llvm.struct<(ptr, i64, i32, i8, i8, i8, i8)> {alignment = 8 : i64} : (i32) -> !llvm.ptr
27+
%13 = llvm.alloca %4 x !llvm.struct<(ptr, i64, i32, i8, i8, i8, i8)> {bindc_name = "alloc_var"} : (i64) -> !llvm.ptr
28+
%39 = llvm.load %arg2 : !llvm.ptr -> i64
29+
%52 = llvm.alloca %39 x f32 {bindc_name = "real_arr"} : (i64) -> !llvm.ptr
30+
%53 = omp.map.info var_ptr(%7 : !llvm.ptr, i32) map_clauses(implicit, exit_release_or_enter_alloc) capture(ByCopy) -> !llvm.ptr {name = "mapped_var"}
31+
%54 = omp.map.info var_ptr(%13 : !llvm.ptr, !llvm.struct<(ptr, i64, i32, i8, i8, i8, i8)>) map_clauses(to) capture(ByRef) -> !llvm.ptr
32+
omp.target map_entries(%53 -> %arg3, %54 -> %arg4 : !llvm.ptr, !llvm.ptr) private(@box.heap_privatizer %13 -> %arg5 [map_idx=1] : !llvm.ptr) {
33+
llvm.call @use_private_var(%arg5) : (!llvm.ptr) -> ()
34+
omp.terminator
35+
}
36+
llvm.return
37+
}
38+
39+
llvm.func @use_private_var(!llvm.ptr) -> ()
40+
41+
llvm.func @_FortranAAssign(!llvm.ptr, !llvm.ptr, !llvm.ptr, i32) -> !llvm.struct<()> attributes {fir.runtime, sym_visibility = "private"}
42+
43+
// The first set of checks ensure that we are calling the offloaded function
44+
// with the right arguments, especially the second argument which needs to
45+
// be a memory reference to the descriptor for the privatized allocatable
46+
// CHECK: define void @target_allocatable_
47+
// CHECK-NOT: define internal void
48+
// CHECK: %[[DESC_ALLOC:.*]] = alloca { ptr, i64, i32, i8, i8, i8, i8 }, i64 1
49+
// CHECK: call void @__omp_offloading_[[OFFLOADED_FUNCTION:.*]](ptr {{[^,]+}},
50+
// CHECK-SAME: ptr %[[DESC_ALLOC]])
51+
52+
// The second set of checks ensure that to allocate memory for the
53+
// allocatable, we are, in fact, using the memory reference of the descriptor
54+
// passed as the second argument to the offloaded function.
55+
// CHECK: define internal void @__omp_offloading_[[OFFLOADED_FUNCTION]]
56+
// CHECK-SAME: (ptr {{[^,]+}}, ptr %[[DESCRIPTOR_ARG:.*]]) {
57+
// CHECK: %[[DESC_TO_DEALLOC:.*]] = alloca { ptr, i64, i32, i8, i8, i8, i8 }
58+
// CHECK: call void @alloc_foo_1(ptr %[[DESCRIPTOR_ARG]])
59+
60+
61+
// CHECK: call void @use_private_var(ptr %[[DESC_TO_DEALLOC]]
62+
63+
// Now, check the deallocation of the private var.
64+
// CHECK: call void @dealloc_foo_1(ptr %[[DESC_TO_DEALLOC]])

0 commit comments

Comments
 (0)