Skip to content

Commit 2cb27e2

Browse files
luporlmylai-mtk
authored andcommitted
[llvm][mlir][OMPIRBuilder] Translate omp.single's copyprivate (llvm#80488)
Use the new copyprivate list from omp.single to emit calls to __kmpc_copyprivate, during the creation of the single operation in OMPIRBuilder. This is patch 4 of 4, to add support for COPYPRIVATE in Flang. Original PR: llvm#73128
1 parent a903a86 commit 2cb27e2

File tree

6 files changed

+330
-12
lines changed

6 files changed

+330
-12
lines changed
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
!===----------------------------------------------------------------------===!
2+
! This directory can be used to add Integration tests involving multiple
3+
! stages of the compiler (for eg. from Fortran to LLVM IR). It should not
4+
! contain executable tests. We should only add tests here sparingly and only
5+
! if there is no other way to test. Repeat this message in each test that is
6+
! added to this directory and sub-directories.
7+
!===----------------------------------------------------------------------===!
8+
9+
!RUN: %flang_fc1 -emit-llvm -fopenmp %s -o - | FileCheck %s
10+
11+
!CHECK-DAG: define void @_copy_box_Uxi32(ptr %{{.*}}, ptr %{{.*}})
12+
!CHECK-DAG: define void @_copy_10xi32(ptr %{{.*}}, ptr %{{.*}})
13+
!CHECK-DAG: define void @_copy_i64(ptr %{{.*}}, ptr %{{.*}})
14+
!CHECK-DAG: define void @_copy_box_Uxi64(ptr %{{.*}}, ptr %{{.*}})
15+
!CHECK-DAG: define void @_copy_f32(ptr %{{.*}}, ptr %{{.*}})
16+
!CHECK-DAG: define void @_copy_2x3xf32(ptr %{{.*}}, ptr %{{.*}})
17+
!CHECK-DAG: define void @_copy_z32(ptr %{{.*}}, ptr %{{.*}})
18+
!CHECK-DAG: define void @_copy_10xz32(ptr %{{.*}}, ptr %{{.*}})
19+
!CHECK-DAG: define void @_copy_l32(ptr %{{.*}}, ptr %{{.*}})
20+
!CHECK-DAG: define void @_copy_5xl32(ptr %{{.*}}, ptr %{{.*}})
21+
!CHECK-DAG: define void @_copy_c8x8(ptr %{{.*}}, ptr %{{.*}})
22+
!CHECK-DAG: define void @_copy_10xc8x8(ptr %{{.*}}, ptr %{{.*}})
23+
!CHECK-DAG: define void @_copy_c16x5(ptr %{{.*}}, ptr %{{.*}})
24+
!CHECK-DAG: define void @_copy_rec__QFtest_typesTdt(ptr %{{.*}}, ptr %{{.*}})
25+
!CHECK-DAG: define void @_copy_box_heap_Uxi32(ptr %{{.*}}, ptr %{{.*}})
26+
!CHECK-DAG: define void @_copy_box_ptr_Uxc8x9(ptr %{{.*}}, ptr %{{.*}})
27+
28+
!CHECK-LABEL: define void @_copy_i32(
29+
!CHECK-SAME: ptr %[[DST:.*]], ptr %[[SRC:.*]]) {
30+
!CHECK-NEXT: %[[SRC_VAL:.*]] = load i32, ptr %[[SRC]]
31+
!CHECK-NEXT: store i32 %[[SRC_VAL]], ptr %[[DST]]
32+
!CHECK-NEXT: ret void
33+
!CHECK-NEXT: }
34+
35+
!CHECK-LABEL: define internal void @test_scalar_..omp_par({{.*}})
36+
!CHECK: %[[I:.*]] = alloca i32, i64 1
37+
!CHECK: %[[J:.*]] = alloca i32, i64 1
38+
!CHECK: %[[DID_IT:.*]] = alloca i32
39+
!CHECK: store i32 0, ptr %[[DID_IT]]
40+
!CHECK: %[[THREAD_NUM1:.*]] = call i32 @__kmpc_global_thread_num(ptr @[[LOC:.*]])
41+
!CHECK: %[[RET:.*]] = call i32 @__kmpc_single({{.*}})
42+
!CHECK: %[[NOT_ZERO:.*]] = icmp ne i32 %[[RET]], 0
43+
!CHECK: br i1 %[[NOT_ZERO]], label %[[OMP_REGION_BODY:.*]], label %[[OMP_REGION_END:.*]]
44+
45+
!CHECK: [[OMP_REGION_END]]:
46+
!CHECK: %[[THREAD_NUM2:.*]] = call i32 @__kmpc_global_thread_num(ptr @[[LOC:.*]])
47+
!CHECK: %[[DID_IT_VAL:.*]] = load i32, ptr %[[DID_IT]]
48+
!CHECK: call void @__kmpc_copyprivate(ptr @[[LOC]], i32 %[[THREAD_NUM2]], i64 0, ptr %[[I]], ptr @_copy_i32, i32 %[[DID_IT_VAL]])
49+
!CHECK: %[[THREAD_NUM3:.*]] = call i32 @__kmpc_global_thread_num(ptr @[[LOC]])
50+
!CHECK: %[[DID_IT_VAL2:.*]] = load i32, ptr %[[DID_IT]]
51+
!CHECK: call void @__kmpc_copyprivate(ptr @[[LOC]], i32 %[[THREAD_NUM3]], i64 0, ptr %[[J]], ptr @_copy_i32, i32 %[[DID_IT_VAL2]])
52+
53+
!CHECK: [[OMP_REGION_BODY]]:
54+
!CHECK: br label %[[OMP_SINGLE_REGION:.*]]
55+
!CHECK: [[OMP_SINGLE_REGION]]:
56+
!CHECK: store i32 11, ptr %[[I]]
57+
!CHECK: store i32 22, ptr %[[J]]
58+
!CHECK: br label %[[OMP_REGION_CONT3:.*]]
59+
!CHECK: [[OMP_REGION_CONT3:.*]]:
60+
!CHECK: store i32 1, ptr %[[DID_IT]]
61+
!CHECK: call void @__kmpc_end_single(ptr @[[LOC]], i32 %[[THREAD_NUM1]])
62+
!CHECK: br label %[[OMP_REGION_END]]
63+
subroutine test_scalar()
64+
integer :: i, j
65+
66+
!$omp parallel private(i, j)
67+
!$omp single
68+
i = 11
69+
j = 22
70+
!$omp end single copyprivate(i, j)
71+
!$omp end parallel
72+
end subroutine
73+
74+
subroutine test_types(a, n)
75+
integer :: a(:), n
76+
integer(4) :: i4, i4a(10)
77+
integer(8) :: i8, i8a(n)
78+
real :: r, ra(2, 3)
79+
complex :: z, za(10)
80+
logical :: l, la(5)
81+
character(kind=1, len=8) :: c1, c1a(10)
82+
character(kind=2, len=5) :: c2
83+
84+
type dt
85+
integer :: i
86+
real :: r
87+
end type
88+
type(dt) :: t
89+
90+
integer, allocatable :: aloc(:)
91+
character(kind=1, len=9), pointer :: ptr(:)
92+
93+
!$omp parallel private(a, i4, i4a, i8, i8a, r, ra, z, za, l, la, c1, c1a, c2, t, aloc, ptr)
94+
!$omp single
95+
!$omp end single copyprivate(a, i4, i4a, i8, i8a, r, ra, z, za, l, la, c1, c1a, c2, t, aloc, ptr)
96+
!$omp end parallel
97+
end subroutine

llvm/include/llvm/Frontend/OpenMP/OMPIRBuilder.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1834,13 +1834,15 @@ class OpenMPIRBuilder {
18341834
/// \param BodyGenCB Callback that will generate the region code.
18351835
/// \param FiniCB Callback to finalize variable copies.
18361836
/// \param IsNowait If false, a barrier is emitted.
1837-
/// \param DidIt Local variable used as a flag to indicate 'single' thread
1837+
/// \param CPVars copyprivate variables.
1838+
/// \param CPFuncs copy functions to use for each copyprivate variable.
18381839
///
18391840
/// \returns The insertion position *after* the single call.
18401841
InsertPointTy createSingle(const LocationDescription &Loc,
18411842
BodyGenCallbackTy BodyGenCB,
18421843
FinalizeCallbackTy FiniCB, bool IsNowait,
1843-
llvm::Value *DidIt);
1844+
ArrayRef<llvm::Value *> CPVars = {},
1845+
ArrayRef<llvm::Function *> CPFuncs = {});
18441846

18451847
/// Generator for '#omp master'
18461848
///

llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4047,13 +4047,17 @@ OpenMPIRBuilder::createCopyPrivate(const LocationDescription &Loc,
40474047

40484048
OpenMPIRBuilder::InsertPointTy OpenMPIRBuilder::createSingle(
40494049
const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB,
4050-
FinalizeCallbackTy FiniCB, bool IsNowait, llvm::Value *DidIt) {
4050+
FinalizeCallbackTy FiniCB, bool IsNowait, ArrayRef<llvm::Value *> CPVars,
4051+
ArrayRef<llvm::Function *> CPFuncs) {
40514052

40524053
if (!updateToLocation(Loc))
40534054
return Loc.IP;
40544055

4055-
// If needed (i.e. not null), initialize `DidIt` with 0
4056-
if (DidIt) {
4056+
// If needed allocate and initialize `DidIt` with 0.
4057+
// DidIt: flag variable: 1=single thread; 0=not single thread.
4058+
llvm::Value *DidIt = nullptr;
4059+
if (!CPVars.empty()) {
4060+
DidIt = Builder.CreateAlloca(llvm::Type::getInt32Ty(Builder.getContext()));
40574061
Builder.CreateStore(Builder.getInt32(0), DidIt);
40584062
}
40594063

@@ -4070,17 +4074,36 @@ OpenMPIRBuilder::InsertPointTy OpenMPIRBuilder::createSingle(
40704074
Function *ExitRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_single);
40714075
Instruction *ExitCall = Builder.CreateCall(ExitRTLFn, Args);
40724076

4077+
auto FiniCBWrapper = [&](InsertPointTy IP) {
4078+
FiniCB(IP);
4079+
4080+
// The thread that executes the single region must set `DidIt` to 1.
4081+
// This is used by __kmpc_copyprivate, to know if the caller is the
4082+
// single thread or not.
4083+
if (DidIt)
4084+
Builder.CreateStore(Builder.getInt32(1), DidIt);
4085+
};
4086+
40734087
// generates the following:
40744088
// if (__kmpc_single()) {
40754089
// .... single region ...
40764090
// __kmpc_end_single
40774091
// }
4092+
// __kmpc_copyprivate
40784093
// __kmpc_barrier
40794094

4080-
EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB,
4095+
EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCBWrapper,
40814096
/*Conditional*/ true,
40824097
/*hasFinalize*/ true);
4083-
if (!IsNowait)
4098+
4099+
if (DidIt) {
4100+
for (size_t I = 0, E = CPVars.size(); I < E; ++I)
4101+
// NOTE BufSize is currently unused, so just pass 0.
4102+
createCopyPrivate(LocationDescription(Builder.saveIP(), Loc.DL),
4103+
/*BufSize=*/ConstantInt::get(Int64, 0), CPVars[I],
4104+
CPFuncs[I], DidIt);
4105+
// NOTE __kmpc_copyprivate already inserts a barrier
4106+
} else if (!IsNowait)
40844107
createBarrier(LocationDescription(Builder.saveIP(), Loc.DL),
40854108
omp::Directive::OMPD_unknown, /* ForceSimpleCall */ false,
40864109
/* CheckCancelFlag */ false);

llvm/unittests/Frontend/OpenMPIRBuilderTest.cpp

Lines changed: 149 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3327,8 +3327,8 @@ TEST_F(OpenMPIRBuilderTest, SingleDirective) {
33273327
EXPECT_NE(IPBB->end(), IP.getPoint());
33283328
};
33293329

3330-
Builder.restoreIP(OMPBuilder.createSingle(
3331-
Builder, BodyGenCB, FiniCB, /*IsNowait*/ false, /*DidIt*/ nullptr));
3330+
Builder.restoreIP(
3331+
OMPBuilder.createSingle(Builder, BodyGenCB, FiniCB, /*IsNowait*/ false));
33323332
Value *EntryBBTI = EntryBB->getTerminator();
33333333
EXPECT_NE(EntryBBTI, nullptr);
33343334
EXPECT_TRUE(isa<BranchInst>(EntryBBTI));
@@ -3417,8 +3417,8 @@ TEST_F(OpenMPIRBuilderTest, SingleDirectiveNowait) {
34173417
EXPECT_NE(IPBB->end(), IP.getPoint());
34183418
};
34193419

3420-
Builder.restoreIP(OMPBuilder.createSingle(
3421-
Builder, BodyGenCB, FiniCB, /*IsNowait*/ true, /*DidIt*/ nullptr));
3420+
Builder.restoreIP(
3421+
OMPBuilder.createSingle(Builder, BodyGenCB, FiniCB, /*IsNowait*/ true));
34223422
Value *EntryBBTI = EntryBB->getTerminator();
34233423
EXPECT_NE(EntryBBTI, nullptr);
34243424
EXPECT_TRUE(isa<BranchInst>(EntryBBTI));
@@ -3464,6 +3464,151 @@ TEST_F(OpenMPIRBuilderTest, SingleDirectiveNowait) {
34643464
EXPECT_EQ(ExitBarrier, nullptr);
34653465
}
34663466

3467+
// Helper class to check each instruction of a BB.
3468+
class BBInstIter {
3469+
BasicBlock *BB;
3470+
BasicBlock::iterator BBI;
3471+
3472+
public:
3473+
BBInstIter(BasicBlock *BB) : BB(BB), BBI(BB->begin()) {}
3474+
3475+
bool hasNext() const { return BBI != BB->end(); }
3476+
3477+
template <typename InstTy> InstTy *next() {
3478+
if (!hasNext())
3479+
return nullptr;
3480+
Instruction *Cur = &*BBI++;
3481+
if (!isa<InstTy>(Cur))
3482+
return nullptr;
3483+
return cast<InstTy>(Cur);
3484+
}
3485+
};
3486+
3487+
TEST_F(OpenMPIRBuilderTest, SingleDirectiveCopyPrivate) {
3488+
using InsertPointTy = OpenMPIRBuilder::InsertPointTy;
3489+
OpenMPIRBuilder OMPBuilder(*M);
3490+
OMPBuilder.initialize();
3491+
F->setName("func");
3492+
IRBuilder<> Builder(BB);
3493+
3494+
OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP(), DL});
3495+
3496+
AllocaInst *PrivAI = nullptr;
3497+
3498+
BasicBlock *EntryBB = nullptr;
3499+
BasicBlock *ThenBB = nullptr;
3500+
3501+
Value *CPVar = Builder.CreateAlloca(F->arg_begin()->getType());
3502+
Builder.CreateStore(F->arg_begin(), CPVar);
3503+
3504+
FunctionType *CopyFuncTy = FunctionType::get(
3505+
Builder.getVoidTy(), {Builder.getPtrTy(), Builder.getPtrTy()}, false);
3506+
Function *CopyFunc =
3507+
Function::Create(CopyFuncTy, Function::PrivateLinkage, "copy_var", *M);
3508+
3509+
auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP) {
3510+
if (AllocaIP.isSet())
3511+
Builder.restoreIP(AllocaIP);
3512+
else
3513+
Builder.SetInsertPoint(&*(F->getEntryBlock().getFirstInsertionPt()));
3514+
PrivAI = Builder.CreateAlloca(F->arg_begin()->getType());
3515+
Builder.CreateStore(F->arg_begin(), PrivAI);
3516+
3517+
llvm::BasicBlock *CodeGenIPBB = CodeGenIP.getBlock();
3518+
llvm::Instruction *CodeGenIPInst = &*CodeGenIP.getPoint();
3519+
EXPECT_EQ(CodeGenIPBB->getTerminator(), CodeGenIPInst);
3520+
3521+
Builder.restoreIP(CodeGenIP);
3522+
3523+
// collect some info for checks later
3524+
ThenBB = Builder.GetInsertBlock();
3525+
EntryBB = ThenBB->getUniquePredecessor();
3526+
3527+
// simple instructions for body
3528+
Value *PrivLoad =
3529+
Builder.CreateLoad(PrivAI->getAllocatedType(), PrivAI, "local.use");
3530+
Builder.CreateICmpNE(F->arg_begin(), PrivLoad);
3531+
};
3532+
3533+
auto FiniCB = [&](InsertPointTy IP) {
3534+
BasicBlock *IPBB = IP.getBlock();
3535+
// IP must be before the unconditional branch to ExitBB
3536+
EXPECT_NE(IPBB->end(), IP.getPoint());
3537+
};
3538+
3539+
Builder.restoreIP(OMPBuilder.createSingle(Builder, BodyGenCB, FiniCB,
3540+
/*IsNowait*/ false, {CPVar},
3541+
{CopyFunc}));
3542+
Value *EntryBBTI = EntryBB->getTerminator();
3543+
EXPECT_NE(EntryBBTI, nullptr);
3544+
EXPECT_TRUE(isa<BranchInst>(EntryBBTI));
3545+
BranchInst *EntryBr = cast<BranchInst>(EntryBB->getTerminator());
3546+
EXPECT_TRUE(EntryBr->isConditional());
3547+
EXPECT_EQ(EntryBr->getSuccessor(0), ThenBB);
3548+
BasicBlock *ExitBB = ThenBB->getUniqueSuccessor();
3549+
EXPECT_EQ(EntryBr->getSuccessor(1), ExitBB);
3550+
3551+
CmpInst *CondInst = cast<CmpInst>(EntryBr->getCondition());
3552+
EXPECT_TRUE(isa<CallInst>(CondInst->getOperand(0)));
3553+
3554+
CallInst *SingleEntryCI = cast<CallInst>(CondInst->getOperand(0));
3555+
EXPECT_EQ(SingleEntryCI->arg_size(), 2U);
3556+
EXPECT_EQ(SingleEntryCI->getCalledFunction()->getName(), "__kmpc_single");
3557+
EXPECT_TRUE(isa<GlobalVariable>(SingleEntryCI->getArgOperand(0)));
3558+
3559+
// check ThenBB
3560+
BBInstIter ThenBBI(ThenBB);
3561+
// load PrivAI
3562+
auto *PrivLI = ThenBBI.next<LoadInst>();
3563+
EXPECT_NE(PrivLI, nullptr);
3564+
EXPECT_EQ(PrivLI->getPointerOperand(), PrivAI);
3565+
// icmp
3566+
EXPECT_TRUE(ThenBBI.next<ICmpInst>());
3567+
// store 1, DidIt
3568+
auto *DidItSI = ThenBBI.next<StoreInst>();
3569+
EXPECT_NE(DidItSI, nullptr);
3570+
EXPECT_EQ(DidItSI->getValueOperand(),
3571+
ConstantInt::get(Type::getInt32Ty(Ctx), 1));
3572+
Value *DidIt = DidItSI->getPointerOperand();
3573+
// call __kmpc_end_single
3574+
auto *SingleEndCI = ThenBBI.next<CallInst>();
3575+
EXPECT_NE(SingleEndCI, nullptr);
3576+
EXPECT_EQ(SingleEndCI->getCalledFunction()->getName(), "__kmpc_end_single");
3577+
EXPECT_EQ(SingleEndCI->arg_size(), 2U);
3578+
EXPECT_TRUE(isa<GlobalVariable>(SingleEndCI->getArgOperand(0)));
3579+
EXPECT_EQ(SingleEndCI->getArgOperand(1), SingleEntryCI->getArgOperand(1));
3580+
// br ExitBB
3581+
auto *ExitBBBI = ThenBBI.next<BranchInst>();
3582+
EXPECT_NE(ExitBBBI, nullptr);
3583+
EXPECT_TRUE(ExitBBBI->isUnconditional());
3584+
EXPECT_EQ(ExitBBBI->getOperand(0), ExitBB);
3585+
EXPECT_FALSE(ThenBBI.hasNext());
3586+
3587+
// check ExitBB
3588+
BBInstIter ExitBBI(ExitBB);
3589+
// call __kmpc_global_thread_num
3590+
auto *ThreadNumCI = ExitBBI.next<CallInst>();
3591+
EXPECT_NE(ThreadNumCI, nullptr);
3592+
EXPECT_EQ(ThreadNumCI->getCalledFunction()->getName(),
3593+
"__kmpc_global_thread_num");
3594+
// load DidIt
3595+
auto *DidItLI = ExitBBI.next<LoadInst>();
3596+
EXPECT_NE(DidItLI, nullptr);
3597+
EXPECT_EQ(DidItLI->getPointerOperand(), DidIt);
3598+
// call __kmpc_copyprivate
3599+
auto *CopyPrivateCI = ExitBBI.next<CallInst>();
3600+
EXPECT_NE(CopyPrivateCI, nullptr);
3601+
EXPECT_EQ(CopyPrivateCI->arg_size(), 6U);
3602+
EXPECT_TRUE(isa<AllocaInst>(CopyPrivateCI->getArgOperand(3)));
3603+
EXPECT_EQ(CopyPrivateCI->getArgOperand(3), CPVar);
3604+
EXPECT_TRUE(isa<Function>(CopyPrivateCI->getArgOperand(4)));
3605+
EXPECT_EQ(CopyPrivateCI->getArgOperand(4), CopyFunc);
3606+
EXPECT_TRUE(isa<LoadInst>(CopyPrivateCI->getArgOperand(5)));
3607+
DidItLI = cast<LoadInst>(CopyPrivateCI->getArgOperand(5));
3608+
EXPECT_EQ(DidItLI->getOperand(0), DidIt);
3609+
EXPECT_FALSE(ExitBBI.hasNext());
3610+
}
3611+
34673612
TEST_F(OpenMPIRBuilderTest, OMPAtomicReadFlt) {
34683613
OpenMPIRBuilder OMPBuilder(*M);
34693614
OMPBuilder.initialize();

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

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -667,8 +667,22 @@ convertOmpSingle(omp::SingleOp &singleOp, llvm::IRBuilderBase &builder,
667667
moduleTranslation, bodyGenStatus);
668668
};
669669
auto finiCB = [&](InsertPointTy codeGenIP) {};
670+
671+
// Handle copyprivate
672+
Operation::operand_range cpVars = singleOp.getCopyprivateVars();
673+
std::optional<ArrayAttr> cpFuncs = singleOp.getCopyprivateFuncs();
674+
llvm::SmallVector<llvm::Value *> llvmCPVars;
675+
llvm::SmallVector<llvm::Function *> llvmCPFuncs;
676+
for (size_t i = 0, e = cpVars.size(); i < e; ++i) {
677+
llvmCPVars.push_back(moduleTranslation.lookupValue(cpVars[i]));
678+
auto llvmFuncOp = SymbolTable::lookupNearestSymbolFrom<LLVM::LLVMFuncOp>(
679+
singleOp, cast<SymbolRefAttr>((*cpFuncs)[i]));
680+
llvmCPFuncs.push_back(
681+
moduleTranslation.lookupFunction(llvmFuncOp.getName()));
682+
}
683+
670684
builder.restoreIP(moduleTranslation.getOpenMPBuilder()->createSingle(
671-
ompLoc, bodyCB, finiCB, singleOp.getNowait(), /*DidIt=*/nullptr));
685+
ompLoc, bodyCB, finiCB, singleOp.getNowait(), llvmCPVars, llvmCPFuncs));
672686
return bodyGenStatus;
673687
}
674688

0 commit comments

Comments
 (0)