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<OpenMPDirectiveKind>(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<CGOpenMPRegionInfo>(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<CGOpenMPRegionInfo>(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<CGObjCRuntime> ObjCRuntime;
   std::unique_ptr<CGOpenCLRuntime> OpenCLRuntime;
   std::unique_ptr<CGOpenMPRuntime> OpenMPRuntime;
+  /// The OpenMP-IR-Builder used for the entire module.
+  std::unique_ptr<llvm::OpenMPIRBuilder> OMPBuilder;
   std::unique_ptr<CGCUDARuntime> CUDARuntime;
   std::unique_ptr<CGDebugInfo> DebugInfo;
   std::unique_ptr<ObjCEntrypoints> 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<Function *, Value *> ThreadIDMap;
+
+  /// Map to remember source location strings
+  StringMap<Constant *> SrcLocStrMap;
+
+  /// Map to remember existing ident_t*.
+  DenseMap<std::pair<Constant *, uint64_t>, 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<Attribute>({__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<Type *>{__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<StructType>(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<CallInst>(&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<Instruction>(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;
+  }
+}