Skip to content

Commit

Permalink
[llvm][mlir][OMPIRBuilder] Translate omp.single's copyprivate (#80488)
Browse files Browse the repository at this point in the history
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: #73128
  • Loading branch information
luporl committed Feb 28, 2024
1 parent 3fac056 commit 64422cf
Show file tree
Hide file tree
Showing 6 changed files with 330 additions and 12 deletions.
97 changes: 97 additions & 0 deletions flang/test/Integration/OpenMP/copyprivate.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
!===----------------------------------------------------------------------===!
! This directory can be used to add Integration tests involving multiple
! stages of the compiler (for eg. from Fortran to LLVM IR). It should not
! contain executable tests. We should only add tests here sparingly and only
! if there is no other way to test. Repeat this message in each test that is
! added to this directory and sub-directories.
!===----------------------------------------------------------------------===!

!RUN: %flang_fc1 -emit-llvm -fopenmp %s -o - | FileCheck %s

!CHECK-DAG: define void @_copy_box_Uxi32(ptr %{{.*}}, ptr %{{.*}})
!CHECK-DAG: define void @_copy_10xi32(ptr %{{.*}}, ptr %{{.*}})
!CHECK-DAG: define void @_copy_i64(ptr %{{.*}}, ptr %{{.*}})
!CHECK-DAG: define void @_copy_box_Uxi64(ptr %{{.*}}, ptr %{{.*}})
!CHECK-DAG: define void @_copy_f32(ptr %{{.*}}, ptr %{{.*}})
!CHECK-DAG: define void @_copy_2x3xf32(ptr %{{.*}}, ptr %{{.*}})
!CHECK-DAG: define void @_copy_z32(ptr %{{.*}}, ptr %{{.*}})
!CHECK-DAG: define void @_copy_10xz32(ptr %{{.*}}, ptr %{{.*}})
!CHECK-DAG: define void @_copy_l32(ptr %{{.*}}, ptr %{{.*}})
!CHECK-DAG: define void @_copy_5xl32(ptr %{{.*}}, ptr %{{.*}})
!CHECK-DAG: define void @_copy_c8x8(ptr %{{.*}}, ptr %{{.*}})
!CHECK-DAG: define void @_copy_10xc8x8(ptr %{{.*}}, ptr %{{.*}})
!CHECK-DAG: define void @_copy_c16x5(ptr %{{.*}}, ptr %{{.*}})
!CHECK-DAG: define void @_copy_rec__QFtest_typesTdt(ptr %{{.*}}, ptr %{{.*}})
!CHECK-DAG: define void @_copy_box_heap_Uxi32(ptr %{{.*}}, ptr %{{.*}})
!CHECK-DAG: define void @_copy_box_ptr_Uxc8x9(ptr %{{.*}}, ptr %{{.*}})

!CHECK-LABEL: define void @_copy_i32(
!CHECK-SAME: ptr %[[DST:.*]], ptr %[[SRC:.*]]) {
!CHECK-NEXT: %[[SRC_VAL:.*]] = load i32, ptr %[[SRC]]
!CHECK-NEXT: store i32 %[[SRC_VAL]], ptr %[[DST]]
!CHECK-NEXT: ret void
!CHECK-NEXT: }

!CHECK-LABEL: define internal void @test_scalar_..omp_par({{.*}})
!CHECK: %[[I:.*]] = alloca i32, i64 1
!CHECK: %[[J:.*]] = alloca i32, i64 1
!CHECK: %[[DID_IT:.*]] = alloca i32
!CHECK: store i32 0, ptr %[[DID_IT]]
!CHECK: %[[THREAD_NUM1:.*]] = call i32 @__kmpc_global_thread_num(ptr @[[LOC:.*]])
!CHECK: %[[RET:.*]] = call i32 @__kmpc_single({{.*}})
!CHECK: %[[NOT_ZERO:.*]] = icmp ne i32 %[[RET]], 0
!CHECK: br i1 %[[NOT_ZERO]], label %[[OMP_REGION_BODY:.*]], label %[[OMP_REGION_END:.*]]

!CHECK: [[OMP_REGION_END]]:
!CHECK: %[[THREAD_NUM2:.*]] = call i32 @__kmpc_global_thread_num(ptr @[[LOC:.*]])
!CHECK: %[[DID_IT_VAL:.*]] = load i32, ptr %[[DID_IT]]
!CHECK: call void @__kmpc_copyprivate(ptr @[[LOC]], i32 %[[THREAD_NUM2]], i64 0, ptr %[[I]], ptr @_copy_i32, i32 %[[DID_IT_VAL]])
!CHECK: %[[THREAD_NUM3:.*]] = call i32 @__kmpc_global_thread_num(ptr @[[LOC]])
!CHECK: %[[DID_IT_VAL2:.*]] = load i32, ptr %[[DID_IT]]
!CHECK: call void @__kmpc_copyprivate(ptr @[[LOC]], i32 %[[THREAD_NUM3]], i64 0, ptr %[[J]], ptr @_copy_i32, i32 %[[DID_IT_VAL2]])

!CHECK: [[OMP_REGION_BODY]]:
!CHECK: br label %[[OMP_SINGLE_REGION:.*]]
!CHECK: [[OMP_SINGLE_REGION]]:
!CHECK: store i32 11, ptr %[[I]]
!CHECK: store i32 22, ptr %[[J]]
!CHECK: br label %[[OMP_REGION_CONT3:.*]]
!CHECK: [[OMP_REGION_CONT3:.*]]:
!CHECK: store i32 1, ptr %[[DID_IT]]
!CHECK: call void @__kmpc_end_single(ptr @[[LOC]], i32 %[[THREAD_NUM1]])
!CHECK: br label %[[OMP_REGION_END]]
subroutine test_scalar()
integer :: i, j

!$omp parallel private(i, j)
!$omp single
i = 11
j = 22
!$omp end single copyprivate(i, j)
!$omp end parallel
end subroutine

subroutine test_types(a, n)
integer :: a(:), n
integer(4) :: i4, i4a(10)
integer(8) :: i8, i8a(n)
real :: r, ra(2, 3)
complex :: z, za(10)
logical :: l, la(5)
character(kind=1, len=8) :: c1, c1a(10)
character(kind=2, len=5) :: c2

type dt
integer :: i
real :: r
end type
type(dt) :: t

integer, allocatable :: aloc(:)
character(kind=1, len=9), pointer :: ptr(:)

!$omp parallel private(a, i4, i4a, i8, i8a, r, ra, z, za, l, la, c1, c1a, c2, t, aloc, ptr)
!$omp single
!$omp end single copyprivate(a, i4, i4a, i8, i8a, r, ra, z, za, l, la, c1, c1a, c2, t, aloc, ptr)
!$omp end parallel
end subroutine
6 changes: 4 additions & 2 deletions llvm/include/llvm/Frontend/OpenMP/OMPIRBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -1834,13 +1834,15 @@ class OpenMPIRBuilder {
/// \param BodyGenCB Callback that will generate the region code.
/// \param FiniCB Callback to finalize variable copies.
/// \param IsNowait If false, a barrier is emitted.
/// \param DidIt Local variable used as a flag to indicate 'single' thread
/// \param CPVars copyprivate variables.
/// \param CPFuncs copy functions to use for each copyprivate variable.
///
/// \returns The insertion position *after* the single call.
InsertPointTy createSingle(const LocationDescription &Loc,
BodyGenCallbackTy BodyGenCB,
FinalizeCallbackTy FiniCB, bool IsNowait,
llvm::Value *DidIt);
ArrayRef<llvm::Value *> CPVars = {},
ArrayRef<llvm::Function *> CPFuncs = {});

/// Generator for '#omp master'
///
Expand Down
33 changes: 28 additions & 5 deletions llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4047,13 +4047,17 @@ OpenMPIRBuilder::createCopyPrivate(const LocationDescription &Loc,

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

if (!updateToLocation(Loc))
return Loc.IP;

// If needed (i.e. not null), initialize `DidIt` with 0
if (DidIt) {
// If needed allocate and initialize `DidIt` with 0.
// DidIt: flag variable: 1=single thread; 0=not single thread.
llvm::Value *DidIt = nullptr;
if (!CPVars.empty()) {
DidIt = Builder.CreateAlloca(llvm::Type::getInt32Ty(Builder.getContext()));
Builder.CreateStore(Builder.getInt32(0), DidIt);
}

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

auto FiniCBWrapper = [&](InsertPointTy IP) {
FiniCB(IP);

// The thread that executes the single region must set `DidIt` to 1.
// This is used by __kmpc_copyprivate, to know if the caller is the
// single thread or not.
if (DidIt)
Builder.CreateStore(Builder.getInt32(1), DidIt);
};

// generates the following:
// if (__kmpc_single()) {
// .... single region ...
// __kmpc_end_single
// }
// __kmpc_copyprivate
// __kmpc_barrier

EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB,
EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCBWrapper,
/*Conditional*/ true,
/*hasFinalize*/ true);
if (!IsNowait)

if (DidIt) {
for (size_t I = 0, E = CPVars.size(); I < E; ++I)
// NOTE BufSize is currently unused, so just pass 0.
createCopyPrivate(LocationDescription(Builder.saveIP(), Loc.DL),
/*BufSize=*/ConstantInt::get(Int64, 0), CPVars[I],
CPFuncs[I], DidIt);
// NOTE __kmpc_copyprivate already inserts a barrier
} else if (!IsNowait)
createBarrier(LocationDescription(Builder.saveIP(), Loc.DL),
omp::Directive::OMPD_unknown, /* ForceSimpleCall */ false,
/* CheckCancelFlag */ false);
Expand Down
153 changes: 149 additions & 4 deletions llvm/unittests/Frontend/OpenMPIRBuilderTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3327,8 +3327,8 @@ TEST_F(OpenMPIRBuilderTest, SingleDirective) {
EXPECT_NE(IPBB->end(), IP.getPoint());
};

Builder.restoreIP(OMPBuilder.createSingle(
Builder, BodyGenCB, FiniCB, /*IsNowait*/ false, /*DidIt*/ nullptr));
Builder.restoreIP(
OMPBuilder.createSingle(Builder, BodyGenCB, FiniCB, /*IsNowait*/ false));
Value *EntryBBTI = EntryBB->getTerminator();
EXPECT_NE(EntryBBTI, nullptr);
EXPECT_TRUE(isa<BranchInst>(EntryBBTI));
Expand Down Expand Up @@ -3417,8 +3417,8 @@ TEST_F(OpenMPIRBuilderTest, SingleDirectiveNowait) {
EXPECT_NE(IPBB->end(), IP.getPoint());
};

Builder.restoreIP(OMPBuilder.createSingle(
Builder, BodyGenCB, FiniCB, /*IsNowait*/ true, /*DidIt*/ nullptr));
Builder.restoreIP(
OMPBuilder.createSingle(Builder, BodyGenCB, FiniCB, /*IsNowait*/ true));
Value *EntryBBTI = EntryBB->getTerminator();
EXPECT_NE(EntryBBTI, nullptr);
EXPECT_TRUE(isa<BranchInst>(EntryBBTI));
Expand Down Expand Up @@ -3464,6 +3464,151 @@ TEST_F(OpenMPIRBuilderTest, SingleDirectiveNowait) {
EXPECT_EQ(ExitBarrier, nullptr);
}

// Helper class to check each instruction of a BB.
class BBInstIter {
BasicBlock *BB;
BasicBlock::iterator BBI;

public:
BBInstIter(BasicBlock *BB) : BB(BB), BBI(BB->begin()) {}

bool hasNext() const { return BBI != BB->end(); }

template <typename InstTy> InstTy *next() {
if (!hasNext())
return nullptr;
Instruction *Cur = &*BBI++;
if (!isa<InstTy>(Cur))
return nullptr;
return cast<InstTy>(Cur);
}
};

TEST_F(OpenMPIRBuilderTest, SingleDirectiveCopyPrivate) {
using InsertPointTy = OpenMPIRBuilder::InsertPointTy;
OpenMPIRBuilder OMPBuilder(*M);
OMPBuilder.initialize();
F->setName("func");
IRBuilder<> Builder(BB);

OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP(), DL});

AllocaInst *PrivAI = nullptr;

BasicBlock *EntryBB = nullptr;
BasicBlock *ThenBB = nullptr;

Value *CPVar = Builder.CreateAlloca(F->arg_begin()->getType());
Builder.CreateStore(F->arg_begin(), CPVar);

FunctionType *CopyFuncTy = FunctionType::get(
Builder.getVoidTy(), {Builder.getPtrTy(), Builder.getPtrTy()}, false);
Function *CopyFunc =
Function::Create(CopyFuncTy, Function::PrivateLinkage, "copy_var", *M);

auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP) {
if (AllocaIP.isSet())
Builder.restoreIP(AllocaIP);
else
Builder.SetInsertPoint(&*(F->getEntryBlock().getFirstInsertionPt()));
PrivAI = Builder.CreateAlloca(F->arg_begin()->getType());
Builder.CreateStore(F->arg_begin(), PrivAI);

llvm::BasicBlock *CodeGenIPBB = CodeGenIP.getBlock();
llvm::Instruction *CodeGenIPInst = &*CodeGenIP.getPoint();
EXPECT_EQ(CodeGenIPBB->getTerminator(), CodeGenIPInst);

Builder.restoreIP(CodeGenIP);

// collect some info for checks later
ThenBB = Builder.GetInsertBlock();
EntryBB = ThenBB->getUniquePredecessor();

// simple instructions for body
Value *PrivLoad =
Builder.CreateLoad(PrivAI->getAllocatedType(), PrivAI, "local.use");
Builder.CreateICmpNE(F->arg_begin(), PrivLoad);
};

auto FiniCB = [&](InsertPointTy IP) {
BasicBlock *IPBB = IP.getBlock();
// IP must be before the unconditional branch to ExitBB
EXPECT_NE(IPBB->end(), IP.getPoint());
};

Builder.restoreIP(OMPBuilder.createSingle(Builder, BodyGenCB, FiniCB,
/*IsNowait*/ false, {CPVar},
{CopyFunc}));
Value *EntryBBTI = EntryBB->getTerminator();
EXPECT_NE(EntryBBTI, nullptr);
EXPECT_TRUE(isa<BranchInst>(EntryBBTI));
BranchInst *EntryBr = cast<BranchInst>(EntryBB->getTerminator());
EXPECT_TRUE(EntryBr->isConditional());
EXPECT_EQ(EntryBr->getSuccessor(0), ThenBB);
BasicBlock *ExitBB = ThenBB->getUniqueSuccessor();
EXPECT_EQ(EntryBr->getSuccessor(1), ExitBB);

CmpInst *CondInst = cast<CmpInst>(EntryBr->getCondition());
EXPECT_TRUE(isa<CallInst>(CondInst->getOperand(0)));

CallInst *SingleEntryCI = cast<CallInst>(CondInst->getOperand(0));
EXPECT_EQ(SingleEntryCI->arg_size(), 2U);
EXPECT_EQ(SingleEntryCI->getCalledFunction()->getName(), "__kmpc_single");
EXPECT_TRUE(isa<GlobalVariable>(SingleEntryCI->getArgOperand(0)));

// check ThenBB
BBInstIter ThenBBI(ThenBB);
// load PrivAI
auto *PrivLI = ThenBBI.next<LoadInst>();
EXPECT_NE(PrivLI, nullptr);
EXPECT_EQ(PrivLI->getPointerOperand(), PrivAI);
// icmp
EXPECT_TRUE(ThenBBI.next<ICmpInst>());
// store 1, DidIt
auto *DidItSI = ThenBBI.next<StoreInst>();
EXPECT_NE(DidItSI, nullptr);
EXPECT_EQ(DidItSI->getValueOperand(),
ConstantInt::get(Type::getInt32Ty(Ctx), 1));
Value *DidIt = DidItSI->getPointerOperand();
// call __kmpc_end_single
auto *SingleEndCI = ThenBBI.next<CallInst>();
EXPECT_NE(SingleEndCI, nullptr);
EXPECT_EQ(SingleEndCI->getCalledFunction()->getName(), "__kmpc_end_single");
EXPECT_EQ(SingleEndCI->arg_size(), 2U);
EXPECT_TRUE(isa<GlobalVariable>(SingleEndCI->getArgOperand(0)));
EXPECT_EQ(SingleEndCI->getArgOperand(1), SingleEntryCI->getArgOperand(1));
// br ExitBB
auto *ExitBBBI = ThenBBI.next<BranchInst>();
EXPECT_NE(ExitBBBI, nullptr);
EXPECT_TRUE(ExitBBBI->isUnconditional());
EXPECT_EQ(ExitBBBI->getOperand(0), ExitBB);
EXPECT_FALSE(ThenBBI.hasNext());

// check ExitBB
BBInstIter ExitBBI(ExitBB);
// call __kmpc_global_thread_num
auto *ThreadNumCI = ExitBBI.next<CallInst>();
EXPECT_NE(ThreadNumCI, nullptr);
EXPECT_EQ(ThreadNumCI->getCalledFunction()->getName(),
"__kmpc_global_thread_num");
// load DidIt
auto *DidItLI = ExitBBI.next<LoadInst>();
EXPECT_NE(DidItLI, nullptr);
EXPECT_EQ(DidItLI->getPointerOperand(), DidIt);
// call __kmpc_copyprivate
auto *CopyPrivateCI = ExitBBI.next<CallInst>();
EXPECT_NE(CopyPrivateCI, nullptr);
EXPECT_EQ(CopyPrivateCI->arg_size(), 6U);
EXPECT_TRUE(isa<AllocaInst>(CopyPrivateCI->getArgOperand(3)));
EXPECT_EQ(CopyPrivateCI->getArgOperand(3), CPVar);
EXPECT_TRUE(isa<Function>(CopyPrivateCI->getArgOperand(4)));
EXPECT_EQ(CopyPrivateCI->getArgOperand(4), CopyFunc);
EXPECT_TRUE(isa<LoadInst>(CopyPrivateCI->getArgOperand(5)));
DidItLI = cast<LoadInst>(CopyPrivateCI->getArgOperand(5));
EXPECT_EQ(DidItLI->getOperand(0), DidIt);
EXPECT_FALSE(ExitBBI.hasNext());
}

TEST_F(OpenMPIRBuilderTest, OMPAtomicReadFlt) {
OpenMPIRBuilder OMPBuilder(*M);
OMPBuilder.initialize();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -667,8 +667,22 @@ convertOmpSingle(omp::SingleOp &singleOp, llvm::IRBuilderBase &builder,
moduleTranslation, bodyGenStatus);
};
auto finiCB = [&](InsertPointTy codeGenIP) {};

// Handle copyprivate
Operation::operand_range cpVars = singleOp.getCopyprivateVars();
std::optional<ArrayAttr> cpFuncs = singleOp.getCopyprivateFuncs();
llvm::SmallVector<llvm::Value *> llvmCPVars;
llvm::SmallVector<llvm::Function *> llvmCPFuncs;
for (size_t i = 0, e = cpVars.size(); i < e; ++i) {
llvmCPVars.push_back(moduleTranslation.lookupValue(cpVars[i]));
auto llvmFuncOp = SymbolTable::lookupNearestSymbolFrom<LLVM::LLVMFuncOp>(
singleOp, cast<SymbolRefAttr>((*cpFuncs)[i]));
llvmCPFuncs.push_back(
moduleTranslation.lookupFunction(llvmFuncOp.getName()));
}

builder.restoreIP(moduleTranslation.getOpenMPBuilder()->createSingle(
ompLoc, bodyCB, finiCB, singleOp.getNowait(), /*DidIt=*/nullptr));
ompLoc, bodyCB, finiCB, singleOp.getNowait(), llvmCPVars, llvmCPFuncs));
return bodyGenStatus;
}

Expand Down
Loading

0 comments on commit 64422cf

Please sign in to comment.