diff --git a/llvm/include/llvm/Frontend/OpenMP/OMPConstants.h b/llvm/include/llvm/Frontend/OpenMP/OMPConstants.h --- a/llvm/include/llvm/Frontend/OpenMP/OMPConstants.h +++ b/llvm/include/llvm/Frontend/OpenMP/OMPConstants.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/Frontend/OpenMP/OMPKinds.def" +/// IDs for all omp runtime library (RTL) functions. +enum class RuntimeFunction { +#define OMP_RTL(Enum, ...) Enum, +#include "llvm/Frontend/OpenMP/OMPKinds.def" +}; + +#define OMP_RTL(Enum, ...) constexpr auto Enum = omp::RuntimeFunction::Enum; +#include "llvm/Frontend/OpenMP/OMPKinds.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/Frontend/OpenMP/OMPKinds.def" + LLVM_MARK_AS_BITMASK_ENUM(0x7FFFFFFF) +}; + +#define OMP_IDENT_FLAG(Enum, ...) constexpr auto Enum = omp::IdentFlag::Enum; +#include "llvm/Frontend/OpenMP/OMPKinds.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/Frontend/OpenMP/OMPKinds.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/Frontend/OpenMP/OMPIRBuilder.h b/llvm/include/llvm/Frontend/OpenMP/OMPIRBuilder.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/Frontend/OpenMP/OMPIRBuilder.h @@ -0,0 +1,141 @@ +//===- 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/Frontend/OpenMP/OMPConstants.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(); + + /// Add attributes known for \p FnID to \p Fn. + void addAttributes(omp::RuntimeFunction FnID, Function &Fn); + + /// Set the cancellation block to \p CBB. + void setCancellationBlock(BasicBlock *CBB) { CancellationBlock = CBB; } + + /// Type used throughout for insertion points. + using InsertPointTy = IRBuilder<>::InsertPoint; + + /// Description of a LLVM-IR insertion point (IP) and a debug/source location + /// (filename, line, column, ...). + struct LocationDescription { + template + LocationDescription(const IRBuilder &IRB) + : IP(IRB.saveIP()), DL(IRB.getCurrentDebugLocation()) {} + LocationDescription(const InsertPointTy &IP) : IP(IP) {} + LocationDescription(const InsertPointTy &IP, const DebugLoc &DL) + : IP(IP), DL(DL) {} + InsertPointTy 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-cancellation) barrier. + /// \param CheckCancelFlag Flag to indicate a cancel barrier return value + /// should be checked and acted upon. + /// + /// \returns The insertion point after the barrier. + InsertPointTy CreateBarrier(const LocationDescription &Loc, omp::Directive DK, + bool ForceSimpleCall = false, + bool CheckCancelFlag = true); + + ///} + +private: + /// Update the internal location to \p Loc. + bool updateToLocation(const LocationDescription &Loc) { + Builder.restoreIP(Loc.IP); + Builder.SetCurrentDebugLocation(Loc.DL); + return Loc.IP.getBlock() != nullptr; + } + + /// Return the function declaration for the runtime function with \p FnID. + Function *getOrCreateRuntimeFunction(omp::RuntimeFunction FnID); + + /// 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-cancellation) barrier. + /// \param CheckCancelFlag Flag to indicate a cancel barrier return value + /// should be checked and acted upon. + /// + /// \returns The insertion point after the barrier. + InsertPointTy 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 cancellation block stack. + BasicBlock *CancellationBlock = 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/Frontend/OpenMP/OMPKinds.def b/llvm/include/llvm/Frontend/OpenMP/OMPKinds.def --- a/llvm/include/llvm/Frontend/OpenMP/OMPKinds.def +++ b/llvm/include/llvm/Frontend/OpenMP/OMPKinds.def @@ -100,3 +100,137 @@ #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 + +#define EnumAttr(Kind) Attribute::get(Ctx, Attribute::AttrKind::Kind) +#define AttributeSet(...) \ + AttributeSet::get(Ctx, ArrayRef({__VA_ARGS__})) + +#ifndef OMP_ATTRS_SET +#define OMP_ATTRS_SET(VarName, AttrSet) +#endif + +#define __OMP_ATTRS_SET(VarName, AttrSet) OMP_ATTRS_SET(VarName, AttrSet) + +__OMP_ATTRS_SET(GetterAttrs, + OptimisticAttributes + ? AttributeSet(EnumAttr(NoUnwind), EnumAttr(ReadOnly), + EnumAttr(NoSync), EnumAttr(NoFree)) + : AttributeSet(EnumAttr(NoUnwind))) + +#undef __OMP_ATTRS_SET +#undef OMP_ATTRS_SET + +#ifndef OMP_RTL_ATTRS +#define OMP_RTL_ATTRS(Enum, FnAttrSet, RetAttrSet, ArgAttrSets) +#endif + +#define __OMP_RTL_ATTRS(Name, FnAttrSet, RetAttrSet, ArgAttrSets) \ + OMP_RTL_ATTRS(OMPRTL_##Name, FnAttrSet, RetAttrSet, ArgAttrSets) + +__OMP_RTL_ATTRS(__kmpc_global_thread_num, GetterAttrs, AttributeSet(), {}) +__OMP_RTL_ATTRS(omp_get_thread_num, GetterAttrs, 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/Frontend/OpenMP/CMakeLists.txt b/llvm/lib/Frontend/OpenMP/CMakeLists.txt --- a/llvm/lib/Frontend/OpenMP/CMakeLists.txt +++ b/llvm/lib/Frontend/OpenMP/CMakeLists.txt @@ -1,9 +1,10 @@ add_llvm_component_library(LLVMFrontendOpenMP OMPConstants.cpp + OMPIRBuilder.cpp ADDITIONAL_HEADER_DIRS ${LLVM_MAIN_INCLUDE_DIR}/llvm/Frontend - ${LLVM_MAIN_INCLUDE_DIR}/llvm/Frontend/OpenMP + ${LLVM_MAIN_INCLUDE_DIR}/llvm/Frontend/OpenMP/OMP DEPENDS intrinsics_gen diff --git a/llvm/lib/Frontend/OpenMP/OMPConstants.cpp b/llvm/lib/Frontend/OpenMP/OMPConstants.cpp --- a/llvm/lib/Frontend/OpenMP/OMPConstants.cpp +++ b/llvm/lib/Frontend/OpenMP/OMPConstants.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/Frontend/OpenMP/OMPKinds.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/Frontend/OpenMP/OMPKinds.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/Frontend/OpenMP/OMPKinds.def" +} diff --git a/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp b/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp @@ -0,0 +1,256 @@ +//===- 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/Frontend/OpenMP/OMPIRBuilder.h" + +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/IR/DebugInfo.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Transforms/Utils/BasicBlockUtils.h" + +#include + +#define DEBUG_TYPE "openmp-ir-builder" + +using namespace llvm; +using namespace omp; +using namespace types; + +static cl::opt + OptimisticAttributes("openmp-ir-builder-optimistic-attributes", cl::Hidden, + cl::desc("Use optimistic attributes describing " + "'as-if' properties of runtime calls."), + cl::init(false)); + +void OpenMPIRBuilder::addAttributes(omp::RuntimeFunction FnID, Function &Fn) { + LLVMContext &Ctx = Fn.getContext(); + +#define OMP_ATTRS_SET(VarName, AttrSet) AttributeSet VarName = AttrSet; +#include "llvm/Frontend/OpenMP/OMPKinds.def" + + // 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/Frontend/OpenMP/OMPKinds.def" + default: + // Attributes are optional. + break; + } +} + +Function *OpenMPIRBuilder::getOrCreateRuntimeFunction(RuntimeFunction FnID) { + 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/Frontend/OpenMP/OMPKinds.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/Frontend/OpenMP/OMPKinds.def" + } + + addAttributes(FnID, *Fn); + } + + assert(Fn && "Failed to create OpenMP runtime function"); + return Fn; +} + +void OpenMPIRBuilder::initialize() { initializeTypes(M); } + +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::stringstream SrcLocStr; + SrcLocStr << ";" << Filename.data() << ";" << Function.data() << ";" + << LineStr << ";" << ColumnStr << ";;"; + return getOrCreateSrcLocStr(SrcLocStr.str()); +} + +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) { + Function *FnDecl = + getOrCreateRuntimeFunction(OMPRTL___kmpc_global_thread_num); + Instruction *Call = + Builder.CreateCall(FnDecl, Ident, "omp_global_thread_num"); + if (auto *IdentI = dyn_cast(Ident)) + Call->moveAfter(IdentI); + else + Call->moveBefore(&*Fn->getEntryBlock().getFirstInsertionPt()); + TID = Call; + } + return TID; +} + +OpenMPIRBuilder::InsertPointTy +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 (!updateToLocation(Loc)) + return Loc.IP; + return emitBarrierImpl(Loc, DK, ForceSimpleCall, CheckCancelFlag); +} + +OpenMPIRBuilder::InsertPointTy +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; + } + + Constant *SrcLocStr = getOrCreateSrcLocStr(Loc); + Value *Args[] = {getOrCreateIdent(SrcLocStr, BarrierLocFlags), + getOrCreateThreadID(getOrCreateIdent(SrcLocStr))}; + + // If we are in a cancellable parallel region, barriers are cancellation + // points. + // TODO: Check why we would force simple calls or to ignore the cancel flag. + bool UseCancelBarrier = !ForceSimpleCall && CancellationBlock; + + Value *Result = Builder.CreateCall( + getOrCreateRuntimeFunction(UseCancelBarrier ? OMPRTL___kmpc_cancel_barrier + : OMPRTL___kmpc_barrier), + Args); + + if (UseCancelBarrier && CheckCancelFlag) { + // For a cancel barrier we create two new blocks. + BasicBlock *BB = Builder.GetInsertBlock(); + BasicBlock *NonCancellationBlock = BasicBlock::Create( + BB->getContext(), BB->getName() + ".cont", BB->getParent()); + BasicBlock *CancellationBlock = BasicBlock::Create( + BB->getContext(), BB->getName() + ".cncl", BB->getParent()); + + // Jump to them based on the return value. + Value *Cmp = Builder.CreateIsNull(Result); + Builder.CreateCondBr(Cmp, NonCancellationBlock, CancellationBlock, + /* TODO weight */ nullptr, nullptr); + + Builder.SetInsertPoint(NonCancellationBlock); + assert(CancellationBlock->getParent() == BB->getParent() && + "Unexpected cancellation block parent!"); + + // TODO: This is a workaround for now, we always reset the cancellation + // block until we manage it ourselves here. + CancellationBlock = nullptr; + } + + return Builder.saveIP(); +} diff --git a/llvm/unittests/CMakeLists.txt b/llvm/unittests/CMakeLists.txt --- a/llvm/unittests/CMakeLists.txt +++ b/llvm/unittests/CMakeLists.txt @@ -18,6 +18,7 @@ add_subdirectory(DebugInfo) add_subdirectory(Demangle) add_subdirectory(ExecutionEngine) +add_subdirectory(Frontend) add_subdirectory(FuzzMutate) add_subdirectory(IR) add_subdirectory(LineEditor) diff --git a/llvm/unittests/Frontend/CMakeLists.txt b/llvm/unittests/Frontend/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/llvm/unittests/Frontend/CMakeLists.txt @@ -0,0 +1,13 @@ +set(LLVM_LINK_COMPONENTS + Analysis + Core + FrontendOpenMP + Support + Passes + ) + +add_llvm_unittest(LLVMFrontendTests + OpenMPIRBuilderTest.cpp + ) + +target_link_libraries(LLVMFrontendTests PRIVATE LLVMTestingSupport) diff --git a/llvm/unittests/Frontend/OpenMPIRBuilderTest.cpp b/llvm/unittests/Frontend/OpenMPIRBuilderTest.cpp new file mode 100644 --- /dev/null +++ b/llvm/unittests/Frontend/OpenMPIRBuilderTest.cpp @@ -0,0 +1,178 @@ +//===- 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/Frontend/OpenMP/OMPIRBuilder.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/Frontend/OpenMP/OMPConstants.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), {Type::getInt32Ty(Ctx)}, + /*isVarArg=*/false); + F = Function::Create(FTy, Function::ExternalLinkage, "", M.get()); + BB = BasicBlock::Create(Ctx, "", F); + + 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(); + DL = DebugLoc::get(3, 7, Scope); + } + + void TearDown() override { + BB = nullptr; + M.reset(); + uninitializeTypes(); + } + + LLVMContext Ctx; + std::unique_ptr M; + Function *F; + BasicBlock *BB; + DebugLoc DL; +}; + +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(), 1U); + 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(), 3U); + 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"); + EXPECT_FALSE(GTID->getCalledFunction()->doesNotAccessMemory()); + EXPECT_FALSE(GTID->getCalledFunction()->doesNotFreeMemory()); + + CallInst *Barrier = dyn_cast(GTID->getNextNode()); + EXPECT_NE(Barrier, nullptr); + EXPECT_EQ(Barrier->getNumArgOperands(), 2U); + EXPECT_EQ(Barrier->getCalledFunction()->getName(), "__kmpc_barrier"); + EXPECT_FALSE(Barrier->getCalledFunction()->doesNotAccessMemory()); + EXPECT_FALSE(Barrier->getCalledFunction()->doesNotFreeMemory()); + + EXPECT_EQ(cast(Barrier)->getArgOperand(1), GTID); + + Builder.CreateUnreachable(); + EXPECT_FALSE(verifyModule(*M)); +} + +TEST_F(OpenMPIRBuilderTest, CreateCancelBarrier) { + OpenMPIRBuilder OMPBuilder(*M); + OMPBuilder.initialize(); + + BasicBlock *CBB = BasicBlock::Create(Ctx, "", F); + new UnreachableInst(Ctx, CBB); + OMPBuilder.setCancellationBlock(CBB); + + IRBuilder<> Builder(BB); + + OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP()}); + auto NewIP = OMPBuilder.CreateBarrier(Loc, OMPD_for); + Builder.restoreIP(NewIP); + EXPECT_FALSE(M->global_empty()); + EXPECT_EQ(M->size(), 3U); + EXPECT_EQ(F->size(), 3U); + EXPECT_EQ(BB->size(), 4U); + + CallInst *GTID = dyn_cast(&BB->front()); + EXPECT_NE(GTID, nullptr); + EXPECT_EQ(GTID->getNumArgOperands(), 1U); + EXPECT_EQ(GTID->getCalledFunction()->getName(), "__kmpc_global_thread_num"); + EXPECT_FALSE(GTID->getCalledFunction()->doesNotAccessMemory()); + EXPECT_FALSE(GTID->getCalledFunction()->doesNotFreeMemory()); + + CallInst *Barrier = dyn_cast(GTID->getNextNode()); + EXPECT_NE(Barrier, nullptr); + EXPECT_EQ(Barrier->getNumArgOperands(), 2U); + EXPECT_EQ(Barrier->getCalledFunction()->getName(), "__kmpc_cancel_barrier"); + EXPECT_FALSE(Barrier->getCalledFunction()->doesNotAccessMemory()); + EXPECT_FALSE(Barrier->getCalledFunction()->doesNotFreeMemory()); + EXPECT_EQ(Barrier->getNumUses(), 1U); + Instruction *BarrierBBTI = Barrier->getParent()->getTerminator(); + EXPECT_EQ(BarrierBBTI->getNumSuccessors(), 2U); + EXPECT_EQ(BarrierBBTI->getSuccessor(0), NewIP.getBlock()); + EXPECT_EQ(BarrierBBTI->getSuccessor(1), CBB); + + 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); + + 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