From: Alexey Bataev Date: Fri, 4 Mar 2016 09:22:22 +0000 (+0000) Subject: [OPENMP 4.0] Codegen for 'declare reduction' construct. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=a6da6f0f080ca28b16e3ba79d0d56dbc873e3b41;p=clang [OPENMP 4.0] Codegen for 'declare reduction' construct. Emit function for 'combiner' part of 'declare reduction' construct and 'initialilzer' part, if any. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262699 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/DeclOpenMP.h b/include/clang/AST/DeclOpenMP.h index 720492f75f..1975bc551c 100644 --- a/include/clang/AST/DeclOpenMP.h +++ b/include/clang/AST/DeclOpenMP.h @@ -99,7 +99,7 @@ public: /// \endcode /// /// Here 'omp_out += omp_in' is a combiner and 'omp_priv = 0' is an initializer. -class OMPDeclareReductionDecl final : public NamedDecl, public DeclContext { +class OMPDeclareReductionDecl final : public ValueDecl, public DeclContext { private: friend class ASTDeclReader; /// \brief Combiner for declare reduction construct. @@ -110,21 +110,18 @@ private: /// scope with the same name. Required for proper templates instantiation if /// the declare reduction construct is declared inside compound statement. LazyDeclPtr PrevDeclInScope; - /// \brief Type of declare reduction construct. - QualType Ty; virtual void anchor(); OMPDeclareReductionDecl(Kind DK, DeclContext *DC, SourceLocation L, DeclarationName Name, QualType Ty, OMPDeclareReductionDecl *PrevDeclInScope) - : NamedDecl(DK, DC, L, Name), DeclContext(DK), Combiner(nullptr), - Initializer(nullptr), PrevDeclInScope(PrevDeclInScope), Ty(Ty) {} + : ValueDecl(DK, DC, L, Name, Ty), DeclContext(DK), Combiner(nullptr), + Initializer(nullptr), PrevDeclInScope(PrevDeclInScope) {} void setPrevDeclInScope(OMPDeclareReductionDecl *Prev) { PrevDeclInScope = Prev; } - void setType(QualType T) { Ty = T; } public: /// \brief Create declare reduction node. @@ -153,8 +150,6 @@ public: OMPDeclareReductionDecl *getPrevDeclInScope(); const OMPDeclareReductionDecl *getPrevDeclInScope() const; - QualType getType() const { return Ty; } - static bool classof(const Decl *D) { return classofKind(D->getKind()); } static bool classofKind(Kind K) { return K == OMPDeclareReduction; } static DeclContext *castToDeclContext(const OMPDeclareReductionDecl *D) { diff --git a/include/clang/AST/GlobalDecl.h b/include/clang/AST/GlobalDecl.h index 54c9d88c9b..adf63a3aea 100644 --- a/include/clang/AST/GlobalDecl.h +++ b/include/clang/AST/GlobalDecl.h @@ -17,6 +17,7 @@ #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" +#include "clang/AST/DeclOpenMP.h" #include "clang/Basic/ABI.h" namespace clang { @@ -43,6 +44,7 @@ public: GlobalDecl(const BlockDecl *D) { Init(D); } GlobalDecl(const CapturedDecl *D) { Init(D); } GlobalDecl(const ObjCMethodDecl *D) { Init(D); } + GlobalDecl(const OMPDeclareReductionDecl *D) { Init(D); } GlobalDecl(const CXXConstructorDecl *D, CXXCtorType Type) : Value(D, Type) {} diff --git a/include/clang/Basic/DeclNodes.td b/include/clang/Basic/DeclNodes.td index 3c64c41f85..b42b33f98c 100644 --- a/include/clang/Basic/DeclNodes.td +++ b/include/clang/Basic/DeclNodes.td @@ -37,6 +37,7 @@ def Named : Decl<1>; def EnumConstant : DDecl; def UnresolvedUsingValue : DDecl; def IndirectField : DDecl; + def OMPDeclareReduction : DDecl, DeclContext; def Declarator : DDecl; def Field : DDecl; def ObjCIvar : DDecl; @@ -75,7 +76,6 @@ def Named : Decl<1>; def ObjCImplementation : DDecl; def ObjCProperty : DDecl; def ObjCCompatibleAlias : DDecl; - def OMPDeclareReduction : DDecl, DeclContext; def LinkageSpec : Decl, DeclContext; def ObjCPropertyImpl : Decl; def FileScopeAsm : Decl; diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 4c9ccd7cc9..cd0eada904 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -8508,7 +8508,9 @@ bool ASTContext::DeclMustBeEmitted(const Decl *D) { else if (isa(D)) return true; else if (isa(D)) - return true; + return !D->getDeclContext()->isDependentContext(); + else if (isa(D)) + return !D->getDeclContext()->isDependentContext(); else return false; diff --git a/lib/CodeGen/CGDecl.cpp b/lib/CodeGen/CGDecl.cpp index 36d20cca38..e0c297508f 100644 --- a/lib/CodeGen/CGDecl.cpp +++ b/lib/CodeGen/CGDecl.cpp @@ -16,6 +16,7 @@ #include "CGCleanup.h" #include "CGDebugInfo.h" #include "CGOpenCLRuntime.h" +#include "CGOpenMPRuntime.h" #include "CodeGenModule.h" #include "clang/AST/ASTContext.h" #include "clang/AST/CharUnits.h" @@ -120,7 +121,7 @@ void CodeGenFunction::EmitDecl(const Decl &D) { } case Decl::OMPDeclareReduction: - return CGM.EmitOMPDeclareReduction(cast(&D)); + return CGM.EmitOMPDeclareReduction(cast(&D), this); case Decl::Typedef: // typedef int X; case Decl::TypeAlias: { // using X = int; [C++0x] @@ -1867,6 +1868,10 @@ void CodeGenFunction::EmitParmDecl(const VarDecl &D, ParamValue Arg, EmitVarAnnotations(&D, DeclPtr.getPointer()); } -void CodeGenModule::EmitOMPDeclareReduction( - const OMPDeclareReductionDecl * /*D*/) {} +void CodeGenModule::EmitOMPDeclareReduction(const OMPDeclareReductionDecl *D, + CodeGenFunction *CGF) { + if (!LangOpts.OpenMP || (!LangOpts.EmitAllDecls && !D->isUsed())) + return; + getOpenMPRuntime().emitUserDefinedReduction(CGF, D); +} diff --git a/lib/CodeGen/CGOpenMPRuntime.cpp b/lib/CodeGen/CGOpenMPRuntime.cpp index 7213c4d079..a38d6fdd3a 100644 --- a/lib/CodeGen/CGOpenMPRuntime.cpp +++ b/lib/CodeGen/CGOpenMPRuntime.cpp @@ -593,8 +593,7 @@ LValue CGOpenMPTaskOutlinedRegionInfo::getThreadIDVariableLValue( } CGOpenMPRuntime::CGOpenMPRuntime(CodeGenModule &CGM) - : CGM(CGM), DefaultOpenMPPSource(nullptr), KmpRoutineEntryPtrTy(nullptr), - OffloadEntriesInfoManager(CGM) { + : CGM(CGM), OffloadEntriesInfoManager(CGM) { IdentTy = llvm::StructType::create( "ident_t", CGM.Int32Ty /* reserved_1 */, CGM.Int32Ty /* flags */, CGM.Int32Ty /* reserved_2 */, CGM.Int32Ty /* reserved_3 */, @@ -612,6 +611,82 @@ void CGOpenMPRuntime::clear() { InternalVars.clear(); } +static llvm::Function * +emitCombinerOrInitializer(CodeGenModule &CGM, QualType Ty, + const Expr *CombinerInitializer, const VarDecl *In, + const VarDecl *Out, bool IsCombiner) { + // void .omp_combiner.(Ty *in, Ty *out); + auto &C = CGM.getContext(); + QualType PtrTy = C.getPointerType(Ty).withRestrict(); + FunctionArgList Args; + ImplicitParamDecl OmpInParm(C, /*DC=*/nullptr, In->getLocation(), + /*Id=*/nullptr, PtrTy); + ImplicitParamDecl OmpOutParm(C, /*DC=*/nullptr, Out->getLocation(), + /*Id=*/nullptr, PtrTy); + Args.push_back(&OmpInParm); + Args.push_back(&OmpOutParm); + FunctionType::ExtInfo Info; + auto &FnInfo = + CGM.getTypes().arrangeFreeFunctionDeclaration(C.VoidTy, Args, Info, + /*isVariadic=*/false); + auto *FnTy = CGM.getTypes().GetFunctionType(FnInfo); + auto *Fn = llvm::Function::Create( + FnTy, llvm::GlobalValue::InternalLinkage, + IsCombiner ? ".omp_combiner." : ".omp_initializer.", &CGM.getModule()); + CGM.SetInternalFunctionAttributes(/*D=*/nullptr, Fn, FnInfo); + CodeGenFunction CGF(CGM); + // Map "T omp_in;" variable to "*omp_in_parm" value in all expressions. + // Map "T omp_out;" variable to "*omp_out_parm" value in all expressions. + CGF.StartFunction(GlobalDecl(), C.VoidTy, Fn, FnInfo, Args); + CodeGenFunction::OMPPrivateScope Scope(CGF); + Address AddrIn = CGF.GetAddrOfLocalVar(&OmpInParm); + Scope.addPrivate(In, [&CGF, AddrIn, PtrTy]() -> Address { + return CGF.EmitLoadOfPointerLValue(AddrIn, PtrTy->castAs()) + .getAddress(); + }); + Address AddrOut = CGF.GetAddrOfLocalVar(&OmpOutParm); + Scope.addPrivate(Out, [&CGF, AddrOut, PtrTy]() -> Address { + return CGF.EmitLoadOfPointerLValue(AddrOut, PtrTy->castAs()) + .getAddress(); + }); + (void)Scope.Privatize(); + CGF.EmitIgnoredExpr(CombinerInitializer); + Scope.ForceCleanup(); + CGF.FinishFunction(); + return Fn; +} + +void CGOpenMPRuntime::emitUserDefinedReduction( + CodeGenFunction *CGF, const OMPDeclareReductionDecl *D) { + if (UDRMap.count(D) > 0) + return; + auto &C = CGM.getContext(); + if (!In || !Out) { + In = &C.Idents.get("omp_in"); + Out = &C.Idents.get("omp_out"); + } + llvm::Function *Combiner = emitCombinerOrInitializer( + CGM, D->getType(), D->getCombiner(), cast(D->lookup(In).front()), + cast(D->lookup(Out).front()), + /*IsCombiner=*/true); + llvm::Function *Initializer = nullptr; + if (auto *Init = D->getInitializer()) { + if (!Priv || !Orig) { + Priv = &C.Idents.get("omp_priv"); + Orig = &C.Idents.get("omp_orig"); + } + Initializer = emitCombinerOrInitializer( + CGM, D->getType(), Init, cast(D->lookup(Orig).front()), + cast(D->lookup(Priv).front()), + /*IsCombiner=*/false); + } + UDRMap.insert(std::make_pair(D, std::make_pair(Combiner, Initializer))); + if (CGF) { + auto &Decls = FunctionUDRMap.FindAndConstruct(CGF->CurFn); + Decls.second.push_back(D); + } +} + // Layout information for ident_t. static CharUnits getIdentAlign(CodeGenModule &CGM) { return CGM.getPointerAlign(); @@ -801,6 +876,12 @@ void CGOpenMPRuntime::functionFinished(CodeGenFunction &CGF) { assert(CGF.CurFn && "No function in current CodeGenFunction."); if (OpenMPLocThreadIDMap.count(CGF.CurFn)) OpenMPLocThreadIDMap.erase(CGF.CurFn); + if (FunctionUDRMap.count(CGF.CurFn) > 0) { + for(auto *D : FunctionUDRMap[CGF.CurFn]) { + UDRMap.erase(D); + } + FunctionUDRMap.erase(CGF.CurFn); + } } llvm::Type *CGOpenMPRuntime::getIdentTyPointerTy() { diff --git a/lib/CodeGen/CGOpenMPRuntime.h b/lib/CodeGen/CGOpenMPRuntime.h index 6169dacb3f..0044bb706f 100644 --- a/lib/CodeGen/CGOpenMPRuntime.h +++ b/lib/CodeGen/CGOpenMPRuntime.h @@ -38,6 +38,8 @@ class Expr; class GlobalDecl; class OMPExecutableDirective; class VarDecl; +class OMPDeclareReductionDecl; +class IdentifierInfo; namespace CodeGen { class Address; @@ -50,7 +52,7 @@ class CGOpenMPRuntime { CodeGenModule &CGM; /// \brief Default const ident_t object used for initialization of all other /// ident_t objects. - llvm::Constant *DefaultOpenMPPSource; + llvm::Constant *DefaultOpenMPPSource = nullptr; /// \brief Map of flags and corresponding default locations. typedef llvm::DenseMap OpenMPDefaultLocMapTy; OpenMPDefaultLocMapTy OpenMPDefaultLocMap; @@ -73,6 +75,20 @@ class CGOpenMPRuntime { typedef llvm::DenseMap OpenMPLocThreadIDMapTy; OpenMPLocThreadIDMapTy OpenMPLocThreadIDMap; + /// Map of UDRs and corresponding combiner/initializer. + typedef llvm::DenseMap> + UDRMapTy; + UDRMapTy UDRMap; + /// Map of functions and locally defined UDRs. + typedef llvm::DenseMap> + FunctionUDRMapTy; + FunctionUDRMapTy FunctionUDRMap; + IdentifierInfo *In = nullptr; + IdentifierInfo *Out = nullptr; + IdentifierInfo *Priv = nullptr; + IdentifierInfo *Orig = nullptr; /// \brief Type kmp_critical_name, originally defined as typedef kmp_int32 /// kmp_critical_name[8]; llvm::ArrayType *KmpCriticalNameTy; @@ -84,7 +100,7 @@ class CGOpenMPRuntime { llvm::StringMap, llvm::BumpPtrAllocator> InternalVars; /// \brief Type typedef kmp_int32 (* kmp_routine_entry_t)(kmp_int32, void *); - llvm::Type *KmpRoutineEntryPtrTy; + llvm::Type *KmpRoutineEntryPtrTy = nullptr; QualType KmpRoutineEntryPtrQTy; /// \brief Type typedef struct kmp_task { /// void * shareds; /**< pointer to block of pointers to @@ -364,6 +380,9 @@ public: virtual ~CGOpenMPRuntime() {} virtual void clear(); + /// Emit code for the specified user defined reduction construct. + virtual void emitUserDefinedReduction(CodeGenFunction *CGF, + const OMPDeclareReductionDecl *D); /// \brief Emits outlined function for the specified OpenMP parallel directive /// \a D. This outlined function has type void(*)(kmp_int32 *ThreadID, /// kmp_int32 BoundID, struct context_vars*). diff --git a/lib/CodeGen/CGOpenMPRuntimeNVPTX.cpp b/lib/CodeGen/CGOpenMPRuntimeNVPTX.cpp index 314a4b5449..680ed57813 100644 --- a/lib/CodeGen/CGOpenMPRuntimeNVPTX.cpp +++ b/lib/CodeGen/CGOpenMPRuntimeNVPTX.cpp @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// #include "CGOpenMPRuntimeNVPTX.h" +#include "clang/AST/DeclOpenMP.h" using namespace clang; using namespace CodeGen; diff --git a/lib/CodeGen/CodeGenModule.cpp b/lib/CodeGen/CodeGenModule.cpp index e171abeace..fad31995ed 100644 --- a/lib/CodeGen/CodeGenModule.cpp +++ b/lib/CodeGen/CodeGenModule.cpp @@ -1543,10 +1543,17 @@ void CodeGenModule::EmitGlobal(GlobalDecl GD) { } } - // If this is OpenMP device, check if it is legal to emit this global - // normally. - if (OpenMPRuntime && OpenMPRuntime->emitTargetGlobal(GD)) - return; + if (LangOpts.OpenMP) { + // If this is OpenMP device, check if it is legal to emit this global + // normally. + if (OpenMPRuntime && OpenMPRuntime->emitTargetGlobal(GD)) + return; + if (auto *DRD = dyn_cast(Global)) { + if (MustBeEmitted(Global)) + EmitOMPDeclareReduction(DRD); + return; + } + } // Ignore declarations, they will be emitted on their first use. if (const auto *FD = dyn_cast(Global)) { diff --git a/lib/CodeGen/CodeGenModule.h b/lib/CodeGen/CodeGenModule.h index 87183582e7..8e662c2537 100644 --- a/lib/CodeGen/CodeGenModule.h +++ b/lib/CodeGen/CodeGenModule.h @@ -1112,7 +1112,8 @@ public: void EmitOMPThreadPrivateDecl(const OMPThreadPrivateDecl *D); /// \brief Emit a code for declare reduction construct. - void EmitOMPDeclareReduction(const OMPDeclareReductionDecl *D); + void EmitOMPDeclareReduction(const OMPDeclareReductionDecl *D, + CodeGenFunction *CGF = nullptr); /// Returns whether we need bit sets attached to vtables. bool NeedVTableBitSets(); diff --git a/lib/CodeGen/ModuleBuilder.cpp b/lib/CodeGen/ModuleBuilder.cpp index 041a15c562..b5a5dfbe09 100644 --- a/lib/CodeGen/ModuleBuilder.cpp +++ b/lib/CodeGen/ModuleBuilder.cpp @@ -187,6 +187,15 @@ namespace { } } } + // For OpenMP emit declare reduction functions, if required. + if (Ctx->getLangOpts().OpenMP) { + for (Decl *Member : D->decls()) { + if (auto *DRD = dyn_cast(Member)) { + if (Ctx->DeclMustBeEmitted(DRD)) + Builder->EmitGlobal(DRD); + } + } + } } void HandleTagDeclRequiredDefinition(const TagDecl *D) override { diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index b644ff215c..a90fda8bfc 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -2861,6 +2861,7 @@ ExprResult Sema::BuildDeclarationNameExpr( // Unresolved using declarations are dependent. case Decl::EnumConstant: case Decl::UnresolvedUsingValue: + case Decl::OMPDeclareReduction: valueKind = VK_RValue; break; diff --git a/lib/Serialization/ASTReaderDecl.cpp b/lib/Serialization/ASTReaderDecl.cpp index 2389d671d4..1e93627df8 100644 --- a/lib/Serialization/ASTReaderDecl.cpp +++ b/lib/Serialization/ASTReaderDecl.cpp @@ -2396,12 +2396,11 @@ void ASTDeclReader::VisitOMPThreadPrivateDecl(OMPThreadPrivateDecl *D) { } void ASTDeclReader::VisitOMPDeclareReductionDecl(OMPDeclareReductionDecl *D) { - VisitNamedDecl(D); + VisitValueDecl(D); D->setLocation(Reader.ReadSourceLocation(F, Record, Idx)); D->setCombiner(Reader.ReadExpr(F)); D->setInitializer(Reader.ReadExpr(F)); D->PrevDeclInScope = Reader.ReadDeclID(F, Record, Idx); - D->setType(Reader.readType(F, Record, Idx)); } void ASTDeclReader::VisitOMPCapturedExprDecl(OMPCapturedExprDecl *D) { @@ -2458,10 +2457,10 @@ static bool isConsumerInterestedIn(Decl *D, bool HasBody) { isa(D) || isa(D) || isa(D) || - isa(D) || - isa(D) || - isa(D)) + isa(D)) return true; + if (isa(D) || isa(D)) + return !D->getDeclContext()->isFunctionOrMethod(); if (VarDecl *Var = dyn_cast(D)) return Var->isFileVarDecl() && Var->isThisDeclarationADefinition() == VarDecl::Definition; diff --git a/lib/Serialization/ASTWriterDecl.cpp b/lib/Serialization/ASTWriterDecl.cpp index 88b74a4b4c..6c81b9e53c 100644 --- a/lib/Serialization/ASTWriterDecl.cpp +++ b/lib/Serialization/ASTWriterDecl.cpp @@ -1655,12 +1655,11 @@ void ASTDeclWriter::VisitOMPThreadPrivateDecl(OMPThreadPrivateDecl *D) { } void ASTDeclWriter::VisitOMPDeclareReductionDecl(OMPDeclareReductionDecl *D) { - VisitNamedDecl(D); + VisitValueDecl(D); Writer.AddSourceLocation(D->getLocStart(), Record); Writer.AddStmt(D->getCombiner()); Writer.AddStmt(D->getInitializer()); Writer.AddDeclRef(D->getPrevDeclInScope(), Record); - Writer.AddTypeRef(D->getType(), Record); Code = serialization::DECL_OMP_DECLARE_REDUCTION; } diff --git a/test/OpenMP/declare_reduction_codegen.c b/test/OpenMP/declare_reduction_codegen.c new file mode 100644 index 0000000000..9926c929cb --- /dev/null +++ b/test/OpenMP/declare_reduction_codegen.c @@ -0,0 +1,158 @@ +// RUN: %clang_cc1 -verify -fopenmp -x c -emit-llvm %s -triple %itanium_abi_triple -o - -femit-all-decls | FileCheck %s +// RUN: %clang_cc1 -fopenmp -x c -triple %itanium_abi_triple -emit-pch -o %t %s -femit-all-decls +// RUN: %clang_cc1 -fopenmp -x c -triple %itanium_abi_triple -include-pch %t -verify %s -emit-llvm -o - -femit-all-decls | FileCheck --check-prefix=CHECK-LOAD %s +// expected-no-diagnostics + +#ifndef HEADER +#define HEADER + +// CHECK: [[SSS_INT:.+]] = type { i32 } +// CHECK-LOAD: [[SSS_INT:.+]] = type { i32 } + +#pragma omp declare reduction(+ : int, char : omp_out *= omp_in) +// CHECK: define internal {{.*}}void @{{[^(]+}}(i32* noalias, i32* noalias) +// CHECK: [[MUL:%.+]] = mul nsw i32 +// CHECK-NEXT: store i32 [[MUL]], i32* +// CHECK-NEXT: ret void +// CHECK-NEXT: } +// CHECK-LOAD: define internal {{.*}}void @{{[^(]+}}(i32* noalias, i32* noalias) +// CHECK-LOAD: [[MUL:%.+]] = mul nsw i32 +// CHECK-LOAD-NEXT: store i32 [[MUL]], i32* +// CHECK-LOAD-NEXT: ret void +// CHECK-LOAD-NEXT: } + +// CHECK: define internal {{.*}}void @{{[^(]+}}(i8* noalias, i8* noalias) +// CHECK: sext i8 +// CHECK: sext i8 +// CHECK: [[MUL:%.+]] = mul nsw i32 +// CHECK-NEXT: [[TRUNC:%.+]] = trunc i32 [[MUL]] to i8 +// CHECK-NEXT: store i8 [[TRUNC]], i8* +// CHECK-NEXT: ret void +// CHECK-NEXT: } +// CHECK-LOAD: define internal {{.*}}void @{{[^(]+}}(i8* noalias, i8* noalias) +// CHECK-LOAD: sext i8 +// CHECK-LOAD: sext i8 +// CHECK-LOAD: [[MUL:%.+]] = mul nsw i32 +// CHECK-LOAD-NEXT: [[TRUNC:%.+]] = trunc i32 [[MUL]] to i8 +// CHECK-LOAD-NEXT: store i8 [[TRUNC]], i8* +// CHECK-LOAD-NEXT: ret void +// CHECK-LOAD-NEXT: } + +#pragma omp declare reduction(fun : float : omp_out += omp_in) initializer(omp_priv = 15 + omp_orig) +// CHECK: define internal {{.*}}void @{{[^(]+}}(float* noalias, float* noalias) +// CHECK: [[ADD:%.+]] = fadd float +// CHECK-NEXT: store float [[ADD]], float* +// CHECK-NEXT: ret void +// CHECK-NEXT: } +// CHECK: define internal {{.*}}void @{{[^(]+}}(float* noalias, float* noalias) +// CHECK: [[ADD:%.+]] = fadd float 1.5 +// CHECK-NEXT: store float [[ADD]], float* +// CHECK-NEXT: ret void +// CHECK-NEXT: } +// CHECK-LOAD: define internal {{.*}}void @{{[^(]+}}(float* noalias, float* noalias) +// CHECK-LOAD: [[ADD:%.+]] = fadd float +// CHECK-LOAD-NEXT: store float [[ADD]], float* +// CHECK-LOAD-NEXT: ret void +// CHECK-LOAD-NEXT: } +// CHECK-LOAD: define internal {{.*}}void @{{[^(]+}}(float* noalias, float* noalias) +// CHECK-LOAD: [[ADD:%.+]] = fadd float 1.5 +// CHECK-LOAD-NEXT: store float [[ADD]], float* +// CHECK-LOAD-NEXT: ret void +// CHECK-LOAD-NEXT: } + +struct SSS { + int field; +#pragma omp declare reduction(+ : int, char : omp_out *= omp_in) + // CHECK: define internal {{.*}}void @{{[^(]+}}(i32* noalias, i32* noalias) + // CHECK: [[MUL:%.+]] = mul nsw i32 + // CHECK-NEXT: store i32 [[MUL]], i32* + // CHECK-NEXT: ret void + // CHECK-NEXT: } + + // CHECK: define internal {{.*}}void @{{[^(]+}}(i8* noalias, i8* noalias) + // CHECK: sext i8 + // CHECK: sext i8 + // CHECK: [[MUL:%.+]] = mul nsw i32 + // CHECK-NEXT: [[TRUNC:%.+]] = trunc i32 [[MUL]] to i8 + // CHECK-NEXT: store i8 [[TRUNC]], i8* + // CHECK-NEXT: ret void + // CHECK-NEXT: } +}; + +void init(struct SSS *priv, struct SSS orig); + +#pragma omp declare reduction(fun : struct SSS : omp_out = omp_in) initializer(init(&omp_priv, omp_orig)) +// CHECK: define internal {{.*}}void @{{[^(]+}}([[SSS_INT]]* noalias, [[SSS_INT]]* noalias) +// CHECK: call void @llvm.memcpy +// CHECK-NEXT: ret void +// CHECK-NEXT: } +// CHECK: define internal {{.*}}void @{{[^(]+}}([[SSS_INT]]* noalias, [[SSS_INT]]* noalias) +// CHECK: call void @init( +// CHECK-NEXT: ret void +// CHECK-NEXT: } +// CHECK-LOAD: define internal {{.*}}void @{{[^(]+}}([[SSS_INT]]* noalias, [[SSS_INT]]* noalias) +// CHECK-LOAD: call void @llvm.memcpy +// CHECK-LOAD-NEXT: ret void +// CHECK-LOAD-NEXT: } +// CHECK-LOAD: define internal {{.*}}void @{{[^(]+}}([[SSS_INT]]* noalias, [[SSS_INT]]* noalias) +// CHECK-LOAD: call void @init( +// CHECK-LOAD-NEXT: ret void +// CHECK-LOAD-NEXT: } + +// CHECK-LABEL: @main +// CHECK-LOAD-LABEL: @main +int main() { +#pragma omp declare reduction(fun : struct SSS : omp_out = omp_in) initializer(init(&omp_priv, omp_orig)) + // CHECK: define internal {{.*}}void @{{[^(]+}}([[SSS_INT]]* noalias, [[SSS_INT]]* noalias) + // CHECK: call void @llvm.memcpy + // CHECK-NEXT: ret void + // CHECK-NEXT: } + // CHECK: define internal {{.*}}void @{{[^(]+}}([[SSS_INT]]* noalias, [[SSS_INT]]* noalias) + // CHECK: call void @init( + // CHECK-NEXT: ret void + // CHECK-NEXT: } + // CHECK-LOAD: define internal {{.*}}void @{{[^(]+}}([[SSS_INT]]* noalias, [[SSS_INT]]* noalias) + // CHECK-LOAD: call void @llvm.memcpy + // CHECK-LOAD-NEXT: ret void + // CHECK-LOAD-NEXT: } + // CHECK-LOAD: define internal {{.*}}void @{{[^(]+}}([[SSS_INT]]* noalias, [[SSS_INT]]* noalias) + // CHECK-LOAD: call void @init( + // CHECK-LOAD-NEXT: ret void + // CHECK-LOAD-NEXT: } + { +#pragma omp declare reduction(fun : struct SSS : omp_out = omp_in) initializer(init(&omp_priv, omp_orig)) + // CHECK: define internal {{.*}}void @{{[^(]+}}([[SSS_INT]]* noalias, [[SSS_INT]]* noalias) + // CHECK: call void @llvm.memcpy + // CHECK-NEXT: ret void + // CHECK-NEXT: } + // CHECK: define internal {{.*}}void @{{[^(]+}}([[SSS_INT]]* noalias, [[SSS_INT]]* noalias) + // CHECK: call void @init( + // CHECK-NEXT: ret void + // CHECK-NEXT: } + // CHECK-LOAD: define internal {{.*}}void @{{[^(]+}}([[SSS_INT]]* noalias, [[SSS_INT]]* noalias) + // CHECK-LOAD: call void @llvm.memcpy + // CHECK-LOAD-NEXT: ret void + // CHECK-LOAD-NEXT: } + // CHECK-LOAD: define internal {{.*}}void @{{[^(]+}}([[SSS_INT]]* noalias, [[SSS_INT]]* noalias) + // CHECK-LOAD: call void @init( + // CHECK-LOAD-NEXT: ret void + // CHECK-LOAD-NEXT: } + } + return 0; +} + +// CHECK-LOAD: define internal {{.*}}void @{{[^(]+}}(i32* noalias, i32* noalias) +// CHECK-LOAD: [[MUL:%.+]] = mul nsw i32 +// CHECK-LOAD-NEXT: store i32 [[MUL]], i32* +// CHECK-LOAD-NEXT: ret void +// CHECK-LOAD-NEXT: } + +// CHECK-LOAD: define internal {{.*}}void @{{[^(]+}}(i8* noalias, i8* noalias) +// CHECK-LOAD: sext i8 +// CHECK-LOAD: sext i8 +// CHECK-LOAD: [[MUL:%.+]] = mul nsw i32 +// CHECK-LOAD-NEXT: [[TRUNC:%.+]] = trunc i32 [[MUL]] to i8 +// CHECK-LOAD-NEXT: store i8 [[TRUNC]], i8* +// CHECK-LOAD-NEXT: ret void +// CHECK-LOAD-NEXT: } +#endif diff --git a/test/OpenMP/declare_reduction_codegen.cpp b/test/OpenMP/declare_reduction_codegen.cpp new file mode 100644 index 0000000000..e5f48d63f3 --- /dev/null +++ b/test/OpenMP/declare_reduction_codegen.cpp @@ -0,0 +1,170 @@ +// RUN: %clang_cc1 -verify -fopenmp -x c++ -emit-llvm %s -triple %itanium_abi_triple -fexceptions -fcxx-exceptions -o - -femit-all-decls | FileCheck %s +// RUN: %clang_cc1 -fopenmp -x c++ -std=c++11 -triple %itanium_abi_triple -fexceptions -fcxx-exceptions -emit-pch -o %t %s -femit-all-decls +// RUN: %clang_cc1 -fopenmp -x c++ -triple %itanium_abi_triple -fexceptions -fcxx-exceptions -std=c++11 -include-pch %t -verify %s -emit-llvm -o - -femit-all-decls | FileCheck --check-prefix=CHECK-LOAD %s +// expected-no-diagnostics + +#ifndef HEADER +#define HEADER + +// CHECK: [[SSS_INT:.+]] = type { i32 } +// CHECK-LOAD: [[SSS_INT:.+]] = type { i32 } + +#pragma omp declare reduction(+ : int, char : omp_out *= omp_in) +// CHECK: define internal {{.*}}void @{{[^(]+}}(i32* noalias, i32* noalias) +// CHECK: [[MUL:%.+]] = mul nsw i32 +// CHECK-NEXT: store i32 [[MUL]], i32* +// CHECK-NEXT: ret void +// CHECK-NEXT: } +// CHECK-LOAD: define internal {{.*}}void @{{[^(]+}}(i32* noalias, i32* noalias) +// CHECK-LOAD: [[MUL:%.+]] = mul nsw i32 +// CHECK-LOAD-NEXT: store i32 [[MUL]], i32* +// CHECK-LOAD-NEXT: ret void +// CHECK-LOAD-NEXT: } + +// CHECK: define internal {{.*}}void @{{[^(]+}}(i8* noalias, i8* noalias) +// CHECK: sext i8 +// CHECK: sext i8 +// CHECK: [[MUL:%.+]] = mul nsw i32 +// CHECK-NEXT: [[TRUNC:%.+]] = trunc i32 [[MUL]] to i8 +// CHECK-NEXT: store i8 [[TRUNC]], i8* +// CHECK-NEXT: ret void +// CHECK-NEXT: } + +// CHECK-LOAD: define internal {{.*}}void @{{[^(]+}}(i8* noalias, i8* noalias) +// CHECK-LOAD: sext i8 +// CHECK-LOAD: sext i8 +// CHECK-LOAD: [[MUL:%.+]] = mul nsw i32 +// CHECK-LOAD-NEXT: [[TRUNC:%.+]] = trunc i32 [[MUL]] to i8 +// CHECK-LOAD-NEXT: store i8 [[TRUNC]], i8* +// CHECK-LOAD-NEXT: ret void +// CHECK-LOAD-NEXT: } + +template +struct SSS { + T a; +#pragma omp declare reduction(fun : T : omp_out ^= omp_in) initializer(omp_priv = 24 + omp_orig) +}; + +SSS d; + +// CHECK: define internal {{.*}}void @{{[^(]+}}(i32* noalias, i32* noalias) +// CHECK: [[XOR:%.+]] = xor i32 +// CHECK-NEXT: store i32 [[XOR]], i32* +// CHECK-NEXT: ret void +// CHECK-NEXT: } + +// CHECK: define internal {{.*}}void @{{[^(]+}}(i32* noalias, i32* noalias) +// CHECK: [[ADD:%.+]] = add nsw i32 24, +// CHECK-NEXT: store i32 [[ADD]], i32* +// CHECK-NEXT: ret void +// CHECK-NEXT: } + +// CHECK: define void [[INIT:@[^(]+]]([[SSS_INT]]* +// CHECK-LOAD: define void [[INIT:@[^(]+]]([[SSS_INT]]* +void init(SSS &lhs, SSS &rhs) {} + +#pragma omp declare reduction(fun : SSS < int > : omp_out = omp_in) initializer(init(omp_priv, omp_orig)) +// CHECK: define internal {{.*}}void @{{[^(]+}}([[SSS_INT]]* noalias, [[SSS_INT]]* noalias) +// CHECK: call void @llvm.memcpy +// CHECK-NEXT: ret void +// CHECK-NEXT: } +// CHECK: define internal {{.*}}void @{{[^(]+}}([[SSS_INT]]* noalias, [[SSS_INT]]* noalias) +// CHECK: call void [[INIT]]( +// CHECK-NEXT: ret void +// CHECK-NEXT: } + +// CHECK-LOAD: define internal {{.*}}void @{{[^(]+}}([[SSS_INT]]* noalias, [[SSS_INT]]* noalias) +// CHECK-LOAD: call void @llvm.memcpy +// CHECK-LOAD-NEXT: ret void +// CHECK-LOAD-NEXT: } +// CHECK-LOAD: define internal {{.*}}void @{{[^(]+}}([[SSS_INT]]* noalias, [[SSS_INT]]* noalias) +// CHECK-LOAD: call void [[INIT]]( +// CHECK-LOAD-NEXT: ret void +// CHECK-LOAD-NEXT: } + +template +T foo(T a) { +#pragma omp declare reduction(fun : T : omp_out += omp_in) initializer(omp_priv = 15 * omp_orig) + { +#pragma omp declare reduction(fun : T : omp_out /= omp_in) initializer(omp_priv = 11 - omp_orig) + } + return a; +} + +// CHECK-LABEL: @main +int main() { + int i = 0; + SSS sss; + // TODO: Add support for scoped reduction identifiers + // #pragma omp parallel reduction(SSS::fun : i) + // TODO-CHECK: #pragma omp parallel reduction(SSS::fun: i) + { + i += 1; + } + // #pragma omp parallel reduction(::fun:sss) + // TODO-CHECK: #pragma omp parallel reduction(::fun: sss) + { + } + return foo(15); +} + +// CHECK-LABEL: i32 @{{.+}}foo{{[^(].+}}(i32 +// CHECK-LOAD-LABEL: i32 @{{.+}}foo{{[^(].+}}(i32 + +// CHECK-LOAD: define internal {{.*}}void @{{[^(]+}}(i32* noalias, i32* noalias) +// CHECK-LOAD: [[XOR:%.+]] = xor i32 +// CHECK-LOAD-NEXT: store i32 [[XOR]], i32* +// CHECK-LOAD-NEXT: ret void +// CHECK-LOAD-NEXT: } + +// CHECK-LOAD: define internal {{.*}}void @{{[^(]+}}(i32* noalias, i32* noalias) +// CHECK-LOAD: [[ADD:%.+]] = add nsw i32 24, +// CHECK-LOAD-NEXT: store i32 [[ADD]], i32* +// CHECK-LOAD-NEXT: ret void +// CHECK-LOAD-NEXT: } + +// CHECK: define internal {{.*}}void @{{[^(]+}}(i32* noalias, i32* noalias) +// CHECK: [[ADD:%.+]] = add nsw i32 +// CHECK-NEXT: store i32 [[ADD]], i32* +// CHECK-NEXT: ret void +// CHECK-NEXT: } +// CHECK-LOAD: define internal {{.*}}void @{{[^(]+}}(i32* noalias, i32* noalias) +// CHECK-LOAD: [[ADD:%.+]] = add nsw i32 +// CHECK-LOAD-NEXT: store i32 [[ADD]], i32* +// CHECK-LOAD-NEXT: ret void +// CHECK-LOAD-NEXT: } + +// CHECK: define internal {{.*}}void @{{[^(]+}}(i32* noalias, i32* noalias) +// CHECK: [[MUL:%.+]] = mul nsw i32 15, +// CHECK-NEXT: store i32 [[MUL]], i32* +// CHECK-NEXT: ret void +// CHECK-NEXT: } +// CHECK-LOAD: define internal {{.*}}void @{{[^(]+}}(i32* noalias, i32* noalias) +// CHECK-LOAD: [[MUL:%.+]] = mul nsw i32 15, +// CHECK-LOAD-NEXT: store i32 [[MUL]], i32* +// CHECK-LOAD-NEXT: ret void +// CHECK-LOAD-NEXT: } + +// CHECK: define internal {{.*}}void @{{[^(]+}}(i32* noalias, i32* noalias) +// CHECK: [[DIV:%.+]] = sdiv i32 +// CHECK-NEXT: store i32 [[DIV]], i32* +// CHECK-NEXT: ret void +// CHECK-NEXT: } +// CHECK-LOAD: define internal {{.*}}void @{{[^(]+}}(i32* noalias, i32* noalias) +// CHECK-LOAD: [[DIV:%.+]] = sdiv i32 +// CHECK-LOAD-NEXT: store i32 [[DIV]], i32* +// CHECK-LOAD-NEXT: ret void +// CHECK-LOAD-NEXT: } + +// CHECK: define internal {{.*}}void @{{[^(]+}}(i32* noalias, i32* noalias) +// CHECK: [[SUB:%.+]] = sub nsw i32 11, +// CHECK-NEXT: store i32 [[SUB]], i32* +// CHECK-NEXT: ret void +// CHECK-NEXT: } +// CHECK-LOAD: define internal {{.*}}void @{{[^(]+}}(i32* noalias, i32* noalias) +// CHECK-LOAD: [[SUB:%.+]] = sub nsw i32 11, +// CHECK-LOAD-NEXT: store i32 [[SUB]], i32* +// CHECK-LOAD-NEXT: ret void +// CHECK-LOAD-NEXT: } + +#endif diff --git a/test/OpenMP/threadprivate_codegen.cpp b/test/OpenMP/threadprivate_codegen.cpp index 793f6c5808..d4b27f0751 100644 --- a/test/OpenMP/threadprivate_codegen.cpp +++ b/test/OpenMP/threadprivate_codegen.cpp @@ -948,5 +948,5 @@ int foobar() { // CHECK-TLS: call void [[ST_S4_ST_CXX_INIT]] // CHECK-TLS: [[DONE_LABEL]] -// CHECK-TLS: declare {{.*}} void [[GS3_TLS_INIT]] -// CHECK-TLS: declare {{.*}} void [[STATIC_S_TLS_INIT]] +// CHECK-TLS-DAG: declare {{.*}} void [[GS3_TLS_INIT]] +// CHECK-TLS-DAG: declare {{.*}} void [[STATIC_S_TLS_INIT]]