Skip to content

Commit 0d05d4a

Browse files
committed
Convert private clauses to LLVM.
1 parent 8903951 commit 0d05d4a

File tree

2 files changed

+240
-34
lines changed

2 files changed

+240
-34
lines changed
Lines changed: 164 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,179 @@
1+
! TODO Convert this file into a bunch of lit tests for each conversion step.
2+
13
subroutine delayed_privatization()
24
integer :: var1
35
integer :: var2
46

7+
var1 = 111
8+
var2 = 222
9+
510
!$OMP PARALLEL FIRSTPRIVATE(var1, var2)
611
var1 = var1 + var2 + 2
712
!$OMP END PARALLEL
813

914
end subroutine
1015

11-
! This is what flang emits with the PoC:
12-
! --------------------------------------
16+
! -----------------------------------------
17+
! ## This is what flang emits with the PoC:
18+
! -----------------------------------------
1319
!
14-
!func.func @_QPdelayed_privatization() {
15-
! %0 = fir.alloca i32 {bindc_name = "var1", uniq_name = "_QFdelayed_privatizationEvar1"}
16-
! %1 = fir.alloca i32 {bindc_name = "var2", uniq_name = "_QFdelayed_privatizationEvar2"}
17-
! omp.parallel private(@var1.privatizer %0, @var2.privatizer %1 : !fir.ref<i32>, !fir.ref<i32>) {
18-
! %2 = fir.load %0 : !fir.ref<i32>
19-
! %3 = fir.load %1 : !fir.ref<i32>
20-
! %4 = arith.addi %2, %3 : i32
21-
! %c2_i32 = arith.constant 2 : i32
22-
! %5 = arith.addi %4, %c2_i32 : i32
23-
! fir.store %5 to %0 : !fir.ref<i32>
24-
! omp.terminator
20+
! ----------------------------
21+
! ### Conversion to FIR + OMP:
22+
! ----------------------------
23+
!module {
24+
! func.func @_QPdelayed_privatization() {
25+
! %0 = fir.alloca i32 {bindc_name = "var1", uniq_name = "_QFdelayed_privatizationEvar1"}
26+
! %1 = fir.alloca i32 {bindc_name = "var2", uniq_name = "_QFdelayed_privatizationEvar2"}
27+
! %c111_i32 = arith.constant 111 : i32
28+
! fir.store %c111_i32 to %0 : !fir.ref<i32>
29+
! %c222_i32 = arith.constant 222 : i32
30+
! fir.store %c222_i32 to %1 : !fir.ref<i32>
31+
! omp.parallel private(@var1.privatizer %0, @var2.privatizer %1 : !fir.ref<i32>, !fir.ref<i32>) {
32+
! %2 = fir.load %0 : !fir.ref<i32>
33+
! %3 = fir.load %1 : !fir.ref<i32>
34+
! %4 = arith.addi %2, %3 : i32
35+
! %c2_i32 = arith.constant 2 : i32
36+
! %5 = arith.addi %4, %c2_i32 : i32
37+
! fir.store %5 to %0 : !fir.ref<i32>
38+
! omp.terminator
39+
! }
40+
! return
2541
! }
26-
! return
42+
! "omp.private"() <{function_type = (!fir.ref<i32>) -> !fir.ref<i32>, sym_name = "var1.privatizer"}> ({
43+
! ^bb0(%arg0: !fir.ref<i32>):
44+
! %0 = fir.alloca i32 {bindc_name = "var1", pinned, uniq_name = "_QFdelayed_privatizationEvar1"}
45+
! %1 = fir.load %arg0 : !fir.ref<i32>
46+
! fir.store %1 to %0 : !fir.ref<i32>
47+
! omp.yield(%0 : !fir.ref<i32>)
48+
! }) : () -> ()
49+
! "omp.private"() <{function_type = (!fir.ref<i32>) -> !fir.ref<i32>, sym_name = "var2.privatizer"}> ({
50+
! ^bb0(%arg0: !fir.ref<i32>):
51+
! %0 = fir.alloca i32 {bindc_name = "var2", pinned, uniq_name = "_QFdelayed_privatizationEvar2"}
52+
! %1 = fir.load %arg0 : !fir.ref<i32>
53+
! fir.store %1 to %0 : !fir.ref<i32>
54+
! omp.yield(%0 : !fir.ref<i32>)
55+
! }) : () -> ()
2756
!}
2857
!
29-
!"omp.private"() <{function_type = (!fir.ref<i32>) -> !fir.ref<i32>, sym_name = "var1.privatizer"}> ({
30-
!^bb0(%arg0: !fir.ref<i32>):
31-
! %0 = fir.alloca i32 {bindc_name = "var1", pinned, uniq_name = "_QFdelayed_privatizationEvar1"}
32-
! %1 = fir.load %arg0 : !fir.ref<i32>
33-
! fir.store %1 to %0 : !fir.ref<i32>
34-
! omp.yield(%0 : !fir.ref<i32>)
35-
!}) : () -> ()
58+
! -----------------------------
59+
! ### Conversion to LLVM + OMP:
60+
! -----------------------------
61+
!module {
62+
! llvm.func @_QPdelayed_privatization() {
63+
! %0 = llvm.mlir.constant(1 : i64) : i64
64+
! %1 = llvm.alloca %0 x i32 {bindc_name = "var1"} : (i64) -> !llvm.ptr
65+
! %2 = llvm.mlir.constant(1 : i64) : i64
66+
! %3 = llvm.alloca %2 x i32 {bindc_name = "var2"} : (i64) -> !llvm.ptr
67+
! %4 = llvm.mlir.constant(111 : i32) : i32
68+
! llvm.store %4, %1 : i32, !llvm.ptr
69+
! %5 = llvm.mlir.constant(222 : i32) : i32
70+
! llvm.store %5, %3 : i32, !llvm.ptr
71+
! omp.parallel private(@var1.privatizer %1, @var2.privatizer %3 : !llvm.ptr, !llvm.ptr) {
72+
! %6 = llvm.load %1 : !llvm.ptr -> i32
73+
! %7 = llvm.load %3 : !llvm.ptr -> i32
74+
! %8 = llvm.add %6, %7 : i32
75+
! %9 = llvm.mlir.constant(2 : i32) : i32
76+
! %10 = llvm.add %8, %9 : i32
77+
! llvm.store %10, %1 : i32, !llvm.ptr
78+
! omp.terminator
79+
! }
80+
! llvm.return
81+
! }
82+
! "omp.private"() <{function_type = (!llvm.ptr) -> !llvm.ptr, sym_name = "var1.privatizer"}> ({
83+
! ^bb0(%arg0: !llvm.ptr):
84+
! %0 = llvm.mlir.constant(1 : i64) : i64
85+
! %1 = llvm.alloca %0 x i32 {bindc_name = "var1", pinned} : (i64) -> !llvm.ptr
86+
! %2 = llvm.load %arg0 : !llvm.ptr -> i32
87+
! llvm.store %2, %1 : i32, !llvm.ptr
88+
! omp.yield(%1 : !llvm.ptr)
89+
! }) : () -> ()
90+
! "omp.private"() <{function_type = (!llvm.ptr) -> !llvm.ptr, sym_name = "var2.privatizer"}> ({
91+
! ^bb0(%arg0: !llvm.ptr):
92+
! %0 = llvm.mlir.constant(1 : i64) : i64
93+
! %1 = llvm.alloca %0 x i32 {bindc_name = "var2", pinned} : (i64) -> !llvm.ptr
94+
! %2 = llvm.load %arg0 : !llvm.ptr -> i32
95+
! llvm.store %2, %1 : i32, !llvm.ptr
96+
! omp.yield(%1 : !llvm.ptr)
97+
! }) : () -> ()
98+
!}
3699
!
37-
!"omp.private"() <{function_type = (!fir.ref<i32>) -> !fir.ref<i32>, sym_name = "var2.privatizer"}> ({
38-
!^bb0(%arg0: !fir.ref<i32>):
39-
! %0 = fir.alloca i32 {bindc_name = "var2", pinned, uniq_name = "_QFdelayed_privatizationEvar2"}
40-
! %1 = fir.load %arg0 : !fir.ref<i32>
41-
! fir.store %1 to %0 : !fir.ref<i32>
42-
! omp.yield(%0 : !fir.ref<i32>)
43-
!}) : () -> ()
100+
! --------------------------
101+
! ### Conversion to LLVM IR:
102+
! --------------------------
103+
!%struct.ident_t = type { i32, i32, i32, i32, ptr }
104+
105+
!@0 = private unnamed_addr constant [23 x i8] c";unknown;unknown;0;0;;\00", align 1
106+
!@1 = private unnamed_addr constant %struct.ident_t { i32 0, i32 2, i32 0, i32 22, ptr @0 }, align 8
107+
108+
!define void @_QPdelayed_privatization() {
109+
! %structArg = alloca { ptr, ptr }, align 8
110+
! %1 = alloca i32, i64 1, align 4
111+
! %2 = alloca i32, i64 1, align 4
112+
! store i32 111, ptr %1, align 4
113+
! store i32 222, ptr %2, align 4
114+
! br label %entry
115+
116+
!entry: ; preds = %0
117+
! %omp_global_thread_num = call i32 @__kmpc_global_thread_num(ptr @1)
118+
! br label %omp_parallel
119+
120+
!omp_parallel: ; preds = %entry
121+
! %gep_ = getelementptr { ptr, ptr }, ptr %structArg, i32 0, i32 0
122+
! store ptr %1, ptr %gep_, align 8
123+
! %gep_2 = getelementptr { ptr, ptr }, ptr %structArg, i32 0, i32 1
124+
! store ptr %2, ptr %gep_2, align 8
125+
! call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @1, i32 1, ptr @_QPdelayed_privatization..omp_par, ptr %structArg)
126+
! br label %omp.par.outlined.exit
127+
128+
!omp.par.outlined.exit: ; preds = %omp_parallel
129+
! br label %omp.par.exit.split
130+
131+
!omp.par.exit.split: ; preds = %omp.par.outlined.exit
132+
! ret void
133+
!}
134+
135+
!; Function Attrs: nounwind
136+
!define internal void @_QPdelayed_privatization..omp_par(ptr noalias %tid.addr, ptr noalias %zero.addr, ptr %0) #0 {
137+
!omp.par.entry:
138+
! %gep_ = getelementptr { ptr, ptr }, ptr %0, i32 0, i32 0
139+
! %loadgep_ = load ptr, ptr %gep_, align 8
140+
! %gep_1 = getelementptr { ptr, ptr }, ptr %0, i32 0, i32 1
141+
! %loadgep_2 = load ptr, ptr %gep_1, align 8
142+
! %tid.addr.local = alloca i32, align 4
143+
! %1 = load i32, ptr %tid.addr, align 4
144+
! store i32 %1, ptr %tid.addr.local, align 4
145+
! %tid = load i32, ptr %tid.addr.local, align 4
146+
! %2 = alloca i32, i64 1, align 4
147+
! %3 = load i32, ptr %loadgep_, align 4
148+
! store i32 %3, ptr %2, align 4
149+
! %4 = alloca i32, i64 1, align 4
150+
! %5 = load i32, ptr %loadgep_2, align 4
151+
! store i32 %5, ptr %4, align 4
152+
! br label %omp.par.region
153+
154+
!omp.par.region: ; preds = %omp.par.entry
155+
! br label %omp.par.region1
156+
157+
!omp.par.region1: ; preds = %omp.par.region
158+
! %6 = load i32, ptr %2, align 4
159+
! %7 = load i32, ptr %4, align 4
160+
! %8 = add i32 %6, %7
161+
! %9 = add i32 %8, 2
162+
! store i32 %9, ptr %2, align 4
163+
! br label %omp.region.cont
164+
165+
!omp.region.cont: ; preds = %omp.par.region1
166+
! br label %omp.par.pre_finalize
167+
168+
!omp.par.pre_finalize: ; preds = %omp.region.cont
169+
! br label %omp.par.outlined.exit.exitStub
170+
171+
!omp.par.outlined.exit.exitStub: ; preds = %omp.par.pre_finalize
172+
! ret void
173+
!}
174+
175+
!; Function Attrs: nounwind
176+
!declare i32 @__kmpc_global_thread_num(ptr) #0
177+
178+
!; Function Attrs: nounwind
179+
!declare !callback !2 void @__kmpc_fork_call(ptr, i32, ptr, ...) #0

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

Lines changed: 76 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1092,6 +1092,75 @@ convertOmpParallel(omp::ParallelOp opInst, llvm::IRBuilderBase &builder,
10921092
llvm::Value *&replacementValue) -> InsertPointTy {
10931093
replacementValue = &vPtr;
10941094

1095+
// If this is a private value, this lambda will return the corresponding
1096+
// mlir value and its `PrivateClauseOp`. Otherwise, empty values are
1097+
// returned.
1098+
auto [privVar,
1099+
privInit] = [&]() -> std::pair<mlir::Value, omp::PrivateClauseOp> {
1100+
if (!opInst.getPrivateVars().empty()) {
1101+
auto privVars = opInst.getPrivateVars();
1102+
auto privInits = opInst.getPrivateInits();
1103+
assert(privInits && privInits->size() == privVars.size());
1104+
1105+
const auto *privInitIt = privInits->begin();
1106+
for (auto privVarIt = privVars.begin(); privVarIt != privVars.end();
1107+
++privVarIt, ++privInitIt) {
1108+
auto *llvmPrivVarOp = moduleTranslation.lookupValue(*privVarIt);
1109+
if (llvmPrivVarOp != &vPtr) {
1110+
continue;
1111+
}
1112+
1113+
auto privSym = llvm::cast<SymbolRefAttr>(*privInitIt);
1114+
auto privOp =
1115+
SymbolTable::lookupNearestSymbolFrom<omp::PrivateClauseOp>(
1116+
opInst, privSym);
1117+
1118+
return {*privVarIt, privOp};
1119+
}
1120+
}
1121+
1122+
return {mlir::Value(), omp::PrivateClauseOp()};
1123+
}();
1124+
1125+
if (privVar) {
1126+
1127+
// Replace the privatizer block argument with mlir value being privatized.
1128+
// This way, the body of the privatizer will be changed from using the
1129+
// region/block argument to the value being privatized.
1130+
assert(privInit->getRegions().front().getNumArguments() == 1);
1131+
1132+
auto arg = privInit->getRegions().front().getArgument(0);
1133+
for (auto &op : privInit->getRegions().front().front()) {
1134+
op.replaceUsesOfWith(arg, privVar);
1135+
}
1136+
1137+
auto oldIP = builder.saveIP();
1138+
builder.restoreIP(allocaIP);
1139+
1140+
// Temporarily unlink the terminator from its parent since
1141+
// `inlineConvertOmpRegions` expects the insertion block to **not**
1142+
// contain a terminator.
1143+
auto &allocaTerminator = builder.GetInsertBlock()->back();
1144+
assert(lastInstr.isTerminator());
1145+
allocaTerminator.removeFromParent();
1146+
1147+
SmallVector<llvm::Value *, 1> yieldedValues;
1148+
if (failed(inlineConvertOmpRegions(privInit->getRegion(0),
1149+
"omp.privatizer", builder,
1150+
moduleTranslation, &yieldedValues))) {
1151+
// TODO proper error-handling.
1152+
builder.restoreIP(oldIP);
1153+
return codeGenIP;
1154+
}
1155+
1156+
allocaTerminator.insertAfter(&builder.GetInsertBlock()->back());
1157+
1158+
assert(yieldedValues.size() == 1);
1159+
replacementValue = yieldedValues.front();
1160+
1161+
builder.restoreIP(oldIP);
1162+
}
1163+
10951164
return codeGenIP;
10961165
};
10971166

@@ -2774,12 +2843,13 @@ LogicalResult OpenMPDialectLLVMIRTranslationInterface::convertOperation(
27742843
.Case([&](omp::TargetOp) {
27752844
return convertOmpTarget(*op, builder, moduleTranslation);
27762845
})
2777-
.Case<omp::MapInfoOp, omp::DataBoundsOp>([&](auto op) {
2778-
// No-op, should be handled by relevant owning operations e.g.
2779-
// TargetOp, EnterDataOp, ExitDataOp, DataOp etc. and then
2780-
// discarded
2781-
return success();
2782-
})
2846+
.Case<omp::MapInfoOp, omp::DataBoundsOp, omp::PrivateClauseOp>(
2847+
[&](auto op) {
2848+
// No-op, should be handled by relevant owning operations e.g.
2849+
// TargetOp, EnterDataOp, ExitDataOp, DataOp etc. and then
2850+
// discarded
2851+
return success();
2852+
})
27832853
.Default([&](Operation *inst) {
27842854
return inst->emitError("unsupported OpenMP operation: ")
27852855
<< inst->getName();

0 commit comments

Comments
 (0)