diff --git a/clang/include/clang/Basic/OpenMPKinds.h b/clang/include/clang/Basic/OpenMPKinds.h --- a/clang/include/clang/Basic/OpenMPKinds.h +++ b/clang/include/clang/Basic/OpenMPKinds.h @@ -20,12 +20,10 @@ /// OpenMP directives. enum OpenMPDirectiveKind { -#define OPENMP_DIRECTIVE(Name) \ - OMPD_##Name, #define OPENMP_DIRECTIVE_EXT(Name, Str) \ OMPD_##Name, #include "clang/Basic/OpenMPKinds.def" - OMPD_unknown + OMPD_unknown, }; /// OpenMP clauses. diff --git a/clang/include/clang/Basic/OpenMPKinds.def b/clang/include/clang/Basic/OpenMPKinds.def --- a/clang/include/clang/Basic/OpenMPKinds.def +++ b/clang/include/clang/Basic/OpenMPKinds.def @@ -11,12 +11,12 @@ /// //===----------------------------------------------------------------------===// -#ifndef OPENMP_DIRECTIVE -# define OPENMP_DIRECTIVE(Name) -#endif #ifndef OPENMP_DIRECTIVE_EXT #define OPENMP_DIRECTIVE_EXT(Name, Str) #endif +#ifndef OPENMP_DIRECTIVE +# define OPENMP_DIRECTIVE(Name) OPENMP_DIRECTIVE_EXT(Name, #Name) +#endif #ifndef OPENMP_CLAUSE # define OPENMP_CLAUSE(Name, Class) #endif diff --git a/clang/lib/Basic/OpenMPKinds.cpp b/clang/lib/Basic/OpenMPKinds.cpp --- a/clang/lib/Basic/OpenMPKinds.cpp +++ b/clang/lib/Basic/OpenMPKinds.cpp @@ -21,7 +21,6 @@ OpenMPDirectiveKind clang::getOpenMPDirectiveKind(StringRef Str) { return llvm::StringSwitch(Str) -#define OPENMP_DIRECTIVE(Name) .Case(#Name, OMPD_##Name) #define OPENMP_DIRECTIVE_EXT(Name, Str) .Case(Str, OMPD_##Name) #include "clang/Basic/OpenMPKinds.def" .Default(OMPD_unknown); @@ -32,9 +31,6 @@ switch (Kind) { case OMPD_unknown: return "unknown"; -#define OPENMP_DIRECTIVE(Name) \ - case OMPD_##Name: \ - return #Name; #define OPENMP_DIRECTIVE_EXT(Name, Str) \ case OMPD_##Name: \ return Str; diff --git a/clang/lib/CodeGen/CGOpenMPRuntime.cpp b/clang/lib/CodeGen/CGOpenMPRuntime.cpp --- a/clang/lib/CodeGen/CGOpenMPRuntime.cpp +++ b/clang/lib/CodeGen/CGOpenMPRuntime.cpp @@ -3476,8 +3476,21 @@ void CGOpenMPRuntime::emitBarrierCall(CodeGenFunction &CGF, SourceLocation Loc, OpenMPDirectiveKind Kind, bool EmitChecks, bool ForceSimpleCall) { + // Check if we can use the OMPBuilder + // TODO: Implement missing functionality and remove this check and directly + // invoke the builder without going through the CGOpenMPRuntime. + auto *OMPRegionInfo = dyn_cast_or_null(CGF.CapturedStmtInfo); + if (!EmitChecks || ForceSimpleCall || !OMPRegionInfo || + !OMPRegionInfo->hasCancel()) { + CGF.CGM.getOpenMPIRBuilder().emitOMPBarrier( + {CGF.Builder.saveIP()}, llvm::OpenMPIRBuilder::DirektiveKind(Kind), + EmitChecks); + return; + } + if (!CGF.HaveInsertPoint()) return; + // Build call __kmpc_cancel_barrier(loc, thread_id); // Build call __kmpc_barrier(loc, thread_id); unsigned Flags = getDefaultFlagsForBarriers(Kind); @@ -3485,8 +3498,7 @@ // thread_id); llvm::Value *Args[] = {emitUpdateLocation(CGF, Loc, Flags), getThreadID(CGF, Loc)}; - if (auto *OMPRegionInfo = - dyn_cast_or_null(CGF.CapturedStmtInfo)) { + if (OMPRegionInfo) { if (!ForceSimpleCall && OMPRegionInfo->hasCancel()) { llvm::Value *Result = CGF.EmitRuntimeCall( createRuntimeFunction(OMPRTL__kmpc_cancel_barrier), Args); diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -33,6 +33,7 @@ #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/StringMap.h" #include "llvm/IR/Module.h" +#include "llvm/IR/OpenMPIRBuilder.h" #include "llvm/IR/ValueHandle.h" #include "llvm/Transforms/Utils/SanitizerStats.h" @@ -319,6 +320,8 @@ std::unique_ptr ObjCRuntime; std::unique_ptr OpenCLRuntime; std::unique_ptr OpenMPRuntime; + /// The OpenMP-IR-Builder used for the entire module. + std::unique_ptr OMPBuilder; std::unique_ptr CUDARuntime; std::unique_ptr DebugInfo; std::unique_ptr ObjCData; @@ -585,6 +588,12 @@ return *OpenMPRuntime; } + /// Return a reference to the configured OpenMP-IR-Builder. + llvm::OpenMPIRBuilder &getOpenMPIRBuilder() { + assert(OMPBuilder != nullptr); + return *OMPBuilder; + } + /// Return a reference to the configured CUDA runtime. CGCUDARuntime &getCUDARuntime() { assert(CUDARuntime != nullptr); diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -216,6 +216,11 @@ OpenMPRuntime.reset(new CGOpenMPRuntime(*this)); break; } + + // The OpenMP-IR-Builder should eventually replace the above runtime codegens + // but we are not there yet so they both reside in CGModule for now. + OMPBuilder.reset(new llvm::OpenMPIRBuilder(TheModule)); + OMPBuilder->initialize(); } void CodeGenModule::createCUDARuntime() { diff --git a/clang/test/OpenMP/for_firstprivate_codegen.cpp b/clang/test/OpenMP/for_firstprivate_codegen.cpp --- a/clang/test/OpenMP/for_firstprivate_codegen.cpp +++ b/clang/test/OpenMP/for_firstprivate_codegen.cpp @@ -288,6 +288,7 @@ // CHECK: ret // // CHECK: define internal void [[TMAIN_MICROTASK]](i{{[0-9]+}}* noalias [[GTID_ADDR:%.+]], i{{[0-9]+}}* noalias %{{.+}}, i32* dereferenceable(4) %{{.+}}, [2 x i32]* dereferenceable(8) %{{.+}}, [2 x [[S_INT_TY]]]* dereferenceable(8) %{{.+}}, [[S_INT_TY]]* dereferenceable(4) %{{.+}}) +// CHECK: [[GTID_CALL:%.+]] = call i32 @__kmpc_global_thread_num // Skip temp vars for loop // CHECK: alloca i{{[0-9]+}}, // CHECK: alloca i{{[0-9]+}}, @@ -337,9 +338,7 @@ // ~(firstprivate var), ~(firstprivate s_arr) // CHECK-DAG: call {{.*}} [[S_INT_TY_DESTR]]([[S_INT_TY]]* [[VAR_PRIV]]) // CHECK-DAG: call {{.*}} [[S_INT_TY_DESTR]]([[S_INT_TY]]* -// CHECK: [[GTID_REF:%.+]] = load i{{[0-9]+}}*, i{{[0-9]+}}** [[GTID_ADDR_ADDR]] -// CHECK: [[GTID:%.+]] = load i{{[0-9]+}}, i{{[0-9]+}}* [[GTID_REF]] -// CHECK: call void @__kmpc_barrier(%{{.+}}* [[IMPLICIT_BARRIER_LOC]], i{{[0-9]+}} [[GTID]]) +// CHECK: call void @__kmpc_barrier(%{{.+}}* [[IMPLICIT_BARRIER_LOC]], i{{[0-9]+}} [[GTID_CALL]]) // CHECK: ret void #endif diff --git a/clang/test/OpenMP/for_linear_codegen.cpp b/clang/test/OpenMP/for_linear_codegen.cpp --- a/clang/test/OpenMP/for_linear_codegen.cpp +++ b/clang/test/OpenMP/for_linear_codegen.cpp @@ -228,7 +228,7 @@ // LAMBDA: call void @__kmpc_for_static_fini(%{{.+}}* @{{.+}}, i32 [[GTID]]) g += 5; g1 += 5; - // LAMBDA: call void @__kmpc_barrier(%{{.+}}* @{{.+}}, i{{[0-9]+}} [[GTID]]) + // LAMBDA: call void @__kmpc_barrier(%{{.+}}* @{{.+}} [&]() { // LAMBDA: define {{.+}} void [[INNER_LAMBDA]](%{{.+}}* [[ARG_PTR:%.+]]) // LAMBDA: store %{{.+}}* [[ARG_PTR]], %{{.+}}** [[ARG_PTR_REF:%.+]], @@ -282,7 +282,7 @@ // BLOCKS: call void @__kmpc_for_static_fini(%{{.+}}* @{{.+}}, i32 [[GTID]]) g += 5; g1 += 5; - // BLOCKS: call void @__kmpc_barrier(%{{.+}}* @{{.+}}, i{{[0-9]+}} [[GTID]]) + // BLOCKS: call void @__kmpc_barrier(%{{.+}}* @{{.+}}, g = 1; g1 = 5; ^{ @@ -415,7 +415,7 @@ // CHECK: store i64 [[ADD]], i64* [[LVAR_PRIV]], // CHECK: call void @__kmpc_for_static_fini(%{{.+}}* @{{.+}}, i32 %{{.+}}) // CHECK: call void @__kmpc_free(i32 [[GTID]], i8* [[LVAR_VOID_PTR]], i8* inttoptr (i64 5 to i8*)) -// CHECK: call void @__kmpc_barrier(%{{.+}}* [[IMPLICIT_BARRIER_LOC]], i{{[0-9]+}} [[GTID]]) +// CHECK: call void @__kmpc_barrier(%{{.+}}* [[IMPLICIT_BARRIER_LOC]] // CHECK: ret void // CHECK: define {{.*}} i{{[0-9]+}} [[TMAIN_INT]]() @@ -514,7 +514,7 @@ // CHECK: [[ADD:%.+]] = add nsw i32 [[LVAR_VAL]], 1 // CHECK: store i32 [[ADD]], i32* [[LVAR_PRIV]], // CHECK: call void @__kmpc_for_static_fini(%{{.+}}* @{{.+}}, i32 %{{.+}}) -// CHECK: call void @__kmpc_barrier(%{{.+}}* [[IMPLICIT_BARRIER_LOC]], i{{[0-9]+}} [[GTID]]) +// CHECK: call void @__kmpc_barrier(%{{.+}}* [[IMPLICIT_BARRIER_LOC]], // CHECK: ret void #endif diff --git a/clang/test/OpenMP/parallel_copyin_codegen.cpp b/clang/test/OpenMP/parallel_copyin_codegen.cpp --- a/clang/test/OpenMP/parallel_copyin_codegen.cpp +++ b/clang/test/OpenMP/parallel_copyin_codegen.cpp @@ -311,12 +311,10 @@ // TLS-CHECK: call {{.*}} [[S_FLOAT_TY_COPY_ASSIGN]]([[S_FLOAT_TY]]* {{.*}}[[VAR]], [[S_FLOAT_TY]]* {{.*}}[[MASTER_REF4]]) -// CHECK: call {{.*}}void @__kmpc_barrier(%{{.+}}* [[IMPLICIT_BARRIER_LOC]], i32 [[GTID]]) +// CHECK: call {{.*}}void @__kmpc_barrier(%{{.+}}* [[IMPLICIT_BARRIER_LOC]], // CHECK: ret void -// TLS-CHECK: [[GTID_ADDR:%.+]] = load i32*, i32** [[GTID_ADDR_ADDR]], -// TLS-CHECK: [[GTID:%.+]] = load i32, i32* [[GTID_ADDR]], -// TLS-CHECK: call {{.*}}void @__kmpc_barrier(%{{.+}}* [[IMPLICIT_BARRIER_LOC]], i32 [[GTID]]) +// TLS-CHECK: call {{.*}}void @__kmpc_barrier(%{{.+}}* [[IMPLICIT_BARRIER_LOC]], // TLS-CHECK: ret void // CHECK: define internal {{.*}}void [[MAIN_MICROTASK1]](i{{[0-9]+}}* noalias [[GTID_ADDR:%.+]], i{{[0-9]+}}* noalias %{{.+}}) @@ -347,13 +345,11 @@ // TLS-CHECK: store i32 [[MASTER_VAL]], i32* [[T_VAR]] // TLS-CHECK: [[DONE]] -// CHECK: call {{.*}}void @__kmpc_barrier(%{{.+}}* [[IMPLICIT_BARRIER_LOC]], i32 [[GTID]]) +// CHECK: call {{.*}}void @__kmpc_barrier(%{{.+}}* [[IMPLICIT_BARRIER_LOC]], // CHECK: add nsw i32 %{{.+}}, 1 // CHECK: ret void -// TLS-CHECK: [[GTID_ADDR:%.+]] = load i32*, i32** [[GTID_ADDR_ADDR]], -// TLS-CHECK: [[GTID:%.+]] = load i32, i32* [[GTID_ADDR]], -// TLS-CHECK: call {{.*}}void @__kmpc_barrier(%{{.+}}* [[IMPLICIT_BARRIER_LOC]], i32 [[GTID]]) +// TLS-CHECK: call {{.*}}void @__kmpc_barrier(%{{.+}}* [[IMPLICIT_BARRIER_LOC]], // TLS-CHECK: ret void // CHECK: define {{.*}} i{{[0-9]+}} [[TMAIN_INT]]() @@ -433,12 +429,10 @@ // TLS-CHECK: call {{.*}} [[S_INT_TY_COPY_ASSIGN]]([[S_INT_TY]]* {{.*}}[[TMAIN_VAR]], [[S_INT_TY]]* {{.*}}[[MASTER_REF3]]) -// CHECK: call {{.*}}void @__kmpc_barrier(%{{.+}}* [[IMPLICIT_BARRIER_LOC]], i32 [[GTID]]) +// CHECK: call {{.*}}void @__kmpc_barrier(%{{.+}}* [[IMPLICIT_BARRIER_LOC]], // CHECK: ret void -// TLS-CHECK: [[GTID_ADDR:%.+]] = load i32*, i32** [[GTID_ADDR_ADDR]], -// TLS-CHECK: [[GTID:%.+]] = load i32, i32* [[GTID_ADDR]], -// TLS-CHECK: call {{.*}}void @__kmpc_barrier(%{{.+}}* [[IMPLICIT_BARRIER_LOC]], i32 [[GTID]]) +// TLS-CHECK: call {{.*}}void @__kmpc_barrier(%{{.+}}* [[IMPLICIT_BARRIER_LOC]], // TLS-CHECK: ret void // CHECK: define internal {{.*}}void [[TMAIN_MICROTASK1]](i{{[0-9]+}}* noalias [[GTID_ADDR:%.+]], i{{[0-9]+}}* noalias %{{.+}}) @@ -469,12 +463,10 @@ // TLS-CHECK: store i32 [[MASTER_VAL]], i32* [[TMAIN_T_VAR]] // TLS-CHECK: [[DONE]] -// CHECK: call {{.*}}void @__kmpc_barrier(%{{.+}}* [[IMPLICIT_BARRIER_LOC]], i32 [[GTID]]) +// CHECK: call {{.*}}void @__kmpc_barrier(%{{.+}}* [[IMPLICIT_BARRIER_LOC]], // CHECK: ret void -// TLS-CHECK: [[GTID_ADDR:%.+]] = load i32*, i32** [[GTID_ADDR_ADDR]], -// TLS-CHECK: [[GTID:%.+]] = load i32, i32* [[GTID_ADDR]], -// TLS-CHECK: call {{.*}}void @__kmpc_barrier(%{{.+}}* [[IMPLICIT_BARRIER_LOC]], i32 [[GTID]]) +// TLS-CHECK: call {{.*}}void @__kmpc_barrier(%{{.+}}* [[IMPLICIT_BARRIER_LOC]], // TLS-CHECK: ret void #endif diff --git a/clang/test/OpenMP/sections_codegen.cpp b/clang/test/OpenMP/sections_codegen.cpp --- a/clang/test/OpenMP/sections_codegen.cpp +++ b/clang/test/OpenMP/sections_codegen.cpp @@ -89,7 +89,7 @@ // CHECK-LABEL: tmain // CHECK: call void {{.*}} @__kmpc_fork_call( -// CHECK-NOT: __kmpc_global_thread_num +// CHECK: __kmpc_global_thread_num // CHECK: call void @__kmpc_for_static_init_4( // CHECK: invoke void @{{.*}}foo{{.*}}() // CHECK-NEXT: unwind label %[[TERM_LPAD:.+]] 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,139 @@ +//===- 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/IRBuilder.h" + +namespace llvm { + +/// An interface to create LLVM-IR for OpenMP directives. +/// +/// Each OpenMP directive has a corresponding public generator method. +struct OpenMPIRBuilder { + + /// IDs for all omp runtime library (RTL) functions. + enum RTLFnKind { +#define OMP_RTL(Enum, ...) Enum, +#include "llvm/IR/OpenMPKinds.def" + }; + + /// IDs for all OpenMP directives. + enum DirektiveKind { +#define OMP_DIRECTIVE(Enum, ...) 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 IdentFlag { +#define OMP_IDENT_FLAG(Enum, Str, Value) Enum = Value, +#include "llvm/IR/OpenMPKinds.def" + }; + + /// 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 other method and only once! + void initialize(); + + /// Description of a LLVM-IR insertion point (IP) and (later) a source + /// location (filename, line, column, ...). + struct LocationDescription { + IRBuilder<>::InsertPoint IP; + }; + + /// 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 CheckCancelFlag Flag to indicate a cancel barrier return value + /// should be checked and acted upon. + void emitOMPBarrier(const LocationDescription &Loc, DirektiveKind DK, + bool CheckCancelFlag = true); + + ///} + +private: + /// Return the function declaration for the runtime function with \p FnID. + Function *getRuntimeFunction(RTLFnKind FnID); + + /// Return the (LLVM-IR) string describing the source location \p LocStr. + Constant *getSrcLocStr(std::string LocStr); + + /// Return the (LLVM-IR) string describing the default source location. + Constant *getDefaultSrcLocStr(); + + /// Return the (LLVM-IR) string describing the source location \p Loc. + Constant *getSrcLocStr(const LocationDescription &Loc); + + /// Return an ident_t* encoding the source location \p SrcLocStr and \p Flags. + Value *getIdent(Constant *SrcLocStr, unsigned Flags = 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 CheckCancelFlag Flag to indicate a cancel barrier return value + /// should be checked and acted upon. + /// \param ForceSimpleCall Flag to force a simple (=non-cancelation) barrier + void emitBarrierImpl(const LocationDescription &Loc, DirektiveKind DK, + bool CheckCancelFlag, bool ForceSimpleCall); + + /// Return the current thread ID. + /// + /// \param Ident The ident (ident_t*) describing the query origin. + Value *getThreadID(Value *Ident); + + /// Declarations for LLVM-IR types (simple and structure) are generated below. + /// Their names are defined and used in OpenMPKinds.def. Here we provide the + /// declarations, the initialize method will provide the values. + /// + ///{ + +#define OMP_TYPE(VarName, InitValue) Type *VarName = nullptr; +#define OMP_STRUCT_TYPE(VarName, StrName, ...) Type *VarName = nullptr; +#include "llvm/IR/OpenMPKinds.def" + + ///} + + /// 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 new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/IR/OpenMPKinds.def @@ -0,0 +1,207 @@ +//===--- OpenMPKinds.def - OpenMP directives, clauses, rt-calls -*- 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 +// +//===----------------------------------------------------------------------===// +/// \file +/// +/// This file defines the list of supported OpenMP directives, clauses, runtime +/// calls, and other things that need to be listed in enums. +/// +//===----------------------------------------------------------------------===// + +/// OpenMP Directives and combined directives +/// +///{ + +#ifndef OMP_DIRECTIVE +#define OMP_DIRECTIVE(Enum, Str) +#endif + +#define __OMP_DIRECTIVE_EXT(Name, Str) OMP_DIRECTIVE(OMPD_##Name, Str) +#define __OMP_DIRECTIVE(Name) __OMP_DIRECTIVE_EXT(Name, #Name) + +__OMP_DIRECTIVE(threadprivate) +__OMP_DIRECTIVE(parallel) +__OMP_DIRECTIVE(task) +__OMP_DIRECTIVE(simd) +__OMP_DIRECTIVE(for) +__OMP_DIRECTIVE(sections) +__OMP_DIRECTIVE(section) +__OMP_DIRECTIVE(single) +__OMP_DIRECTIVE(master) +__OMP_DIRECTIVE(critical) +__OMP_DIRECTIVE(taskyield) +__OMP_DIRECTIVE(barrier) +__OMP_DIRECTIVE(taskwait) +__OMP_DIRECTIVE(taskgroup) +__OMP_DIRECTIVE(flush) +__OMP_DIRECTIVE(ordered) +__OMP_DIRECTIVE(atomic) +__OMP_DIRECTIVE(target) +__OMP_DIRECTIVE(teams) +__OMP_DIRECTIVE(cancel) +__OMP_DIRECTIVE(requires) +__OMP_DIRECTIVE_EXT(target_data, "target data") +__OMP_DIRECTIVE_EXT(target_enter_data, "target enter data") +__OMP_DIRECTIVE_EXT(target_exit_data, "target exit data") +__OMP_DIRECTIVE_EXT(target_parallel, "target parallel") +__OMP_DIRECTIVE_EXT(target_parallel_for, "target parallel for") +__OMP_DIRECTIVE_EXT(target_update, "target update") +__OMP_DIRECTIVE_EXT(parallel_for, "parallel for") +__OMP_DIRECTIVE_EXT(parallel_for_simd, "parallel for simd") +__OMP_DIRECTIVE_EXT(parallel_sections, "parallel sections") +__OMP_DIRECTIVE_EXT(for_simd, "for simd") +__OMP_DIRECTIVE_EXT(cancellation_point, "cancellation point") +__OMP_DIRECTIVE_EXT(declare_reduction, "declare reduction") +__OMP_DIRECTIVE_EXT(declare_mapper, "declare mapper") +__OMP_DIRECTIVE_EXT(declare_simd, "declare simd") +__OMP_DIRECTIVE(taskloop) +__OMP_DIRECTIVE_EXT(taskloop_simd, "taskloop simd") +__OMP_DIRECTIVE(distribute) +__OMP_DIRECTIVE_EXT(declare_target, "declare target") +__OMP_DIRECTIVE_EXT(end_declare_target, "end declare target") +__OMP_DIRECTIVE_EXT(distribute_parallel_for, "distribute parallel for") +__OMP_DIRECTIVE_EXT(distribute_parallel_for_simd, + "distribute parallel for simd") +__OMP_DIRECTIVE_EXT(distribute_simd, "distribute simd") +__OMP_DIRECTIVE_EXT(target_parallel_for_simd, "target parallel for simd") +__OMP_DIRECTIVE_EXT(target_simd, "target simd") +__OMP_DIRECTIVE_EXT(teams_distribute, "teams distribute") +__OMP_DIRECTIVE_EXT(teams_distribute_simd, "teams distribute simd") +__OMP_DIRECTIVE_EXT(teams_distribute_parallel_for_simd, + "teams distribute parallel for simd") +__OMP_DIRECTIVE_EXT(teams_distribute_parallel_for, + "teams distribute parallel for") +__OMP_DIRECTIVE_EXT(target_teams, "target teams") +__OMP_DIRECTIVE_EXT(target_teams_distribute, "target teams distribute") +__OMP_DIRECTIVE_EXT(target_teams_distribute_parallel_for, + "target teams distribute parallel for") +__OMP_DIRECTIVE_EXT(target_teams_distribute_parallel_for_simd, + "target teams distribute parallel for simd") +__OMP_DIRECTIVE_EXT(target_teams_distribute_simd, + "target teams distribute simd") +__OMP_DIRECTIVE(allocate) +__OMP_DIRECTIVE_EXT(declare_variant, "declare variant") +__OMP_DIRECTIVE_EXT(master_taskloop, "master taskloop") +__OMP_DIRECTIVE_EXT(parallel_master_taskloop, "parallel master taskloop") +__OMP_DIRECTIVE_EXT(master_taskloop_simd, "master taskloop simd") +__OMP_DIRECTIVE_EXT(parallel_master_taskloop_simd, + "parallel master taskloop simd") + +// Has to be the last because Clang implicitly expects it to be. +__OMP_DIRECTIVE(unknown) + +#undef __OMP_DIRECTIVE_EXT +#undef __OMP_DIRECTIVE +#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) + +#undef __OMP_TYPE +#undef OMP_TYPE + +///} + +/// Struct 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(IdentPtr, ident_t, Int32, Int32, Int32, Int32, Int8Ptr) + +#undef __OMP_STRUCT_TYPE +#undef OMP_STRUCT_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) + +#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)), + 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 @@ -39,6 +39,7 @@ Metadata.cpp Module.cpp ModuleSummaryIndex.cpp + OpenMPIRBuilder.cpp Operator.cpp OptBisect.cpp Pass.cpp 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,217 @@ +//===- 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/IR/MDBuilder.h" +#include "llvm/Transforms/Utils/BasicBlockUtils.h" + +#define DEBUG_TYPE "openmp-ir-builder" + +using namespace llvm; + +Function *OpenMPIRBuilder::getRuntimeFunction(RTLFnKind 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/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"); + + 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() { + 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. + Type *T; +#define OMP_TYPE(VarName, InitValue) this->VarName = InitValue; +#define OMP_STRUCT_TYPE(VarName, StructName, ...) \ + T = M.getTypeByName(StructName); \ + if (!T) \ + T = StructType::create(Ctx, {__VA_ARGS__}, StructName); \ + this->VarName = PointerType::getUnqual(T); +#include "llvm/IR/OpenMPKinds.def" +} + +Value *OpenMPIRBuilder::getIdent(Constant *SrcLocStr, unsigned LocFlags) { + // Enable "C-mode". + LocFlags |= OMP_IDENT_FLAG_KMPC; + + GlobalVariable *&DefaultIdent = IdentMap[{SrcLocStr, LocFlags}]; + if (!DefaultIdent) { + Constant *I32Null = ConstantInt::getNullValue(Int32); + Constant *IdentData[] = {I32Null, ConstantInt::get(Int32, 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::getSrcLocStr(std::string 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::getDefaultSrcLocStr() { + return getSrcLocStr(";unknown;unknown;0;0;;"); +} + +Constant *OpenMPIRBuilder::getSrcLocStr(const LocationDescription &Loc) { + // TODO: Support actual source locations. + return getDefaultSrcLocStr(); +} + +Value *OpenMPIRBuilder::getThreadID(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; + + auto FnDecl = getRuntimeFunction(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::emitOMPBarrier(const LocationDescription &Loc, + DirektiveKind DK, bool CheckCancelFlag) { + assert(Loc.IP.getBlock() && "No insertion point provided!"); + Builder.restoreIP(Loc.IP); + return emitBarrierImpl(Loc, DK, CheckCancelFlag, + /* ForceSimpleCall */ false); +} + +void OpenMPIRBuilder::emitBarrierImpl(const LocationDescription &Loc, + DirektiveKind Kind, bool CheckCancelFlag, + bool ForceSimpleCall) { + // Build call __kmpc_cancel_barrier(loc, thread_id) or + // __kmpc_barrier(loc, thread_id); + + unsigned BarrierLocFlags = 0; + 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 = getSrcLocStr(Loc); + Value *Args[] = {getIdent(SrcLocStr, BarrierLocFlags), + getThreadID(getIdent(SrcLocStr))}; + bool UseCancelBarrier = !ForceSimpleCall && CancelationBlock; + Value *Result = Builder.CreateCall( + getRuntimeFunction(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; + } +}