[OPENMP] Codegen for 'copyprivate' clause ('single' directive).
If there is at least one 'copyprivate' clause is associated with the single directive, the following code is generated: ``` i32 did_it = 0; \\ for 'copyprivate' clause if(__kmpc_single(ident_t *, gtid)) { SingleOpGen(); __kmpc_end_single(ident_t *, gtid); did_it = 1; \\ for 'copyprivate' clause } <copyprivate_list>[0] = &var0; ... <copyprivate_list>[n] = &varn; call __kmpc_copyprivate(ident_t *, gtid, <copyprivate_list_size>, <copyprivate_list>, <copy_func>, did_it); ... void<copy_func>(void *LHSArg, void *RHSArg) { Dst = (void * [n])(LHSArg); Src = (void * [n])(RHSArg); Dst[0] = Src[0]; ... Dst[n] = Src[n]; } ``` All list items from all 'copyprivate' clauses are gathered into single <copyprivate list> (<copyprivate_list_size> is a size in bytes of this list) and <copy_func> is used to propagate values of private or threadprivate variables from the 'single' region to other implicit threads from outer 'parallel' region. Differential Revision: http://reviews.llvm.org/D8410 llvm-svn: 232932
This commit is contained in:
parent
1565992679
commit
a63048e4fd
|
@ -2562,6 +2562,15 @@ template <typename Derived>
|
|||
bool RecursiveASTVisitor<Derived>::VisitOMPCopyprivateClause(
|
||||
OMPCopyprivateClause *C) {
|
||||
TRY_TO(VisitOMPClauseList(C));
|
||||
for (auto *E : C->source_exprs()) {
|
||||
TRY_TO(TraverseStmt(E));
|
||||
}
|
||||
for (auto *E : C->destination_exprs()) {
|
||||
TRY_TO(TraverseStmt(E));
|
||||
}
|
||||
for (auto *E : C->assignment_ops()) {
|
||||
TRY_TO(TraverseStmt(E));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -1674,6 +1674,7 @@ public:
|
|||
/// with the variables 'a' and 'b'.
|
||||
///
|
||||
class OMPCopyprivateClause : public OMPVarListClause<OMPCopyprivateClause> {
|
||||
friend class OMPClauseReader;
|
||||
/// \brief Build clause with number of variables \a N.
|
||||
///
|
||||
/// \param StartLoc Starting location of the clause.
|
||||
|
@ -1695,6 +1696,46 @@ class OMPCopyprivateClause : public OMPVarListClause<OMPCopyprivateClause> {
|
|||
OMPC_copyprivate, SourceLocation(), SourceLocation(),
|
||||
SourceLocation(), N) {}
|
||||
|
||||
/// \brief Set list of helper expressions, required for proper codegen of the
|
||||
/// clause. These expressions represent source expression in the final
|
||||
/// assignment statement performed by the copyprivate clause.
|
||||
void setSourceExprs(ArrayRef<Expr *> SrcExprs);
|
||||
|
||||
/// \brief Get the list of helper source expressions.
|
||||
MutableArrayRef<Expr *> getSourceExprs() {
|
||||
return MutableArrayRef<Expr *>(varlist_end(), varlist_size());
|
||||
}
|
||||
ArrayRef<const Expr *> getSourceExprs() const {
|
||||
return llvm::makeArrayRef(varlist_end(), varlist_size());
|
||||
}
|
||||
|
||||
/// \brief Set list of helper expressions, required for proper codegen of the
|
||||
/// clause. These expressions represent destination expression in the final
|
||||
/// assignment statement performed by the copyprivate clause.
|
||||
void setDestinationExprs(ArrayRef<Expr *> DstExprs);
|
||||
|
||||
/// \brief Get the list of helper destination expressions.
|
||||
MutableArrayRef<Expr *> getDestinationExprs() {
|
||||
return MutableArrayRef<Expr *>(getSourceExprs().end(), varlist_size());
|
||||
}
|
||||
ArrayRef<const Expr *> getDestinationExprs() const {
|
||||
return llvm::makeArrayRef(getSourceExprs().end(), varlist_size());
|
||||
}
|
||||
|
||||
/// \brief Set list of helper assignment expressions, required for proper
|
||||
/// codegen of the clause. These expressions are assignment expressions that
|
||||
/// assign source helper expressions to destination helper expressions
|
||||
/// correspondingly.
|
||||
void setAssignmentOps(ArrayRef<Expr *> AssignmentOps);
|
||||
|
||||
/// \brief Get the list of helper assignment expressions.
|
||||
MutableArrayRef<Expr *> getAssignmentOps() {
|
||||
return MutableArrayRef<Expr *>(getDestinationExprs().end(), varlist_size());
|
||||
}
|
||||
ArrayRef<const Expr *> getAssignmentOps() const {
|
||||
return llvm::makeArrayRef(getDestinationExprs().end(), varlist_size());
|
||||
}
|
||||
|
||||
public:
|
||||
/// \brief Creates clause with a list of variables \a VL.
|
||||
///
|
||||
|
@ -1703,10 +1744,24 @@ public:
|
|||
/// \param LParenLoc Location of '('.
|
||||
/// \param EndLoc Ending location of the clause.
|
||||
/// \param VL List of references to the variables.
|
||||
/// \param SrcExprs List of helper expressions for proper generation of
|
||||
/// assignment operation required for copyprivate clause. This list represents
|
||||
/// sources.
|
||||
/// \param DstExprs List of helper expressions for proper generation of
|
||||
/// assignment operation required for copyprivate clause. This list represents
|
||||
/// destinations.
|
||||
/// \param AsignmentOps List of helper expressions that represents assignment
|
||||
/// operation:
|
||||
/// \code
|
||||
/// DstExprs = SrcExprs;
|
||||
/// \endcode
|
||||
/// Required for proper codegen of final assignment performed by the
|
||||
/// copyprivate clause.
|
||||
///
|
||||
static OMPCopyprivateClause *
|
||||
Create(const ASTContext &C, SourceLocation StartLoc, SourceLocation LParenLoc,
|
||||
SourceLocation EndLoc, ArrayRef<Expr *> VL);
|
||||
SourceLocation EndLoc, ArrayRef<Expr *> VL, ArrayRef<Expr *> SrcExprs,
|
||||
ArrayRef<Expr *> DstExprs, ArrayRef<Expr *> AssignmentOps);
|
||||
/// \brief Creates an empty clause with \a N variables.
|
||||
///
|
||||
/// \param C AST context.
|
||||
|
@ -1714,6 +1769,36 @@ public:
|
|||
///
|
||||
static OMPCopyprivateClause *CreateEmpty(const ASTContext &C, unsigned N);
|
||||
|
||||
typedef MutableArrayRef<Expr *>::iterator helper_expr_iterator;
|
||||
typedef ArrayRef<const Expr *>::iterator helper_expr_const_iterator;
|
||||
typedef llvm::iterator_range<helper_expr_iterator> helper_expr_range;
|
||||
typedef llvm::iterator_range<helper_expr_const_iterator>
|
||||
helper_expr_const_range;
|
||||
|
||||
helper_expr_const_range source_exprs() const {
|
||||
return helper_expr_const_range(getSourceExprs().begin(),
|
||||
getSourceExprs().end());
|
||||
}
|
||||
helper_expr_range source_exprs() {
|
||||
return helper_expr_range(getSourceExprs().begin(), getSourceExprs().end());
|
||||
}
|
||||
helper_expr_const_range destination_exprs() const {
|
||||
return helper_expr_const_range(getDestinationExprs().begin(),
|
||||
getDestinationExprs().end());
|
||||
}
|
||||
helper_expr_range destination_exprs() {
|
||||
return helper_expr_range(getDestinationExprs().begin(),
|
||||
getDestinationExprs().end());
|
||||
}
|
||||
helper_expr_const_range assignment_ops() const {
|
||||
return helper_expr_const_range(getAssignmentOps().begin(),
|
||||
getAssignmentOps().end());
|
||||
}
|
||||
helper_expr_range assignment_ops() {
|
||||
return helper_expr_range(getAssignmentOps().begin(),
|
||||
getAssignmentOps().end());
|
||||
}
|
||||
|
||||
StmtRange children() {
|
||||
return StmtRange(reinterpret_cast<Stmt **>(varlist_begin()),
|
||||
reinterpret_cast<Stmt **>(varlist_end()));
|
||||
|
|
|
@ -2592,6 +2592,15 @@ template <typename Derived>
|
|||
bool RecursiveASTVisitor<Derived>::VisitOMPCopyprivateClause(
|
||||
OMPCopyprivateClause *C) {
|
||||
TRY_TO(VisitOMPClauseList(C));
|
||||
for (auto *E : C->source_exprs()) {
|
||||
TRY_TO(TraverseStmt(E));
|
||||
}
|
||||
for (auto *E : C->destination_exprs()) {
|
||||
TRY_TO(TraverseStmt(E));
|
||||
}
|
||||
for (auto *E : C->assignment_ops()) {
|
||||
TRY_TO(TraverseStmt(E));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -1388,17 +1388,41 @@ OMPCopyinClause *OMPCopyinClause::CreateEmpty(const ASTContext &C,
|
|||
return new (Mem) OMPCopyinClause(N);
|
||||
}
|
||||
|
||||
OMPCopyprivateClause *OMPCopyprivateClause::Create(const ASTContext &C,
|
||||
SourceLocation StartLoc,
|
||||
SourceLocation LParenLoc,
|
||||
SourceLocation EndLoc,
|
||||
ArrayRef<Expr *> VL) {
|
||||
void OMPCopyprivateClause::setSourceExprs(ArrayRef<Expr *> SrcExprs) {
|
||||
assert(SrcExprs.size() == varlist_size() && "Number of source expressions is "
|
||||
"not the same as the "
|
||||
"preallocated buffer");
|
||||
std::copy(SrcExprs.begin(), SrcExprs.end(), varlist_end());
|
||||
}
|
||||
|
||||
void OMPCopyprivateClause::setDestinationExprs(ArrayRef<Expr *> DstExprs) {
|
||||
assert(DstExprs.size() == varlist_size() && "Number of destination "
|
||||
"expressions is not the same as "
|
||||
"the preallocated buffer");
|
||||
std::copy(DstExprs.begin(), DstExprs.end(), getSourceExprs().end());
|
||||
}
|
||||
|
||||
void OMPCopyprivateClause::setAssignmentOps(ArrayRef<Expr *> AssignmentOps) {
|
||||
assert(AssignmentOps.size() == varlist_size() &&
|
||||
"Number of assignment expressions is not the same as the preallocated "
|
||||
"buffer");
|
||||
std::copy(AssignmentOps.begin(), AssignmentOps.end(),
|
||||
getDestinationExprs().end());
|
||||
}
|
||||
|
||||
OMPCopyprivateClause *OMPCopyprivateClause::Create(
|
||||
const ASTContext &C, SourceLocation StartLoc, SourceLocation LParenLoc,
|
||||
SourceLocation EndLoc, ArrayRef<Expr *> VL, ArrayRef<Expr *> SrcExprs,
|
||||
ArrayRef<Expr *> DstExprs, ArrayRef<Expr *> AssignmentOps) {
|
||||
void *Mem = C.Allocate(llvm::RoundUpToAlignment(sizeof(OMPCopyprivateClause),
|
||||
llvm::alignOf<Expr *>()) +
|
||||
sizeof(Expr *) * VL.size());
|
||||
4 * sizeof(Expr *) * VL.size());
|
||||
OMPCopyprivateClause *Clause =
|
||||
new (Mem) OMPCopyprivateClause(StartLoc, LParenLoc, EndLoc, VL.size());
|
||||
Clause->setVarRefs(VL);
|
||||
Clause->setSourceExprs(SrcExprs);
|
||||
Clause->setDestinationExprs(DstExprs);
|
||||
Clause->setAssignmentOps(AssignmentOps);
|
||||
return Clause;
|
||||
}
|
||||
|
||||
|
@ -1406,7 +1430,7 @@ OMPCopyprivateClause *OMPCopyprivateClause::CreateEmpty(const ASTContext &C,
|
|||
unsigned N) {
|
||||
void *Mem = C.Allocate(llvm::RoundUpToAlignment(sizeof(OMPCopyprivateClause),
|
||||
llvm::alignOf<Expr *>()) +
|
||||
sizeof(Expr *) * N);
|
||||
4 * sizeof(Expr *) * N);
|
||||
return new (Mem) OMPCopyprivateClause(N);
|
||||
}
|
||||
|
||||
|
|
|
@ -381,6 +381,15 @@ void OMPClauseProfiler::VisitOMPCopyinClause(const OMPCopyinClause *C) {
|
|||
void
|
||||
OMPClauseProfiler::VisitOMPCopyprivateClause(const OMPCopyprivateClause *C) {
|
||||
VisitOMPClauseList(C);
|
||||
for (auto *E : C->source_exprs()) {
|
||||
Profiler->VisitStmt(E);
|
||||
}
|
||||
for (auto *E : C->destination_exprs()) {
|
||||
Profiler->VisitStmt(E);
|
||||
}
|
||||
for (auto *E : C->assignment_ops()) {
|
||||
Profiler->VisitStmt(E);
|
||||
}
|
||||
}
|
||||
void OMPClauseProfiler::VisitOMPFlushClause(const OMPFlushClause *C) {
|
||||
VisitOMPClauseList(C);
|
||||
|
|
|
@ -580,6 +580,21 @@ CGOpenMPRuntime::createRuntimeFunction(OpenMPRTLFunction Function) {
|
|||
RTLFn = CGM.CreateRuntimeFunction(FnTy, /*Name=*/"__kmpc_omp_task");
|
||||
break;
|
||||
}
|
||||
case OMPRTL__kmpc_copyprivate: {
|
||||
// Build void __kmpc_copyprivate(ident_t *loc, kmp_int32 global_tid,
|
||||
// kmp_int32 cpy_size, void *cpy_data, void(*cpy_func)(void *, void *),
|
||||
// kmp_int32 didit);
|
||||
llvm::Type *CpyTypeParams[] = {CGM.VoidPtrTy, CGM.VoidPtrTy};
|
||||
auto *CpyFnTy =
|
||||
llvm::FunctionType::get(CGM.VoidTy, CpyTypeParams, /*isVarArg=*/false);
|
||||
llvm::Type *TypeParams[] = {getIdentTyPointerTy(), CGM.Int32Ty, CGM.Int32Ty,
|
||||
CGM.VoidPtrTy, CpyFnTy->getPointerTo(),
|
||||
CGM.Int32Ty};
|
||||
llvm::FunctionType *FnTy =
|
||||
llvm::FunctionType::get(CGM.VoidTy, TypeParams, /*isVarArg=*/false);
|
||||
RTLFn = CGM.CreateRuntimeFunction(FnTy, /*Name=*/"__kmpc_copyprivate");
|
||||
break;
|
||||
}
|
||||
}
|
||||
return RTLFn;
|
||||
}
|
||||
|
@ -965,19 +980,107 @@ void CGOpenMPRuntime::emitTaskyieldCall(CodeGenFunction &CGF,
|
|||
CGF.EmitRuntimeCall(createRuntimeFunction(OMPRTL__kmpc_omp_taskyield), Args);
|
||||
}
|
||||
|
||||
static llvm::Value *emitCopyprivateCopyFunction(
|
||||
CodeGenModule &CGM, llvm::Type *ArgsType, ArrayRef<const Expr *> SrcExprs,
|
||||
ArrayRef<const Expr *> DstExprs, ArrayRef<const Expr *> AssignmentOps) {
|
||||
auto &C = CGM.getContext();
|
||||
// void copy_func(void *LHSArg, void *RHSArg);
|
||||
FunctionArgList Args;
|
||||
ImplicitParamDecl LHSArg(C, /*DC=*/nullptr, SourceLocation(), /*Id=*/nullptr,
|
||||
C.VoidPtrTy);
|
||||
ImplicitParamDecl RHSArg(C, /*DC=*/nullptr, SourceLocation(), /*Id=*/nullptr,
|
||||
C.VoidPtrTy);
|
||||
Args.push_back(&LHSArg);
|
||||
Args.push_back(&RHSArg);
|
||||
FunctionType::ExtInfo EI;
|
||||
auto &CGFI = CGM.getTypes().arrangeFreeFunctionDeclaration(
|
||||
C.VoidTy, Args, EI, /*isVariadic=*/false);
|
||||
auto *Fn = llvm::Function::Create(
|
||||
CGM.getTypes().GetFunctionType(CGFI), llvm::GlobalValue::InternalLinkage,
|
||||
".omp.copyprivate.copy_func", &CGM.getModule());
|
||||
CGM.SetLLVMFunctionAttributes(/*D=*/nullptr, CGFI, Fn);
|
||||
CodeGenFunction CGF(CGM);
|
||||
CGF.StartFunction(GlobalDecl(), C.VoidTy, Fn, CGFI, Args);
|
||||
// Dst = (void*[n])(LHSArg);
|
||||
// Src = (void*[n])(RHSArg);
|
||||
auto *LHS = CGF.Builder.CreatePointerBitCastOrAddrSpaceCast(
|
||||
CGF.Builder.CreateAlignedLoad(CGF.GetAddrOfLocalVar(&LHSArg),
|
||||
CGF.PointerAlignInBytes),
|
||||
ArgsType);
|
||||
auto *RHS = CGF.Builder.CreatePointerBitCastOrAddrSpaceCast(
|
||||
CGF.Builder.CreateAlignedLoad(CGF.GetAddrOfLocalVar(&RHSArg),
|
||||
CGF.PointerAlignInBytes),
|
||||
ArgsType);
|
||||
// *(Type0*)Dst[0] = *(Type0*)Src[0];
|
||||
// *(Type1*)Dst[1] = *(Type1*)Src[1];
|
||||
// ...
|
||||
// *(Typen*)Dst[n] = *(Typen*)Src[n];
|
||||
CodeGenFunction::OMPPrivateScope Scope(CGF);
|
||||
for (unsigned I = 0, E = AssignmentOps.size(); I < E; ++I) {
|
||||
Scope.addPrivate(
|
||||
cast<VarDecl>(cast<DeclRefExpr>(SrcExprs[I])->getDecl()),
|
||||
[&]() -> llvm::Value *{
|
||||
return CGF.Builder.CreatePointerBitCastOrAddrSpaceCast(
|
||||
CGF.Builder.CreateAlignedLoad(CGF.Builder.CreateStructGEP(RHS, I),
|
||||
CGM.PointerAlignInBytes),
|
||||
CGF.ConvertTypeForMem(C.getPointerType(SrcExprs[I]->getType())));
|
||||
});
|
||||
Scope.addPrivate(
|
||||
cast<VarDecl>(cast<DeclRefExpr>(DstExprs[I])->getDecl()),
|
||||
[&]() -> llvm::Value *{
|
||||
return CGF.Builder.CreatePointerBitCastOrAddrSpaceCast(
|
||||
CGF.Builder.CreateAlignedLoad(CGF.Builder.CreateStructGEP(LHS, I),
|
||||
CGM.PointerAlignInBytes),
|
||||
CGF.ConvertTypeForMem(C.getPointerType(SrcExprs[I]->getType())));
|
||||
});
|
||||
}
|
||||
Scope.Privatize();
|
||||
for (auto *E : AssignmentOps) {
|
||||
CGF.EmitIgnoredExpr(E);
|
||||
}
|
||||
Scope.ForceCleanup();
|
||||
CGF.FinishFunction();
|
||||
return Fn;
|
||||
}
|
||||
|
||||
void CGOpenMPRuntime::emitSingleRegion(CodeGenFunction &CGF,
|
||||
const std::function<void()> &SingleOpGen,
|
||||
SourceLocation Loc) {
|
||||
SourceLocation Loc,
|
||||
ArrayRef<const Expr *> CopyprivateVars,
|
||||
ArrayRef<const Expr *> SrcExprs,
|
||||
ArrayRef<const Expr *> DstExprs,
|
||||
ArrayRef<const Expr *> AssignmentOps) {
|
||||
assert(CopyprivateVars.size() == SrcExprs.size() &&
|
||||
CopyprivateVars.size() == DstExprs.size() &&
|
||||
CopyprivateVars.size() == AssignmentOps.size());
|
||||
auto &C = CGM.getContext();
|
||||
// int32 did_it = 0;
|
||||
// if(__kmpc_single(ident_t *, gtid)) {
|
||||
// SingleOpGen();
|
||||
// __kmpc_end_single(ident_t *, gtid);
|
||||
// did_it = 1;
|
||||
// }
|
||||
// call __kmpc_copyprivate(ident_t *, gtid, <buf_size>, <copyprivate list>,
|
||||
// <copy_func>, did_it);
|
||||
|
||||
llvm::AllocaInst *DidIt = nullptr;
|
||||
if (!CopyprivateVars.empty()) {
|
||||
// int32 did_it = 0;
|
||||
auto KmpInt32Ty = C.getIntTypeForBitwidth(/*DestWidth=*/32, /*Signed=*/1);
|
||||
DidIt = CGF.CreateMemTemp(KmpInt32Ty, ".omp.copyprivate.did_it");
|
||||
CGF.InitTempAlloca(DidIt, CGF.Builder.getInt32(0));
|
||||
}
|
||||
// Prepare arguments and build a call to __kmpc_single
|
||||
llvm::Value *Args[] = {emitUpdateLocation(CGF, Loc), getThreadID(CGF, Loc)};
|
||||
auto *IsSingle =
|
||||
CGF.EmitRuntimeCall(createRuntimeFunction(OMPRTL__kmpc_single), Args);
|
||||
emitIfStmt(CGF, IsSingle, [&]() -> void {
|
||||
SingleOpGen();
|
||||
if (DidIt) {
|
||||
// did_it = 1;
|
||||
CGF.Builder.CreateAlignedStore(CGF.Builder.getInt32(1), DidIt,
|
||||
DidIt->getAlignment());
|
||||
}
|
||||
// Build a call to __kmpc_end_single.
|
||||
// OpenMP [1.2.2 OpenMP Language Terminology]
|
||||
// For C/C++, an executable statement, possibly compound, with a single
|
||||
|
@ -994,6 +1097,44 @@ void CGOpenMPRuntime::emitSingleRegion(CodeGenFunction &CGF,
|
|||
// fallthrough rather than pushing a normal cleanup for it.
|
||||
CGF.EmitRuntimeCall(createRuntimeFunction(OMPRTL__kmpc_end_single), Args);
|
||||
});
|
||||
// call __kmpc_copyprivate(ident_t *, gtid, <buf_size>, <copyprivate list>,
|
||||
// <copy_func>, did_it);
|
||||
if (DidIt) {
|
||||
llvm::APInt ArraySize(/*unsigned int numBits=*/32, CopyprivateVars.size());
|
||||
auto CopyprivateArrayTy =
|
||||
C.getConstantArrayType(C.VoidPtrTy, ArraySize, ArrayType::Normal,
|
||||
/*IndexTypeQuals=*/0);
|
||||
// Create a list of all private variables for copyprivate.
|
||||
auto *CopyprivateList =
|
||||
CGF.CreateMemTemp(CopyprivateArrayTy, ".omp.copyprivate.cpr_list");
|
||||
for (unsigned I = 0, E = CopyprivateVars.size(); I < E; ++I) {
|
||||
auto *Elem = CGF.Builder.CreateStructGEP(CopyprivateList, I);
|
||||
CGF.Builder.CreateAlignedStore(
|
||||
CGF.Builder.CreatePointerBitCastOrAddrSpaceCast(
|
||||
CGF.EmitLValue(CopyprivateVars[I]).getAddress(), CGF.VoidPtrTy),
|
||||
Elem, CGM.PointerAlignInBytes);
|
||||
}
|
||||
// Build function that copies private values from single region to all other
|
||||
// threads in the corresponding parallel region.
|
||||
auto *CpyFn = emitCopyprivateCopyFunction(
|
||||
CGM, CGF.ConvertTypeForMem(CopyprivateArrayTy)->getPointerTo(),
|
||||
SrcExprs, DstExprs, AssignmentOps);
|
||||
auto *BufSize = CGF.Builder.getInt32(
|
||||
C.getTypeSizeInChars(CopyprivateArrayTy).getQuantity());
|
||||
auto *CL = CGF.Builder.CreatePointerBitCastOrAddrSpaceCast(CopyprivateList,
|
||||
CGF.VoidPtrTy);
|
||||
auto *DidItVal =
|
||||
CGF.Builder.CreateAlignedLoad(DidIt, CGF.PointerAlignInBytes);
|
||||
llvm::Value *Args[] = {
|
||||
emitUpdateLocation(CGF, Loc), // ident_t *<loc>
|
||||
getThreadID(CGF, Loc), // i32 <gtid>
|
||||
BufSize, // i32 <buf_size>
|
||||
CL, // void *<copyprivate list>
|
||||
CpyFn, // void (*) (void *, void *) <copy_func>
|
||||
DidItVal // i32 did_it
|
||||
};
|
||||
CGF.EmitRuntimeCall(createRuntimeFunction(OMPRTL__kmpc_copyprivate), Args);
|
||||
}
|
||||
}
|
||||
|
||||
void CGOpenMPRuntime::emitBarrierCall(CodeGenFunction &CGF, SourceLocation Loc,
|
||||
|
|
|
@ -96,6 +96,10 @@ class CGOpenMPRuntime {
|
|||
// Call to kmp_int32 __kmpc_omp_task(ident_t *, kmp_int32 gtid, kmp_task_t *
|
||||
// new_task);
|
||||
OMPRTL__kmpc_omp_task,
|
||||
// Call to void __kmpc_copyprivate(ident_t *loc, kmp_int32 global_tid,
|
||||
// kmp_int32 cpy_size, void *cpy_data, void(*cpy_func)(void *, void *),
|
||||
// kmp_int32 didit);
|
||||
OMPRTL__kmpc_copyprivate,
|
||||
};
|
||||
|
||||
/// \brief Values for bit flags used in the ident_t to describe the fields.
|
||||
|
@ -348,7 +352,11 @@ public:
|
|||
/// single region.
|
||||
virtual void emitSingleRegion(CodeGenFunction &CGF,
|
||||
const std::function<void()> &SingleOpGen,
|
||||
SourceLocation Loc);
|
||||
SourceLocation Loc,
|
||||
ArrayRef<const Expr *> CopyprivateVars,
|
||||
ArrayRef<const Expr *> SrcExprs,
|
||||
ArrayRef<const Expr *> DstExprs,
|
||||
ArrayRef<const Expr *> AssignmentOps);
|
||||
|
||||
/// \brief Emits explicit barrier for OpenMP threads.
|
||||
/// \param IsExplicit true, if it is explicitly specified barrier.
|
||||
|
|
|
@ -882,7 +882,7 @@ void CodeGenFunction::EmitOMPSectionsDirective(const OMPSectionsDirective &S) {
|
|||
InlinedOpenMPRegionScopeRAII Region(*this, S);
|
||||
EmitStmt(Stmt);
|
||||
EnsureInsertPoint();
|
||||
}, S.getLocStart());
|
||||
}, S.getLocStart(), llvm::None, llvm::None, llvm::None, llvm::None);
|
||||
}
|
||||
|
||||
// Emit an implicit barrier at the end.
|
||||
|
@ -898,11 +898,38 @@ void CodeGenFunction::EmitOMPSectionDirective(const OMPSectionDirective &S) {
|
|||
}
|
||||
|
||||
void CodeGenFunction::EmitOMPSingleDirective(const OMPSingleDirective &S) {
|
||||
llvm::SmallVector<const Expr *, 8> CopyprivateVars;
|
||||
llvm::SmallVector<const Expr *, 8> SrcExprs;
|
||||
llvm::SmallVector<const Expr *, 8> DstExprs;
|
||||
llvm::SmallVector<const Expr *, 8> AssignmentOps;
|
||||
// Check if there are any 'copyprivate' clauses associated with this 'single'
|
||||
// construct.
|
||||
auto CopyprivateFilter = [](const OMPClause *C) -> bool {
|
||||
return C->getClauseKind() == OMPC_copyprivate;
|
||||
};
|
||||
// Build a list of copyprivate variables along with helper expressions
|
||||
// (<source>, <destination>, <destination>=<source> expressions)
|
||||
typedef OMPExecutableDirective::filtered_clause_iterator<decltype(
|
||||
CopyprivateFilter)> CopyprivateIter;
|
||||
for (CopyprivateIter I(S.clauses(), CopyprivateFilter); I; ++I) {
|
||||
auto *C = cast<OMPCopyprivateClause>(*I);
|
||||
CopyprivateVars.append(C->varlists().begin(), C->varlists().end());
|
||||
SrcExprs.append(C->source_exprs().begin(), C->source_exprs().end());
|
||||
DstExprs.append(C->destination_exprs().begin(),
|
||||
C->destination_exprs().end());
|
||||
AssignmentOps.append(C->assignment_ops().begin(),
|
||||
C->assignment_ops().end());
|
||||
}
|
||||
// Emit code for 'single' region along with 'copyprivate' clauses
|
||||
CGM.getOpenMPRuntime().emitSingleRegion(*this, [&]() -> void {
|
||||
InlinedOpenMPRegionScopeRAII Region(*this, S);
|
||||
EmitStmt(cast<CapturedStmt>(S.getAssociatedStmt())->getCapturedStmt());
|
||||
EnsureInsertPoint();
|
||||
}, S.getLocStart());
|
||||
}, S.getLocStart(), CopyprivateVars, SrcExprs, DstExprs, AssignmentOps);
|
||||
// Emit an implicit barrier at the end.
|
||||
if (!S.getSingleClause(OMPC_nowait))
|
||||
CGM.getOpenMPRuntime().emitBarrierCall(*this, S.getLocStart(),
|
||||
/*IsExplicit=*/false);
|
||||
}
|
||||
|
||||
void CodeGenFunction::EmitOMPMasterDirective(const OMPMasterDirective &S) {
|
||||
|
|
|
@ -604,7 +604,10 @@ public:
|
|||
}
|
||||
|
||||
/// \brief Exit scope - all the mapped variables are restored.
|
||||
~OMPPrivateScope() { ForceCleanup(); }
|
||||
~OMPPrivateScope() {
|
||||
if (PerformCleanup)
|
||||
ForceCleanup();
|
||||
}
|
||||
};
|
||||
|
||||
/// \brief Takes the old cleanup stack size and emits the cleanup blocks
|
||||
|
|
|
@ -5614,11 +5614,17 @@ OMPClause *Sema::ActOnOpenMPCopyprivateClause(ArrayRef<Expr *> VarList,
|
|||
SourceLocation LParenLoc,
|
||||
SourceLocation EndLoc) {
|
||||
SmallVector<Expr *, 8> Vars;
|
||||
SmallVector<Expr *, 8> SrcExprs;
|
||||
SmallVector<Expr *, 8> DstExprs;
|
||||
SmallVector<Expr *, 8> AssignmentOps;
|
||||
for (auto &RefExpr : VarList) {
|
||||
assert(RefExpr && "NULL expr in OpenMP copyprivate clause.");
|
||||
if (isa<DependentScopeDeclRefExpr>(RefExpr)) {
|
||||
// It will be analyzed later.
|
||||
Vars.push_back(RefExpr);
|
||||
SrcExprs.push_back(nullptr);
|
||||
DstExprs.push_back(nullptr);
|
||||
AssignmentOps.push_back(nullptr);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -5640,6 +5646,9 @@ OMPClause *Sema::ActOnOpenMPCopyprivateClause(ArrayRef<Expr *> VarList,
|
|||
if (Type->isDependentType() || Type->isInstantiationDependentType()) {
|
||||
// It will be analyzed later.
|
||||
Vars.push_back(DE);
|
||||
SrcExprs.push_back(nullptr);
|
||||
DstExprs.push_back(nullptr);
|
||||
AssignmentOps.push_back(nullptr);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -5648,8 +5657,8 @@ OMPClause *Sema::ActOnOpenMPCopyprivateClause(ArrayRef<Expr *> VarList,
|
|||
// private or firstprivate clause on the single construct.
|
||||
if (!DSAStack->isThreadPrivate(VD)) {
|
||||
auto DVar = DSAStack->getTopDSA(VD, false);
|
||||
if (DVar.CKind != OMPC_copyprivate && DVar.CKind != OMPC_unknown &&
|
||||
!(DVar.CKind == OMPC_private && !DVar.RefExpr)) {
|
||||
if (DVar.CKind != OMPC_unknown && DVar.CKind != OMPC_copyprivate &&
|
||||
DVar.RefExpr) {
|
||||
Diag(ELoc, diag::err_omp_wrong_dsa)
|
||||
<< getOpenMPClauseName(DVar.CKind)
|
||||
<< getOpenMPClauseName(OMPC_copyprivate);
|
||||
|
@ -5676,41 +5685,36 @@ OMPClause *Sema::ActOnOpenMPCopyprivateClause(ArrayRef<Expr *> VarList,
|
|||
// A variable of class type (or array thereof) that appears in a
|
||||
// copyin clause requires an accessible, unambiguous copy assignment
|
||||
// operator for the class type.
|
||||
Type = Context.getBaseElementType(Type);
|
||||
CXXRecordDecl *RD =
|
||||
getLangOpts().CPlusPlus ? Type->getAsCXXRecordDecl() : nullptr;
|
||||
// FIXME This code must be replaced by actual assignment of the
|
||||
// threadprivate variable.
|
||||
if (RD) {
|
||||
CXXMethodDecl *MD = LookupCopyingAssignment(RD, 0, false, 0);
|
||||
DeclAccessPair FoundDecl = DeclAccessPair::make(MD, MD->getAccess());
|
||||
if (MD) {
|
||||
if (CheckMemberAccess(ELoc, RD, FoundDecl) == AR_inaccessible ||
|
||||
MD->isDeleted()) {
|
||||
Diag(ELoc, diag::err_omp_required_method)
|
||||
<< getOpenMPClauseName(OMPC_copyprivate) << 2;
|
||||
bool IsDecl = VD->isThisDeclarationADefinition(Context) ==
|
||||
VarDecl::DeclarationOnly;
|
||||
Diag(VD->getLocation(),
|
||||
IsDecl ? diag::note_previous_decl : diag::note_defined_here)
|
||||
<< VD;
|
||||
Diag(RD->getLocation(), diag::note_previous_decl) << RD;
|
||||
continue;
|
||||
}
|
||||
MarkFunctionReferenced(ELoc, MD);
|
||||
DiagnoseUseOfDecl(MD, ELoc);
|
||||
}
|
||||
}
|
||||
auto *SrcVD = BuildVarDecl(*this, DE->getLocStart(), VD->getType(),
|
||||
".copyprivate.src");
|
||||
auto *PseudoSrcExpr = BuildDeclRefExpr(SrcVD, DE->getType(), VK_LValue,
|
||||
DE->getExprLoc()).get();
|
||||
auto *DstVD = BuildVarDecl(*this, DE->getLocStart(), VD->getType(),
|
||||
".copyprivate.dst");
|
||||
auto *PseudoDstExpr = BuildDeclRefExpr(DstVD, DE->getType(), VK_LValue,
|
||||
DE->getExprLoc()).get();
|
||||
auto AssignmentOp = BuildBinOp(/*S=*/nullptr, DE->getExprLoc(), BO_Assign,
|
||||
PseudoDstExpr, PseudoSrcExpr);
|
||||
if (AssignmentOp.isInvalid())
|
||||
continue;
|
||||
AssignmentOp = ActOnFinishFullExpr(AssignmentOp.get(), DE->getExprLoc(),
|
||||
/*DiscardedValue=*/true);
|
||||
if (AssignmentOp.isInvalid())
|
||||
continue;
|
||||
|
||||
// No need to mark vars as copyprivate, they are already threadprivate or
|
||||
// implicitly private.
|
||||
Vars.push_back(DE);
|
||||
SrcExprs.push_back(PseudoSrcExpr);
|
||||
DstExprs.push_back(PseudoDstExpr);
|
||||
AssignmentOps.push_back(AssignmentOp.get());
|
||||
}
|
||||
|
||||
if (Vars.empty())
|
||||
return nullptr;
|
||||
|
||||
return OMPCopyprivateClause::Create(Context, StartLoc, LParenLoc, EndLoc, Vars);
|
||||
return OMPCopyprivateClause::Create(Context, StartLoc, LParenLoc, EndLoc,
|
||||
Vars, SrcExprs, DstExprs, AssignmentOps);
|
||||
}
|
||||
|
||||
OMPClause *Sema::ActOnOpenMPFlushClause(ArrayRef<Expr *> VarList,
|
||||
|
|
|
@ -1969,11 +1969,23 @@ void OMPClauseReader::VisitOMPCopyinClause(OMPCopyinClause *C) {
|
|||
void OMPClauseReader::VisitOMPCopyprivateClause(OMPCopyprivateClause *C) {
|
||||
C->setLParenLoc(Reader->ReadSourceLocation(Record, Idx));
|
||||
unsigned NumVars = C->varlist_size();
|
||||
SmallVector<Expr *, 16> Vars;
|
||||
Vars.reserve(NumVars);
|
||||
SmallVector<Expr *, 16> Exprs;
|
||||
Exprs.reserve(NumVars);
|
||||
for (unsigned i = 0; i != NumVars; ++i)
|
||||
Vars.push_back(Reader->Reader.ReadSubExpr());
|
||||
C->setVarRefs(Vars);
|
||||
Exprs.push_back(Reader->Reader.ReadSubExpr());
|
||||
C->setVarRefs(Exprs);
|
||||
Exprs.clear();
|
||||
for (unsigned i = 0; i != NumVars; ++i)
|
||||
Exprs.push_back(Reader->Reader.ReadSubExpr());
|
||||
C->setSourceExprs(Exprs);
|
||||
Exprs.clear();
|
||||
for (unsigned i = 0; i != NumVars; ++i)
|
||||
Exprs.push_back(Reader->Reader.ReadSubExpr());
|
||||
C->setDestinationExprs(Exprs);
|
||||
Exprs.clear();
|
||||
for (unsigned i = 0; i != NumVars; ++i)
|
||||
Exprs.push_back(Reader->Reader.ReadSubExpr());
|
||||
C->setAssignmentOps(Exprs);
|
||||
}
|
||||
|
||||
void OMPClauseReader::VisitOMPFlushClause(OMPFlushClause *C) {
|
||||
|
|
|
@ -1857,6 +1857,12 @@ void OMPClauseWriter::VisitOMPCopyprivateClause(OMPCopyprivateClause *C) {
|
|||
Writer->Writer.AddSourceLocation(C->getLParenLoc(), Record);
|
||||
for (auto *VE : C->varlists())
|
||||
Writer->Writer.AddStmt(VE);
|
||||
for (auto *E : C->source_exprs())
|
||||
Writer->Writer.AddStmt(E);
|
||||
for (auto *E : C->destination_exprs())
|
||||
Writer->Writer.AddStmt(E);
|
||||
for (auto *E : C->assignment_ops())
|
||||
Writer->Writer.AddStmt(E);
|
||||
}
|
||||
|
||||
void OMPClauseWriter::VisitOMPFlushClause(OMPFlushClause *C) {
|
||||
|
|
|
@ -7,19 +7,38 @@
|
|||
#ifndef HEADER
|
||||
#define HEADER
|
||||
|
||||
class TestClass {
|
||||
public:
|
||||
int a;
|
||||
TestClass() : a(0) {}
|
||||
TestClass(const TestClass &C) : a(C.a) {}
|
||||
TestClass &operator=(const TestClass &) { return *this;}
|
||||
~TestClass(){};
|
||||
};
|
||||
|
||||
// CHECK-DAG: [[TEST_CLASS_TY:%.+]] = type { i{{[0-9]+}} }
|
||||
// CHECK: [[IDENT_T_TY:%.+]] = type { i32, i32, i32, i32, i8* }
|
||||
|
||||
// CHECK: define void [[FOO:@.+]]()
|
||||
|
||||
TestClass tc;
|
||||
#pragma omp threadprivate(tc)
|
||||
|
||||
void foo() {}
|
||||
|
||||
// CHECK-LABEL: @main
|
||||
// TERM_DEBUG-LABEL: @main
|
||||
int main() {
|
||||
// CHECK: [[A_ADDR:%.+]] = alloca i8
|
||||
// CHECK-DAG: [[A_ADDR:%.+]] = alloca i8
|
||||
// CHECK-DAG: [[C_ADDR:%.+]] = alloca [[TEST_CLASS_TY]]
|
||||
char a;
|
||||
TestClass c;
|
||||
|
||||
// CHECK: [[GTID:%.+]] = call i32 @__kmpc_global_thread_num([[IDENT_T_TY]]* [[DEFAULT_LOC:@.+]])
|
||||
// CHECK-DAG: [[DID_IT:%.+]] = alloca i32,
|
||||
// CHECK-DAG: [[COPY_LIST:%.+]] = alloca [3 x i8*],
|
||||
// CHECK: store i32 0, i32* [[DID_IT]]
|
||||
|
||||
// CHECK: [[RES:%.+]] = call i32 @__kmpc_single([[IDENT_T_TY]]* [[DEFAULT_LOC]], i32 [[GTID]])
|
||||
// CHECK-NEXT: [[IS_SINGLE:%.+]] = icmp ne i32 [[RES]], 0
|
||||
// CHECK-NEXT: br i1 [[IS_SINGLE]], label {{%?}}[[THEN:.+]], label {{%?}}[[EXIT:.+]]
|
||||
|
@ -28,23 +47,69 @@ int main() {
|
|||
// CHECK-NEXT: call void @__kmpc_end_single([[IDENT_T_TY]]* [[DEFAULT_LOC]], i32 [[GTID]])
|
||||
// CHECK-NEXT: br label {{%?}}[[EXIT]]
|
||||
// CHECK: [[EXIT]]
|
||||
#pragma omp single
|
||||
#pragma omp single nowait
|
||||
a = 2;
|
||||
// CHECK: [[RES:%.+]] = call i32 @__kmpc_single([[IDENT_T_TY]]* [[DEFAULT_LOC]], i32 [[GTID]])
|
||||
// CHECK-NEXT: [[IS_SINGLE:%.+]] = icmp ne i32 [[RES]], 0
|
||||
// CHECK-NEXT: br i1 [[IS_SINGLE]], label {{%?}}[[THEN:.+]], label {{%?}}[[EXIT:.+]]
|
||||
// CHECK: [[THEN]]
|
||||
// CHECK-NEXT: invoke void [[FOO]]()
|
||||
// CHECK: to label {{%?}}[[CONT:.+]] unwind
|
||||
// CHECK: [[CONT]]
|
||||
// CHECK: store i32 1, i32* [[DID_IT]]
|
||||
// CHECK: call void @__kmpc_end_single([[IDENT_T_TY]]* [[DEFAULT_LOC]], i32 [[GTID]])
|
||||
// CHECK-NEXT: br label {{%?}}[[EXIT]]
|
||||
// CHECK: [[EXIT]]
|
||||
#pragma omp single
|
||||
// CHECK: [[A_PTR_REF:%.+]] = getelementptr inbounds [3 x i8*], [3 x i8*]* [[COPY_LIST]], i{{[0-9]+}} 0, i{{[0-9]+}} 0
|
||||
// CHECK: store i8* [[A_ADDR]], i8** [[A_PTR_REF]],
|
||||
// CHECK: [[C_PTR_REF:%.+]] = getelementptr inbounds [3 x i8*], [3 x i8*]* [[COPY_LIST]], i{{[0-9]+}} 0, i{{[0-9]+}} 1
|
||||
// CHECK: [[C_PTR_REF_VOID_PTR:%.+]] = bitcast [[TEST_CLASS_TY]]* [[C_ADDR]] to i8*
|
||||
// CHECK: store i8* [[C_PTR_REF_VOID_PTR]], i8** [[C_PTR_REF]],
|
||||
// CHECK: [[TC_PTR_REF:%.+]] = getelementptr inbounds [3 x i8*], [3 x i8*]* [[COPY_LIST]], i{{[0-9]+}} 0, i{{[0-9]+}} 2
|
||||
// CHECK: [[TC_THREADPRIVATE_ADDR_VOID_PTR:%.+]] = call{{.*}} i8* @__kmpc_threadprivate_cached
|
||||
// CHECK: [[TC_THREADPRIVATE_ADDR:%.+]] = bitcast i8* [[TC_THREADPRIVATE_ADDR_VOID_PTR]] to [[TEST_CLASS_TY]]*
|
||||
// CHECK: [[TC_PTR_REF_VOID_PTR:%.+]] = bitcast [[TEST_CLASS_TY]]* [[TC_THREADPRIVATE_ADDR]] to i8*
|
||||
// CHECK: store i8* [[TC_PTR_REF_VOID_PTR]], i8** [[TC_PTR_REF]],
|
||||
// CHECK: [[COPY_LIST_VOID_PTR:%.+]] = bitcast [3 x i8*]* [[COPY_LIST]] to i8*
|
||||
// CHECK: [[DID_IT_VAL:%.+]] = load i32, i32* [[DID_IT]],
|
||||
// CHECK: call void @__kmpc_copyprivate([[IDENT_T_TY]]* [[DEFAULT_LOC]], i32 [[GTID]], i32 24, i8* [[COPY_LIST_VOID_PTR]], void (i8*, i8*)* [[COPY_FUNC:@.+]], i32 [[DID_IT_VAL]])
|
||||
// CHECK: call{{.*}} @__kmpc_cancel_barrier([[IDENT_T_TY]]* {{@.+}}, i32 [[GTID]])
|
||||
#pragma omp single copyprivate(a, c, tc)
|
||||
foo();
|
||||
// CHECK-NOT: call i32 @__kmpc_single
|
||||
// CHECK-NOT: call void @__kmpc_end_single
|
||||
return a;
|
||||
}
|
||||
|
||||
// CHECK: void [[COPY_FUNC]](i8*, i8*)
|
||||
// CHECK: store i8* %0, i8** [[DST_ADDR_REF:%.+]],
|
||||
// CHECK: store i8* %1, i8** [[SRC_ADDR_REF:%.+]],
|
||||
// CHECK: [[DST_ADDR_VOID_PTR:%.+]] = load i8*, i8** [[DST_ADDR_REF]],
|
||||
// CHECK: [[DST_ADDR:%.+]] = bitcast i8* [[DST_ADDR_VOID_PTR]] to [3 x i8*]*
|
||||
// CHECK: [[SRC_ADDR_VOID_PTR:%.+]] = load i8*, i8** [[SRC_ADDR_REF]],
|
||||
// CHECK: [[SRC_ADDR:%.+]] = bitcast i8* [[SRC_ADDR_VOID_PTR]] to [3 x i8*]*
|
||||
// CHECK: [[SRC_A_ADDR_REF:%.+]] = getelementptr inbounds [3 x i8*], [3 x i8*]* [[SRC_ADDR]], i{{[0-9]+}} 0, i{{[0-9]+}} 0
|
||||
// CHECK: [[SRC_A_ADDR:%.+]] = load i8*, i8** [[SRC_A_ADDR_REF]],
|
||||
// CHECK: [[DST_A_ADDR_REF:%.+]] = getelementptr inbounds [3 x i8*], [3 x i8*]* [[DST_ADDR]], i{{[0-9]+}} 0, i{{[0-9]+}} 0
|
||||
// CHECK: [[DST_A_ADDR:%.+]] = load i8*, i8** [[DST_A_ADDR_REF]],
|
||||
// CHECK: [[SRC_C_ADDR_REF:%.+]] = getelementptr inbounds [3 x i8*], [3 x i8*]* [[SRC_ADDR]], i{{[0-9]+}} 0, i{{[0-9]+}} 1
|
||||
// CHECK: [[SRC_C_ADDR_VOID_PTR:%.+]] = load i8*, i8** [[SRC_C_ADDR_REF]],
|
||||
// CHECK: [[SRC_C_ADDR:%.+]] = bitcast i8* [[SRC_C_ADDR_VOID_PTR:%.+]] to [[TEST_CLASS_TY]]*
|
||||
// CHECK: [[DST_C_ADDR_REF:%.+]] = getelementptr inbounds [3 x i8*], [3 x i8*]* [[DST_ADDR]], i{{[0-9]+}} 0, i{{[0-9]+}} 1
|
||||
// CHECK: [[DST_C_ADDR_VOID_PTR:%.+]] = load i8*, i8** [[DST_C_ADDR_REF]],
|
||||
// CHECK: [[DST_C_ADDR:%.+]] = bitcast i8* [[DST_C_ADDR_VOID_PTR:%.+]] to [[TEST_CLASS_TY]]*
|
||||
// CHECK: [[SRC_TC_ADDR_REF:%.+]] = getelementptr inbounds [3 x i8*], [3 x i8*]* [[SRC_ADDR]], i{{[0-9]+}} 0, i{{[0-9]+}} 2
|
||||
// CHECK: [[SRC_TC_ADDR_VOID_PTR:%.+]] = load i8*, i8** [[SRC_TC_ADDR_REF]],
|
||||
// CHECK: [[SRC_TC_ADDR:%.+]] = bitcast i8* [[SRC_TC_ADDR_VOID_PTR:%.+]] to [[TEST_CLASS_TY]]*
|
||||
// CHECK: [[DST_TC_ADDR_REF:%.+]] = getelementptr inbounds [3 x i8*], [3 x i8*]* [[DST_ADDR]], i{{[0-9]+}} 0, i{{[0-9]+}} 2
|
||||
// CHECK: [[DST_TC_ADDR_VOID_PTR:%.+]] = load i8*, i8** [[DST_TC_ADDR_REF]],
|
||||
// CHECK: [[DST_TC_ADDR:%.+]] = bitcast i8* [[DST_TC_ADDR_VOID_PTR:%.+]] to [[TEST_CLASS_TY]]*
|
||||
// CHECK: [[SRC_A_VAL:%.+]] = load i8, i8* [[SRC_A_ADDR]],
|
||||
// CHECK: store i8 [[SRC_A_VAL]], i8* [[DST_A_ADDR]],
|
||||
// CHECK: call{{.*}} [[TEST_CLASS_TY_ASSIGN:@.+]]([[TEST_CLASS_TY]]* [[DST_C_ADDR]], [[TEST_CLASS_TY]]* {{.*}}[[SRC_C_ADDR]])
|
||||
// CHECK: call{{.*}} [[TEST_CLASS_TY_ASSIGN:@.+]]([[TEST_CLASS_TY]]* [[DST_TC_ADDR]], [[TEST_CLASS_TY]]* {{.*}}[[SRC_TC_ADDR]])
|
||||
// CHECK: ret void
|
||||
|
||||
// CHECK-LABEL: parallel_single
|
||||
// TERM_DEBUG-LABEL: parallel_single
|
||||
void parallel_single() {
|
||||
|
@ -61,7 +126,7 @@ void parallel_single() {
|
|||
// TERM_DEBUG: unreachable
|
||||
foo();
|
||||
}
|
||||
// TERM_DEBUG-DAG: [[DBG_LOC_START]] = !MDLocation(line: 52,
|
||||
// TERM_DEBUG-DAG: [[DBG_LOC_END]] = !MDLocation(line: 52,
|
||||
// TERM_DEBUG-DAG: [[DBG_LOC_START]] = !MDLocation(line: [[@LINE-12]],
|
||||
// TERM_DEBUG-DAG: [[DBG_LOC_END]] = !MDLocation(line: [[@LINE-13]],
|
||||
|
||||
#endif
|
||||
|
|
|
@ -18,18 +18,18 @@ public:
|
|||
S3() : a(0) {}
|
||||
S3 &operator=(S3 &s3) { return *this; }
|
||||
};
|
||||
class S4 { // expected-note 2 {{'S4' declared here}}
|
||||
class S4 {
|
||||
int a;
|
||||
S4();
|
||||
S4 &operator=(const S4 &s4);
|
||||
S4 &operator=(const S4 &s4); // expected-note 3 {{implicitly declared private here}}
|
||||
|
||||
public:
|
||||
S4(int v) : a(v) {}
|
||||
};
|
||||
class S5 { // expected-note 2 {{'S5' declared here}}
|
||||
class S5 {
|
||||
int a;
|
||||
S5() : a(0) {}
|
||||
S5 &operator=(const S5 &s5) { return *this; }
|
||||
S5 &operator=(const S5 &s5) { return *this; } // expected-note 3 {{implicitly declared private here}}
|
||||
|
||||
public:
|
||||
S5(int v) : a(v) {}
|
||||
|
@ -37,8 +37,8 @@ public:
|
|||
|
||||
S2 k;
|
||||
S3 h;
|
||||
S4 l(3); // expected-note 2 {{'l' defined here}}
|
||||
S5 m(4); // expected-note 2 {{'m' defined here}}
|
||||
S4 l(3);
|
||||
S5 m(4);
|
||||
#pragma omp threadprivate(h, k, l, m)
|
||||
|
||||
template <class T, class C>
|
||||
|
@ -58,7 +58,7 @@ T tmain(T argc, C **argv) {
|
|||
#pragma omp parallel
|
||||
#pragma omp single copyprivate(argc > 0 ? argv[1] : argv[2]) // expected-error {{expected variable name}}
|
||||
#pragma omp parallel
|
||||
#pragma omp single copyprivate(l) // expected-error {{copyprivate variable must have an accessible, unambiguous copy assignment operator}}
|
||||
#pragma omp single copyprivate(l) // expected-error 2 {{'operator=' is a private member of 'S4'}}
|
||||
#pragma omp parallel
|
||||
#pragma omp single copyprivate(S1) // expected-error {{'S1' does not refer to a value}}
|
||||
#pragma omp parallel
|
||||
|
@ -66,7 +66,7 @@ T tmain(T argc, C **argv) {
|
|||
#pragma omp parallel // expected-note {{implicitly determined as shared}}
|
||||
#pragma omp single copyprivate(i) // expected-error {{copyprivate variable must be threadprivate or private in the enclosing context}}
|
||||
#pragma omp parallel
|
||||
#pragma omp single copyprivate(m) // expected-error {{copyprivate variable must have an accessible, unambiguous copy assignment operator}}
|
||||
#pragma omp single copyprivate(m) // expected-error 2 {{'operator=' is a private member of 'S5'}}
|
||||
foo();
|
||||
#pragma omp parallel private(i)
|
||||
{
|
||||
|
@ -121,7 +121,7 @@ int main(int argc, char **argv) {
|
|||
#pragma omp parallel
|
||||
#pragma omp single copyprivate(argc > 0 ? argv[1] : argv[2]) // expected-error {{expected variable name}}
|
||||
#pragma omp parallel
|
||||
#pragma omp single copyprivate(l) // expected-error {{copyprivate variable must have an accessible, unambiguous copy assignment operator}}
|
||||
#pragma omp single copyprivate(l) // expected-error {{'operator=' is a private member of 'S4'}}
|
||||
#pragma omp parallel
|
||||
#pragma omp single copyprivate(S1) // expected-error {{'S1' does not refer to a value}}
|
||||
#pragma omp parallel
|
||||
|
@ -129,7 +129,7 @@ int main(int argc, char **argv) {
|
|||
#pragma omp parallel // expected-note {{implicitly determined as shared}}
|
||||
#pragma omp single copyprivate(i) // expected-error {{copyprivate variable must be threadprivate or private in the enclosing context}}
|
||||
#pragma omp parallel
|
||||
#pragma omp single copyprivate(m) // expected-error {{copyprivate variable must have an accessible, unambiguous copy assignment operator}}
|
||||
#pragma omp single copyprivate(m) // expected-error {{'operator=' is a private member of 'S5'}}
|
||||
foo();
|
||||
#pragma omp parallel private(i)
|
||||
{
|
||||
|
|
|
@ -2053,6 +2053,15 @@ void OMPClauseEnqueue::VisitOMPCopyinClause(const OMPCopyinClause *C) {
|
|||
void
|
||||
OMPClauseEnqueue::VisitOMPCopyprivateClause(const OMPCopyprivateClause *C) {
|
||||
VisitOMPClauseList(C);
|
||||
for (auto *E : C->source_exprs()) {
|
||||
Visitor->AddStmt(E);
|
||||
}
|
||||
for (auto *E : C->destination_exprs()) {
|
||||
Visitor->AddStmt(E);
|
||||
}
|
||||
for (auto *E : C->assignment_ops()) {
|
||||
Visitor->AddStmt(E);
|
||||
}
|
||||
}
|
||||
void OMPClauseEnqueue::VisitOMPFlushClause(const OMPFlushClause *C) {
|
||||
VisitOMPClauseList(C);
|
||||
|
|
Loading…
Reference in New Issue