diff --git a/llvm/include/llvm/IR/DebugInfoMetadata.h b/llvm/include/llvm/IR/DebugInfoMetadata.h --- a/llvm/include/llvm/IR/DebugInfoMetadata.h +++ b/llvm/include/llvm/IR/DebugInfoMetadata.h @@ -2710,7 +2710,7 @@ } static DICommonBlock *getImpl(LLVMContext &Context, Metadata *Scope, Metadata *Decl, MDString *Name, Metadata *File, - unsigned LineNo, + unsigned LineNo, StorageType Storage, bool ShouldCreate = true); TempDICommonBlock cloneImpl() const { diff --git a/llvm/include/llvm/IR/OpenMPConstants.h b/llvm/include/llvm/IR/OpenMPConstants.h --- a/llvm/include/llvm/IR/OpenMPConstants.h +++ b/llvm/include/llvm/IR/OpenMPConstants.h @@ -14,11 +14,18 @@ #ifndef LLVM_OPENMP_CONSTANTS_H #define LLVM_OPENMP_CONSTANTS_H +#include "llvm/ADT/BitmaskEnum.h" #include "llvm/ADT/StringRef.h" namespace llvm { +class Type; +class Module; +class StructType; +class PointerType; +class FunctionType; namespace omp { +LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE(); /// IDs for all OpenMP directives. enum class Directive { @@ -33,12 +40,58 @@ #define OMP_DIRECTIVE(Enum, ...) constexpr auto Enum = omp::Directive::Enum; #include "llvm/IR/OpenMPKinds.def" +/// IDs for all omp runtime library (RTL) functions. +enum class RuntimeFunction { +#define OMP_RTL(Enum, ...) Enum, +#include "llvm/IR/OpenMPKinds.def" +}; + +#define OMP_RTL(Enum, ...) constexpr auto Enum = omp::RuntimeFunction::Enum; +#include "llvm/IR/OpenMPKinds.def" + +/// IDs for all omp runtime library ident_t flag encodings (see +/// their defintion in openmp/runtime/src/kmp.h). +enum class IdentFlag { +#define OMP_IDENT_FLAG(Enum, Str, Value) Enum = Value, +#include "llvm/IR/OpenMPKinds.def" + LLVM_MARK_AS_BITMASK_ENUM(0x7FFFFFFF) +}; + +#define OMP_IDENT_FLAG(Enum, ...) constexpr auto Enum = omp::IdentFlag::Enum; +#include "llvm/IR/OpenMPKinds.def" + /// Parse \p Str and return the directive it matches or OMPD_unknown if none. Directive getOpenMPDirectiveKind(StringRef Str); /// Return a textual representation of the directive \p D. StringRef getOpenMPDirectiveName(Directive D); +/// Forward declarations for LLVM-IR types (simple, function and structure) are +/// generated below. Their names are defined and used in OpenMPKinds.def. Here +/// we provide the forward declarations, the initializeTypes function will +/// provide the values. +/// +///{ +namespace types { + +#define OMP_TYPE(VarName, InitValue) extern Type *VarName; +#define OMP_FUNCTION_TYPE(VarName, IsVarArg, ReturnType, ...) \ + extern FunctionType *VarName; \ + extern PointerType *VarName##Ptr; +#define OMP_STRUCT_TYPE(VarName, StrName, ...) \ + extern StructType *VarName; \ + extern PointerType *VarName##Ptr; +#include "llvm/IR/OpenMPKinds.def" + +/// Helper to initialize all types defined in OpenMPKinds.def. +void initializeTypes(Module &M); + +/// Helper to uninitialize all types defined in OpenMPKinds.def. +void uninitializeTypes(); + +} // namespace types +///} + } // end namespace omp } // end namespace llvm diff --git a/llvm/include/llvm/IR/OpenMPIRBuilder.h b/llvm/include/llvm/IR/OpenMPIRBuilder.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/IR/OpenMPIRBuilder.h @@ -0,0 +1,125 @@ +//===- IR/OpenMPIRBuilder.h - OpenMP encoding builder for LLVM IR - C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines the OpenMPIRBuilder class and helpers used as a convenient +// way to create LLVM instructions for OpenMP directives. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_OPENMP_IR_IRBUILDER_H +#define LLVM_OPENMP_IR_IRBUILDER_H + +#include "llvm/IR/DebugLoc.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/OpenMPConstants.h" + +namespace llvm { + +/// An interface to create LLVM-IR for OpenMP directives. +/// +/// Each OpenMP directive has a corresponding public generator method. +class OpenMPIRBuilder { +public: + /// Create a new OpenMPIRBuilder operating on the given module \p M. This will + /// not have an effect on \p M (see initialize). + OpenMPIRBuilder(Module &M) : M(M), Builder(M.getContext()) {} + + /// Initialize the internal state, this will put structures types and + /// potentially other helpers into the underlying module. Must be called + /// before any other method and only once! + void initialize(); + + /// Description of a LLVM-IR insertion point (IP) and a debug/source location + /// (filename, line, column, ...). + struct LocationDescription { + LocationDescription(const IRBuilder<>::InsertPoint &IP) : IP(IP) {} + LocationDescription(const IRBuilder<>::InsertPoint &IP, const DebugLoc &DL) + : IP(IP), DL(DL) {} + IRBuilder<>::InsertPoint IP; + DebugLoc DL; + }; + + /// Emitter methods for OpenMP directives. + /// + ///{ + + /// Generator for '#omp barrier' + /// + /// \param Loc The location where the barrier directive was encountered. + /// \param DK The kind of directive that caused the barrier. + /// \param ForceSimpleCall Flag to force a simple (=non-cancelation) barrier. + /// \param CheckCancelFlag Flag to indicate a cancel barrier return value + /// should be checked and acted upon. + void CreateBarrier(const LocationDescription &Loc, omp::Directive DK, + bool ForceSimpleCall = false, bool CheckCancelFlag = true); + + ///} + +private: + /// Update the internal location to \p Loc. + void updateToLocation(const LocationDescription &Loc) { + Builder.restoreIP(Loc.IP); + Builder.SetCurrentDebugLocation(Loc.DL); + } + + /// Return the function declaration for the runtime function with \p FnID. + /// If \p Annotate is set, the return functiion is annotated with known + /// attributes. + Function *getOrCreateRuntimeFunction(omp::RuntimeFunction FnID, + bool Annotate = false); + + /// Return the (LLVM-IR) string describing the source location \p LocStr. + Constant *getOrCreateSrcLocStr(StringRef LocStr); + + /// Return the (LLVM-IR) string describing the default source location. + Constant *getOrCreateDefaultSrcLocStr(); + + /// Return the (LLVM-IR) string describing the source location \p Loc. + Constant *getOrCreateSrcLocStr(const LocationDescription &Loc); + + /// Return an ident_t* encoding the source location \p SrcLocStr and \p Flags. + Value *getOrCreateIdent(Constant *SrcLocStr, + omp::IdentFlag Flags = omp::IdentFlag(0)); + + /// Generate a barrier runtime call. + /// + /// \param Loc The location at which the request originated and is fulfilled. + /// \param DK The directive which caused the barrier + /// \param ForceSimpleCall Flag to force a simple (=non-cancelation) barrier. + /// \param CheckCancelFlag Flag to indicate a cancel barrier return value + /// should be checked and acted upon. + void emitBarrierImpl(const LocationDescription &Loc, omp::Directive DK, + bool ForceSimpleCall, bool CheckCancelFlag); + + /// Return the current thread ID. + /// + /// \param Ident The ident (ident_t*) describing the query origin. + Value *getOrCreateThreadID(Value *Ident); + + /// The underlying LLVM-IR module + Module &M; + + /// The LLVM-IR Builder used to create IR. + IRBuilder<> Builder; + + /// TODO: Stub for a cancelation block stack. + BasicBlock *CancelationBlock = nullptr; + + /// Map to remember the thread in a function. + DenseMap ThreadIDMap; + + /// Map to remember source location strings + StringMap SrcLocStrMap; + + /// Map to remember existing ident_t*. + DenseMap, GlobalVariable *> IdentMap; +}; + +} // end namespace llvm + +#endif // LLVM_IR_IRBUILDER_H diff --git a/llvm/include/llvm/IR/OpenMPKinds.def b/llvm/include/llvm/IR/OpenMPKinds.def --- a/llvm/include/llvm/IR/OpenMPKinds.def +++ b/llvm/include/llvm/IR/OpenMPKinds.def @@ -99,3 +99,130 @@ #undef OMP_DIRECTIVE ///} + +/// Types used in runtime structs or runtime functions +/// +///{ + +#ifndef OMP_TYPE +#define OMP_TYPE(VarName, InitValue) +#endif + +#define __OMP_TYPE(VarName) OMP_TYPE(VarName, Type::get##VarName##Ty(Ctx)) + +__OMP_TYPE(Void) +__OMP_TYPE(Int8) +__OMP_TYPE(Int32) +__OMP_TYPE(Int8Ptr) +__OMP_TYPE(Int32Ptr) + +#undef __OMP_TYPE +#undef OMP_TYPE + +///} + +/// Struct and function types +/// +///{ + +#ifndef OMP_STRUCT_TYPE +#define OMP_STRUCT_TYPE(VarName, StructName, ...) +#endif + +#define __OMP_STRUCT_TYPE(VarName, Name, ...) \ + OMP_STRUCT_TYPE(VarName, "struct." #Name, __VA_ARGS__) + +__OMP_STRUCT_TYPE(Ident, ident_t, Int32, Int32, Int32, Int32, Int8Ptr) + +#undef __OMP_STRUCT_TYPE +#undef OMP_STRUCT_TYPE + +#ifndef OMP_FUNCTION_TYPE +#define OMP_FUNCTION_TYPE(VarName, IsVarArg, ReturnType, ...) +#endif + +#define __OMP_FUNCTION_TYPE(VarName, IsVarArg, ReturnType, ...) \ + OMP_FUNCTION_TYPE(VarName, IsVarArg, ReturnType, __VA_ARGS__) + +__OMP_FUNCTION_TYPE(ParallelTask, true, Void, Int32Ptr, Int32Ptr) + +#undef __OMP_FUNCTION_TYPE +#undef OMP_FUNCTION_TYPE + +///} + +/// Runtime library function (and their attributes) +/// +///{ + +#ifndef OMP_RTL +#define OMP_RTL(Enum, Str, IsVarArg, ReturnType, ...) +#endif + +#define __OMP_RTL(Name, IsVarArg, ReturnType, ...) \ + OMP_RTL(OMPRTL_##Name, #Name, IsVarArg, ReturnType, __VA_ARGS__) + +__OMP_RTL(__kmpc_barrier, false, Void, IdentPtr, Int32) +__OMP_RTL(__kmpc_cancel_barrier, false, Int32, IdentPtr, Int32) +__OMP_RTL(__kmpc_global_thread_num, false, Int32, IdentPtr) +__OMP_RTL(__kmpc_fork_call, true, Void, IdentPtr, Int32, ParallelTaskPtr) +__OMP_RTL(omp_get_thread_num, false, Int32, ) + +#undef __OMP_RTL +#undef OMP_RTL + +#ifndef OMP_RTL_ATTRS +#define OMP_RTL_ATTRS(Enum, FnAttrSet, RetAttrSet, ArgAttrSets) +#endif + +#define EnumAttr(Kind) Attribute::get(Ctx, Attribute::AttrKind::Kind) +#define AttributeSet(...) \ + AttributeSet::get(Ctx, ArrayRef({__VA_ARGS__})) + +#define __OMP_RTL_ATTRS(Name, FnAttrSet, RetAttrSet, ArgAttrSets) \ + OMP_RTL_ATTRS(OMPRTL_##Name, FnAttrSet, RetAttrSet, ArgAttrSets) + +__OMP_RTL_ATTRS(__kmpc_global_thread_num, + AttributeSet(EnumAttr(InaccessibleMemOrArgMemOnly), + EnumAttr(ReadOnly), EnumAttr(NoUnwind), + EnumAttr(NoFree), EnumAttr(NoSync)), + AttributeSet(), {}) +__OMP_RTL_ATTRS(omp_get_thread_num, + AttributeSet(EnumAttr(InaccessibleMemOnly), EnumAttr(ReadOnly), + EnumAttr(NoUnwind), EnumAttr(NoFree), + EnumAttr(NoSync)), + AttributeSet(), {}) + +#undef __OMP_RTL_ATTRS +#undef OMP_RTL_ATTRS +#undef AttributeSet +#undef EnumAttr + +///} + +/// KMP ident_t bit flags +/// +/// In accordance with the values in `openmp/runtime/src/kmp.h`. +/// +///{ + +#ifndef OMP_IDENT_FLAG +#define OMP_IDENT_FLAG(Enum, Str, Value) +#endif + +#define __OMP_IDENT_FLAG(Name, Value) \ + OMP_IDENT_FLAG(OMP_IDENT_FLAG_##Name, #Name, Value) + +__OMP_IDENT_FLAG(KMPC, 0x02) +__OMP_IDENT_FLAG(BARRIER_EXPL, 0x20) +__OMP_IDENT_FLAG(BARRIER_IMPL, 0x0040) +__OMP_IDENT_FLAG(BARRIER_IMPL_MASK, 0x01C0) +__OMP_IDENT_FLAG(BARRIER_IMPL_FOR, 0x0040) +__OMP_IDENT_FLAG(BARRIER_IMPL_SECTIONS, 0x00C0) +__OMP_IDENT_FLAG(BARRIER_IMPL_SINGLE, 0x0140) +__OMP_IDENT_FLAG(BARRIER_IMPL_WORKSHARE, 0x01C0) + +#undef __OMP_IDENT_FLAG +#undef OMP_IDENT_FLAG + +///} diff --git a/llvm/lib/IR/CMakeLists.txt b/llvm/lib/IR/CMakeLists.txt --- a/llvm/lib/IR/CMakeLists.txt +++ b/llvm/lib/IR/CMakeLists.txt @@ -40,6 +40,7 @@ Module.cpp ModuleSummaryIndex.cpp OpenMPConstants.cpp + OpenMPIRBuilder.cpp Operator.cpp OptBisect.cpp Pass.cpp diff --git a/llvm/lib/IR/OpenMPConstants.cpp b/llvm/lib/IR/OpenMPConstants.cpp --- a/llvm/lib/IR/OpenMPConstants.cpp +++ b/llvm/lib/IR/OpenMPConstants.cpp @@ -12,9 +12,12 @@ #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSwitch.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/Type.h" using namespace llvm; using namespace omp; +using namespace types; Directive llvm::omp::getOpenMPDirectiveKind(StringRef Str) { return llvm::StringSwitch(Str) @@ -32,3 +35,53 @@ } llvm_unreachable("Invalid OpenMP directive kind"); } + +/// Declarations for LLVM-IR types (simple, function and structure) are +/// generated below. Their names are defined and used in OpenMPKinds.def. Here +/// we provide the declarations, the initializeTypes function will provide the +/// values. +/// +///{ + +#define OMP_TYPE(VarName, InitValue) Type *llvm::omp::types::VarName = nullptr; +#define OMP_FUNCTION_TYPE(VarName, IsVarArg, ReturnType, ...) \ + FunctionType *llvm::omp::types::VarName = nullptr; \ + PointerType *llvm::omp::types::VarName##Ptr = nullptr; +#define OMP_STRUCT_TYPE(VarName, StrName, ...) \ + StructType *llvm::omp::types::VarName = nullptr; \ + PointerType *llvm::omp::types::VarName##Ptr = nullptr; +#include "llvm/IR/OpenMPKinds.def" + +///} + +void llvm::omp::types::initializeTypes(Module &M) { + if (Void) + return; + + LLVMContext &Ctx = M.getContext(); + // Create all simple and struct types exposed by the runtime and remember + // the llvm::PointerTypes of them for easy access later. + StructType *T; +#define OMP_TYPE(VarName, InitValue) VarName = InitValue; +#define OMP_FUNCTION_TYPE(VarName, IsVarArg, ReturnType, ...) \ + VarName = FunctionType::get(ReturnType, {__VA_ARGS__}, IsVarArg); \ + VarName##Ptr = PointerType::getUnqual(T); +#define OMP_STRUCT_TYPE(VarName, StructName, ...) \ + T = M.getTypeByName(StructName); \ + if (!T) \ + T = StructType::create(Ctx, {__VA_ARGS__}, StructName); \ + VarName = T; \ + VarName##Ptr = PointerType::getUnqual(T); +#include "llvm/IR/OpenMPKinds.def" +} + +void llvm::omp::types::uninitializeTypes() { +#define OMP_TYPE(VarName, InitValue) VarName = nullptr; +#define OMP_FUNCTION_TYPE(VarName, IsVarArg, ReturnType, ...) \ + VarName = nullptr; \ + VarName##Ptr = nullptr; +#define OMP_STRUCT_TYPE(VarName, StrName, ...) \ + VarName = nullptr; \ + VarName##Ptr = nullptr; +#include "llvm/IR/OpenMPKinds.def" +} diff --git a/llvm/lib/IR/OpenMPIRBuilder.cpp b/llvm/lib/IR/OpenMPIRBuilder.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/IR/OpenMPIRBuilder.cpp @@ -0,0 +1,234 @@ +//===- OpenMPIRBuilder.cpp - Builder for LLVM-IR for OpenMP directives ----===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// \file +/// +/// This file implements the OpenMPIRBuilder class, which is used as a +/// convenient way to create LLVM instructions for OpenMP directives. +/// +//===----------------------------------------------------------------------===// + +#include "llvm/IR/OpenMPIRBuilder.h" + +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/IR/DebugInfo.h" +#include "llvm/IR/IRBuilder.h" + +#define DEBUG_TYPE "openmp-ir-builder" + +using namespace llvm; +using namespace omp; +using namespace types; + +Function *OpenMPIRBuilder::getOrCreateRuntimeFunction(RuntimeFunction FnID, + bool Annotate) { + Function *Fn = nullptr; + + // Try to find the declation in the module first. + switch (FnID) { +#define OMP_RTL(Enum, Str, IsVarArg, ReturnType, ...) \ + case Enum: \ + Fn = M.getFunction(Str); \ + break; +#include "llvm/IR/OpenMPKinds.def" + } + + if (!Fn) { + // Create a new declaration if we need one. + switch (FnID) { +#define OMP_RTL(Enum, Str, IsVarArg, ReturnType, ...) \ + case Enum: \ + Fn = Function::Create(FunctionType::get(ReturnType, \ + ArrayRef{__VA_ARGS__}, \ + IsVarArg), \ + GlobalValue::ExternalLinkage, Str, M); \ + break; +#include "llvm/IR/OpenMPKinds.def" + } + } + + assert(Fn && "Failed to create OpenMP runtime function"); + + if (Annotate) { + LLVMContext &Ctx = Fn->getContext(); + // Add attributes to the new declaration. + switch (FnID) { +#define OMP_RTL_ATTRS(Enum, FnAttrSet, RetAttrSet, ArgAttrSets) \ + case Enum: \ + Fn->setAttributes( \ + AttributeList::get(Ctx, FnAttrSet, RetAttrSet, ArgAttrSets)); \ + break; +#include "llvm/IR/OpenMPKinds.def" + default: + // Attributes are optional. + break; + } + } + + return Fn; +} + +void OpenMPIRBuilder::initialize() { + initializeTypes(M); +#define OMP_RTL(Enum, ...) \ + getOrCreateRuntimeFunction(Enum, /* Annotate = */ true); +#include "llvm/IR/OpenMPKinds.def" +} + +Value *OpenMPIRBuilder::getOrCreateIdent(Constant *SrcLocStr, + IdentFlag LocFlags) { + // Enable "C-mode". + LocFlags |= OMP_IDENT_FLAG_KMPC; + + GlobalVariable *&DefaultIdent = IdentMap[{SrcLocStr, uint64_t(LocFlags)}]; + if (!DefaultIdent) { + Constant *I32Null = ConstantInt::getNullValue(Int32); + Constant *IdentData[] = {I32Null, + ConstantInt::get(Int32, uint64_t(LocFlags)), + I32Null, I32Null, SrcLocStr}; + Constant *Initializer = ConstantStruct::get( + cast(IdentPtr->getPointerElementType()), IdentData); + + // Look for existing encoding of the location + flags, not needed but + // minimizes the difference to the existing solution while we transition. + for (GlobalVariable &GV : M.getGlobalList()) + if (GV.getType() == IdentPtr && GV.hasInitializer()) + if (GV.getInitializer() == Initializer) + return DefaultIdent = &GV; + + DefaultIdent = new GlobalVariable(M, IdentPtr->getPointerElementType(), + /* isConstant = */ false, + GlobalValue::PrivateLinkage, Initializer); + DefaultIdent->setUnnamedAddr(GlobalValue::UnnamedAddr::Global); + DefaultIdent->setAlignment(Align(8)); + } + return DefaultIdent; +} + +Constant *OpenMPIRBuilder::getOrCreateSrcLocStr(StringRef LocStr) { + Constant *&SrcLocStr = SrcLocStrMap[LocStr]; + if (!SrcLocStr) { + Constant *Initializer = + ConstantDataArray::getString(M.getContext(), LocStr); + + // Look for existing encoding of the location, not needed but minimizes the + // difference to the existing solution while we transition. + for (GlobalVariable &GV : M.getGlobalList()) + if (GV.isConstant() && GV.hasInitializer() && + GV.getInitializer() == Initializer) + return SrcLocStr = ConstantExpr::getPointerCast(&GV, Int8Ptr); + + SrcLocStr = Builder.CreateGlobalStringPtr(LocStr); + } + return SrcLocStr; +} + +Constant *OpenMPIRBuilder::getOrCreateDefaultSrcLocStr() { + return getOrCreateSrcLocStr(";unknown;unknown;0;0;;"); +} + +Constant * +OpenMPIRBuilder::getOrCreateSrcLocStr(const LocationDescription &Loc) { + DILocation *DIL = Loc.DL.get(); + if (!DIL) + return getOrCreateDefaultSrcLocStr(); + StringRef Filename = + !DIL->getFilename().empty() ? DIL->getFilename() : M.getName(); + StringRef Function = DIL->getScope()->getSubprogram()->getName(); + Function = + !Function.empty() ? Function : Loc.IP.getBlock()->getParent()->getName(); + std::string LineStr = std::to_string(DIL->getLine()); + std::string ColumnStr = std::to_string(DIL->getColumn()); + std::string SrcLocStr = (";" + Filename + ";" + Function).str() + ";" + + LineStr + ";" + ColumnStr + ";;"; + return getOrCreateSrcLocStr(SrcLocStr); +} + +Value *OpenMPIRBuilder::getOrCreateThreadID(Value *Ident) { + // TODO: It makes only so much sense to actually cache the global_thread_num + // calls in the front-end as we can do a better job later on. Once + // the middle-end combines global_thread_num calls (user calls and + // generated ones!) we can rethink having a caching scheme here. + Function *Fn = Builder.GetInsertBlock()->getParent(); + Value *&TID = ThreadIDMap[Fn]; + if (!TID) { + // Search the entry block, not needed once all thread id calls go through + // here and are cached in the OpenMPIRBuilder. + for (Instruction &I : Fn->getEntryBlock()) + if (CallInst *CI = dyn_cast(&I)) + if (CI->getCalledFunction() && + CI->getCalledFunction()->getName() == "__kmpc_global_thread_num") + return TID = CI; + + Function *FnDecl = + getOrCreateRuntimeFunction(OMPRTL___kmpc_global_thread_num); + Instruction *Call = + Builder.CreateCall(FnDecl, Ident, "omp_global_thread_num"); + if (Instruction *IdentI = dyn_cast(Ident)) + Call->moveAfter(IdentI); + else + Call->moveBefore(&*Fn->getEntryBlock().getFirstInsertionPt()); + TID = Call; + } + return TID; +} + +void OpenMPIRBuilder::CreateBarrier(const LocationDescription &Loc, + Directive DK, bool ForceSimpleCall, + bool CheckCancelFlag) { + // TODO: Do we really expect these create calls to happen at an invalid + // location and if so is ignoring them the right thing to do? This + // mimics Clang's behavior for now. + if (!Loc.IP.getBlock()) + return; + updateToLocation(Loc); + emitBarrierImpl(Loc, DK, ForceSimpleCall, CheckCancelFlag); +} + +void OpenMPIRBuilder::emitBarrierImpl(const LocationDescription &Loc, + Directive Kind, bool ForceSimpleCall, + bool CheckCancelFlag) { + // Build call __kmpc_cancel_barrier(loc, thread_id) or + // __kmpc_barrier(loc, thread_id); + + IdentFlag BarrierLocFlags; + switch (Kind) { + case OMPD_for: + BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_IMPL_FOR; + break; + case OMPD_sections: + BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_IMPL_SECTIONS; + break; + case OMPD_single: + BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_IMPL_SINGLE; + break; + case OMPD_barrier: + BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_EXPL; + break; + default: + BarrierLocFlags = OMP_IDENT_FLAG_BARRIER_IMPL; + break; + } + + // Set new insertion point for the internal builder. + Constant *SrcLocStr = getOrCreateSrcLocStr(Loc); + Value *Args[] = {getOrCreateIdent(SrcLocStr, BarrierLocFlags), + getOrCreateThreadID(getOrCreateIdent(SrcLocStr))}; + bool UseCancelBarrier = !ForceSimpleCall && CancelationBlock; + Value *Result = Builder.CreateCall( + getOrCreateRuntimeFunction(UseCancelBarrier ? OMPRTL___kmpc_cancel_barrier + : OMPRTL___kmpc_barrier), + Args); + + if (UseCancelBarrier && CheckCancelFlag) { + Value *Cmp = Builder.CreateIsNotNull(Result); + // TODO Reimplement part of llvm::SplitBlockAndInsertIfThen in a helper and + // use it here. + (void)Cmp; + } +} diff --git a/llvm/unittests/IR/CMakeLists.txt b/llvm/unittests/IR/CMakeLists.txt --- a/llvm/unittests/IR/CMakeLists.txt +++ b/llvm/unittests/IR/CMakeLists.txt @@ -28,6 +28,7 @@ ManglerTest.cpp MetadataTest.cpp ModuleTest.cpp + OpenMPIRBuilderTest.cpp PassManagerTest.cpp PatternMatch.cpp TimePassesTest.cpp diff --git a/llvm/unittests/IR/IRBuilderTest.cpp b/llvm/unittests/IR/IRBuilderTest.cpp --- a/llvm/unittests/IR/IRBuilderTest.cpp +++ b/llvm/unittests/IR/IRBuilderTest.cpp @@ -198,12 +198,12 @@ ASSERT_TRUE(isa(V)); II = cast(V); EXPECT_EQ(II->getIntrinsicID(), Intrinsic::experimental_constrained_fmul); - + V = Builder.CreateFDiv(V, V); ASSERT_TRUE(isa(V)); II = cast(V); EXPECT_EQ(II->getIntrinsicID(), Intrinsic::experimental_constrained_fdiv); - + V = Builder.CreateFRem(V, V); ASSERT_TRUE(isa(V)); II = cast(V); @@ -467,7 +467,7 @@ ASSERT_TRUE(isa(F)); FDiv = cast(F); EXPECT_FALSE(FDiv->hasAllowReciprocal()); - + // Try individual flags. FMF.clear(); FMF.setAllowReciprocal(); @@ -526,7 +526,7 @@ EXPECT_TRUE(FAdd->hasApproxFunc()); EXPECT_TRUE(FAdd->hasAllowContract()); EXPECT_FALSE(FAdd->hasAllowReassoc()); - + FMF.setAllowReassoc(); Builder.clearFastMathFlags(); Builder.setFastMathFlags(FMF); diff --git a/llvm/unittests/IR/OpenMPIRBuilderTest.cpp b/llvm/unittests/IR/OpenMPIRBuilderTest.cpp new file mode 100644 --- /dev/null +++ b/llvm/unittests/IR/OpenMPIRBuilderTest.cpp @@ -0,0 +1,129 @@ +//===- llvm/unittest/IR/OpenMPIRBuilderTest.cpp - OpenMPIRBuilder tests ---===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/IR/OpenMPIRBuilder.h" +#include "llvm/IR/BasicBlock.h" +#include "llvm/IR/DIBuilder.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/OpenMPConstants.h" +#include "llvm/IR/Verifier.h" +#include "gtest/gtest.h" + +using namespace llvm; +using namespace omp; +using namespace types; + +namespace { + +class OpenMPIRBuilderTest : public testing::Test { +protected: + void SetUp() override { + M.reset(new Module("MyModule", Ctx)); + FunctionType *FTy = FunctionType::get(Type::getVoidTy(Ctx), + /*isVarArg=*/false); + F = Function::Create(FTy, Function::ExternalLinkage, "", M.get()); + BB = BasicBlock::Create(Ctx, "", F); + } + + void TearDown() override { + BB = nullptr; + M.reset(); + uninitializeTypes(); + } + + LLVMContext Ctx; + std::unique_ptr M; + Function *F; + BasicBlock *BB; +}; + +TEST_F(OpenMPIRBuilderTest, CreateBarrier) { + OpenMPIRBuilder OMPBuilder(*M); + OMPBuilder.initialize(); + + IRBuilder<> Builder(BB); + + OMPBuilder.CreateBarrier({IRBuilder<>::InsertPoint()}, OMPD_for); + EXPECT_TRUE(M->global_empty()); + EXPECT_EQ(M->size(), 6U); + EXPECT_EQ(F->size(), 1U); + EXPECT_EQ(BB->size(), 0U); + + OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP()}); + OMPBuilder.CreateBarrier(Loc, OMPD_for); + EXPECT_FALSE(M->global_empty()); + EXPECT_EQ(M->size(), 6U); + EXPECT_EQ(F->size(), 1U); + EXPECT_EQ(BB->size(), 2U); + + CallInst *GTID = dyn_cast(&BB->front()); + EXPECT_NE(GTID, nullptr); + EXPECT_EQ(GTID->getNumArgOperands(), 1U); + EXPECT_EQ(GTID->getCalledFunction()->getName(), "__kmpc_global_thread_num"); + + CallInst *Barrier = dyn_cast(GTID->getNextNode()); + EXPECT_NE(Barrier, nullptr); + EXPECT_EQ(Barrier->getNumArgOperands(), 2U); + EXPECT_EQ(Barrier->getCalledFunction()->getName(), "__kmpc_barrier"); + + EXPECT_EQ(cast(Barrier)->getArgOperand(1), GTID); + + Builder.CreateUnreachable(); + EXPECT_FALSE(verifyModule(*M)); +} + +TEST_F(OpenMPIRBuilderTest, DbgLoc) { + OpenMPIRBuilder OMPBuilder(*M); + OMPBuilder.initialize(); + F->setName("func"); + + IRBuilder<> Builder(BB); + + DIBuilder DIB(*M); + auto File = DIB.createFile("test.dbg", "/"); + auto CU = + DIB.createCompileUnit(dwarf::DW_LANG_C, File, "llvm-C", true, "", 0); + auto Type = DIB.createSubroutineType(DIB.getOrCreateTypeArray(None)); + auto SP = DIB.createFunction( + CU, "foo", "", File, 1, Type, 1, DINode::FlagZero, + DISubprogram::SPFlagDefinition | DISubprogram::SPFlagOptimized); + F->setSubprogram(SP); + auto Scope = DIB.createLexicalBlockFile(SP, File, 0); + DIB.finalize(); + + DebugLoc DL = DebugLoc::get(3, 7, Scope); + OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP(), DL}); + OMPBuilder.CreateBarrier(Loc, OMPD_for); + CallInst *GTID = dyn_cast(&BB->front()); + CallInst *Barrier = dyn_cast(GTID->getNextNode()); + EXPECT_EQ(GTID->getDebugLoc(), DL); + EXPECT_EQ(Barrier->getDebugLoc(), DL); + EXPECT_TRUE(isa(Barrier->getOperand(0))); + if (!isa(Barrier->getOperand(0))) + return; + GlobalVariable *Ident = cast(Barrier->getOperand(0)); + EXPECT_TRUE(Ident->hasInitializer()); + if (!Ident->hasInitializer()) + return; + Constant *Initializer = Ident->getInitializer(); + EXPECT_TRUE( + isa(Initializer->getOperand(4)->stripPointerCasts())); + GlobalVariable *SrcStrGlob = + cast(Initializer->getOperand(4)->stripPointerCasts()); + if (!SrcStrGlob) + return; + EXPECT_TRUE(isa(SrcStrGlob->getInitializer())); + ConstantDataArray *SrcSrc = + dyn_cast(SrcStrGlob->getInitializer()); + if (!SrcSrc) + return; + EXPECT_EQ(SrcSrc->getAsCString(), ";test.dbg;foo;3;7;;"); +} +} // namespace