Skip to content

Commit

Permalink
Convert private clauses to LLVM.
Browse files Browse the repository at this point in the history
  • Loading branch information
ergawy committed Feb 2, 2024
1 parent 8903951 commit 0d05d4a
Show file tree
Hide file tree
Showing 2 changed files with 240 additions and 34 deletions.
192 changes: 164 additions & 28 deletions flang/test/Lower/OpenMP/FIR/delayed_privatization.f90
Original file line number Diff line number Diff line change
@@ -1,43 +1,179 @@
! TODO Convert this file into a bunch of lit tests for each conversion step.

subroutine delayed_privatization()
integer :: var1
integer :: var2

var1 = 111
var2 = 222

!$OMP PARALLEL FIRSTPRIVATE(var1, var2)
var1 = var1 + var2 + 2
!$OMP END PARALLEL

end subroutine

! This is what flang emits with the PoC:
! --------------------------------------
! -----------------------------------------
! ## This is what flang emits with the PoC:
! -----------------------------------------
!
!func.func @_QPdelayed_privatization() {
! %0 = fir.alloca i32 {bindc_name = "var1", uniq_name = "_QFdelayed_privatizationEvar1"}
! %1 = fir.alloca i32 {bindc_name = "var2", uniq_name = "_QFdelayed_privatizationEvar2"}
! omp.parallel private(@var1.privatizer %0, @var2.privatizer %1 : !fir.ref<i32>, !fir.ref<i32>) {
! %2 = fir.load %0 : !fir.ref<i32>
! %3 = fir.load %1 : !fir.ref<i32>
! %4 = arith.addi %2, %3 : i32
! %c2_i32 = arith.constant 2 : i32
! %5 = arith.addi %4, %c2_i32 : i32
! fir.store %5 to %0 : !fir.ref<i32>
! omp.terminator
! ----------------------------
! ### Conversion to FIR + OMP:
! ----------------------------
!module {
! func.func @_QPdelayed_privatization() {
! %0 = fir.alloca i32 {bindc_name = "var1", uniq_name = "_QFdelayed_privatizationEvar1"}
! %1 = fir.alloca i32 {bindc_name = "var2", uniq_name = "_QFdelayed_privatizationEvar2"}
! %c111_i32 = arith.constant 111 : i32
! fir.store %c111_i32 to %0 : !fir.ref<i32>
! %c222_i32 = arith.constant 222 : i32
! fir.store %c222_i32 to %1 : !fir.ref<i32>
! omp.parallel private(@var1.privatizer %0, @var2.privatizer %1 : !fir.ref<i32>, !fir.ref<i32>) {
! %2 = fir.load %0 : !fir.ref<i32>
! %3 = fir.load %1 : !fir.ref<i32>
! %4 = arith.addi %2, %3 : i32
! %c2_i32 = arith.constant 2 : i32
! %5 = arith.addi %4, %c2_i32 : i32
! fir.store %5 to %0 : !fir.ref<i32>
! omp.terminator
! }
! return
! }
! return
! "omp.private"() <{function_type = (!fir.ref<i32>) -> !fir.ref<i32>, sym_name = "var1.privatizer"}> ({
! ^bb0(%arg0: !fir.ref<i32>):
! %0 = fir.alloca i32 {bindc_name = "var1", pinned, uniq_name = "_QFdelayed_privatizationEvar1"}
! %1 = fir.load %arg0 : !fir.ref<i32>
! fir.store %1 to %0 : !fir.ref<i32>
! omp.yield(%0 : !fir.ref<i32>)
! }) : () -> ()
! "omp.private"() <{function_type = (!fir.ref<i32>) -> !fir.ref<i32>, sym_name = "var2.privatizer"}> ({
! ^bb0(%arg0: !fir.ref<i32>):
! %0 = fir.alloca i32 {bindc_name = "var2", pinned, uniq_name = "_QFdelayed_privatizationEvar2"}
! %1 = fir.load %arg0 : !fir.ref<i32>
! fir.store %1 to %0 : !fir.ref<i32>
! omp.yield(%0 : !fir.ref<i32>)
! }) : () -> ()
!}
!
!"omp.private"() <{function_type = (!fir.ref<i32>) -> !fir.ref<i32>, sym_name = "var1.privatizer"}> ({
!^bb0(%arg0: !fir.ref<i32>):
! %0 = fir.alloca i32 {bindc_name = "var1", pinned, uniq_name = "_QFdelayed_privatizationEvar1"}
! %1 = fir.load %arg0 : !fir.ref<i32>
! fir.store %1 to %0 : !fir.ref<i32>
! omp.yield(%0 : !fir.ref<i32>)
!}) : () -> ()
! -----------------------------
! ### Conversion to LLVM + OMP:
! -----------------------------
!module {
! llvm.func @_QPdelayed_privatization() {
! %0 = llvm.mlir.constant(1 : i64) : i64
! %1 = llvm.alloca %0 x i32 {bindc_name = "var1"} : (i64) -> !llvm.ptr
! %2 = llvm.mlir.constant(1 : i64) : i64
! %3 = llvm.alloca %2 x i32 {bindc_name = "var2"} : (i64) -> !llvm.ptr
! %4 = llvm.mlir.constant(111 : i32) : i32
! llvm.store %4, %1 : i32, !llvm.ptr
! %5 = llvm.mlir.constant(222 : i32) : i32
! llvm.store %5, %3 : i32, !llvm.ptr
! omp.parallel private(@var1.privatizer %1, @var2.privatizer %3 : !llvm.ptr, !llvm.ptr) {
! %6 = llvm.load %1 : !llvm.ptr -> i32
! %7 = llvm.load %3 : !llvm.ptr -> i32
! %8 = llvm.add %6, %7 : i32
! %9 = llvm.mlir.constant(2 : i32) : i32
! %10 = llvm.add %8, %9 : i32
! llvm.store %10, %1 : i32, !llvm.ptr
! omp.terminator
! }
! llvm.return
! }
! "omp.private"() <{function_type = (!llvm.ptr) -> !llvm.ptr, sym_name = "var1.privatizer"}> ({
! ^bb0(%arg0: !llvm.ptr):
! %0 = llvm.mlir.constant(1 : i64) : i64
! %1 = llvm.alloca %0 x i32 {bindc_name = "var1", pinned} : (i64) -> !llvm.ptr
! %2 = llvm.load %arg0 : !llvm.ptr -> i32
! llvm.store %2, %1 : i32, !llvm.ptr
! omp.yield(%1 : !llvm.ptr)
! }) : () -> ()
! "omp.private"() <{function_type = (!llvm.ptr) -> !llvm.ptr, sym_name = "var2.privatizer"}> ({
! ^bb0(%arg0: !llvm.ptr):
! %0 = llvm.mlir.constant(1 : i64) : i64
! %1 = llvm.alloca %0 x i32 {bindc_name = "var2", pinned} : (i64) -> !llvm.ptr
! %2 = llvm.load %arg0 : !llvm.ptr -> i32
! llvm.store %2, %1 : i32, !llvm.ptr
! omp.yield(%1 : !llvm.ptr)
! }) : () -> ()
!}
!
!"omp.private"() <{function_type = (!fir.ref<i32>) -> !fir.ref<i32>, sym_name = "var2.privatizer"}> ({
!^bb0(%arg0: !fir.ref<i32>):
! %0 = fir.alloca i32 {bindc_name = "var2", pinned, uniq_name = "_QFdelayed_privatizationEvar2"}
! %1 = fir.load %arg0 : !fir.ref<i32>
! fir.store %1 to %0 : !fir.ref<i32>
! omp.yield(%0 : !fir.ref<i32>)
!}) : () -> ()
! --------------------------
! ### Conversion to LLVM IR:
! --------------------------
!%struct.ident_t = type { i32, i32, i32, i32, ptr }

!@0 = private unnamed_addr constant [23 x i8] c";unknown;unknown;0;0;;\00", align 1
!@1 = private unnamed_addr constant %struct.ident_t { i32 0, i32 2, i32 0, i32 22, ptr @0 }, align 8

!define void @_QPdelayed_privatization() {
! %structArg = alloca { ptr, ptr }, align 8
! %1 = alloca i32, i64 1, align 4
! %2 = alloca i32, i64 1, align 4
! store i32 111, ptr %1, align 4
! store i32 222, ptr %2, align 4
! br label %entry

!entry: ; preds = %0
! %omp_global_thread_num = call i32 @__kmpc_global_thread_num(ptr @1)
! br label %omp_parallel

!omp_parallel: ; preds = %entry
! %gep_ = getelementptr { ptr, ptr }, ptr %structArg, i32 0, i32 0
! store ptr %1, ptr %gep_, align 8
! %gep_2 = getelementptr { ptr, ptr }, ptr %structArg, i32 0, i32 1
! store ptr %2, ptr %gep_2, align 8
! call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @1, i32 1, ptr @_QPdelayed_privatization..omp_par, ptr %structArg)
! br label %omp.par.outlined.exit

!omp.par.outlined.exit: ; preds = %omp_parallel
! br label %omp.par.exit.split

!omp.par.exit.split: ; preds = %omp.par.outlined.exit
! ret void
!}

!; Function Attrs: nounwind
!define internal void @_QPdelayed_privatization..omp_par(ptr noalias %tid.addr, ptr noalias %zero.addr, ptr %0) #0 {
!omp.par.entry:
! %gep_ = getelementptr { ptr, ptr }, ptr %0, i32 0, i32 0
! %loadgep_ = load ptr, ptr %gep_, align 8
! %gep_1 = getelementptr { ptr, ptr }, ptr %0, i32 0, i32 1
! %loadgep_2 = load ptr, ptr %gep_1, align 8
! %tid.addr.local = alloca i32, align 4
! %1 = load i32, ptr %tid.addr, align 4
! store i32 %1, ptr %tid.addr.local, align 4
! %tid = load i32, ptr %tid.addr.local, align 4
! %2 = alloca i32, i64 1, align 4
! %3 = load i32, ptr %loadgep_, align 4
! store i32 %3, ptr %2, align 4
! %4 = alloca i32, i64 1, align 4
! %5 = load i32, ptr %loadgep_2, align 4
! store i32 %5, ptr %4, align 4
! br label %omp.par.region

!omp.par.region: ; preds = %omp.par.entry
! br label %omp.par.region1

!omp.par.region1: ; preds = %omp.par.region
! %6 = load i32, ptr %2, align 4
! %7 = load i32, ptr %4, align 4
! %8 = add i32 %6, %7
! %9 = add i32 %8, 2
! store i32 %9, ptr %2, align 4
! br label %omp.region.cont

!omp.region.cont: ; preds = %omp.par.region1
! br label %omp.par.pre_finalize

!omp.par.pre_finalize: ; preds = %omp.region.cont
! br label %omp.par.outlined.exit.exitStub

!omp.par.outlined.exit.exitStub: ; preds = %omp.par.pre_finalize
! ret void
!}

!; Function Attrs: nounwind
!declare i32 @__kmpc_global_thread_num(ptr) #0

!; Function Attrs: nounwind
!declare !callback !2 void @__kmpc_fork_call(ptr, i32, ptr, ...) #0
Original file line number Diff line number Diff line change
Expand Up @@ -1092,6 +1092,75 @@ convertOmpParallel(omp::ParallelOp opInst, llvm::IRBuilderBase &builder,
llvm::Value *&replacementValue) -> InsertPointTy {
replacementValue = &vPtr;

// If this is a private value, this lambda will return the corresponding
// mlir value and its `PrivateClauseOp`. Otherwise, empty values are
// returned.
auto [privVar,
privInit] = [&]() -> std::pair<mlir::Value, omp::PrivateClauseOp> {
if (!opInst.getPrivateVars().empty()) {
auto privVars = opInst.getPrivateVars();
auto privInits = opInst.getPrivateInits();
assert(privInits && privInits->size() == privVars.size());

const auto *privInitIt = privInits->begin();
for (auto privVarIt = privVars.begin(); privVarIt != privVars.end();
++privVarIt, ++privInitIt) {
auto *llvmPrivVarOp = moduleTranslation.lookupValue(*privVarIt);
if (llvmPrivVarOp != &vPtr) {
continue;
}

auto privSym = llvm::cast<SymbolRefAttr>(*privInitIt);
auto privOp =
SymbolTable::lookupNearestSymbolFrom<omp::PrivateClauseOp>(
opInst, privSym);

return {*privVarIt, privOp};
}
}

return {mlir::Value(), omp::PrivateClauseOp()};
}();

if (privVar) {

// Replace the privatizer block argument with mlir value being privatized.
// This way, the body of the privatizer will be changed from using the
// region/block argument to the value being privatized.
assert(privInit->getRegions().front().getNumArguments() == 1);

auto arg = privInit->getRegions().front().getArgument(0);
for (auto &op : privInit->getRegions().front().front()) {
op.replaceUsesOfWith(arg, privVar);
}

auto oldIP = builder.saveIP();
builder.restoreIP(allocaIP);

// Temporarily unlink the terminator from its parent since
// `inlineConvertOmpRegions` expects the insertion block to **not**
// contain a terminator.
auto &allocaTerminator = builder.GetInsertBlock()->back();
assert(lastInstr.isTerminator());
allocaTerminator.removeFromParent();

SmallVector<llvm::Value *, 1> yieldedValues;
if (failed(inlineConvertOmpRegions(privInit->getRegion(0),
"omp.privatizer", builder,
moduleTranslation, &yieldedValues))) {
// TODO proper error-handling.
builder.restoreIP(oldIP);
return codeGenIP;
}

allocaTerminator.insertAfter(&builder.GetInsertBlock()->back());

assert(yieldedValues.size() == 1);
replacementValue = yieldedValues.front();

builder.restoreIP(oldIP);
}

return codeGenIP;
};

Expand Down Expand Up @@ -2774,12 +2843,13 @@ LogicalResult OpenMPDialectLLVMIRTranslationInterface::convertOperation(
.Case([&](omp::TargetOp) {
return convertOmpTarget(*op, builder, moduleTranslation);
})
.Case<omp::MapInfoOp, omp::DataBoundsOp>([&](auto op) {
// No-op, should be handled by relevant owning operations e.g.
// TargetOp, EnterDataOp, ExitDataOp, DataOp etc. and then
// discarded
return success();
})
.Case<omp::MapInfoOp, omp::DataBoundsOp, omp::PrivateClauseOp>(
[&](auto op) {
// No-op, should be handled by relevant owning operations e.g.
// TargetOp, EnterDataOp, ExitDataOp, DataOp etc. and then
// discarded
return success();
})
.Default([&](Operation *inst) {
return inst->emitError("unsupported OpenMP operation: ")
<< inst->getName();
Expand Down

0 comments on commit 0d05d4a

Please sign in to comment.