Index: mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td =================================================================== --- mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td +++ mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td @@ -882,6 +882,30 @@ }]; } +//===----------------------------------------------------------------------===// +// [5.1] 2.21.2 threadprivate Directive +//===----------------------------------------------------------------------===// + +def ThreadprivateOp : OpenMP_Op<"threadprivate"> { + let summary = "threadprivate directive"; + let description = [{ + The threadprivate directive specifies that variables are replicated, with + each thread having its own copy. + + This model takes in the addressing of symbol and returns the addressing of + thread local storage (TLS) for the original variable. + + The `sym_addr` refers to the addressing of the symbol, which is pointer to + original global variable. + }]; + + let arguments = (ins OpenMP_PointerLikeType:$sym_addr); + let results = (outs OpenMP_PointerLikeType:$tls_addr); + let assemblyFormat = [{ + $sym_addr `:` type($sym_addr) `->` type($tls_addr) attr-dict + }]; +} + //===----------------------------------------------------------------------===// // 2.19.5.7 declare reduction Directive //===----------------------------------------------------------------------===// Index: mlir/include/mlir/Target/LLVMIR/ModuleTranslation.h =================================================================== --- mlir/include/mlir/Target/LLVMIR/ModuleTranslation.h +++ mlir/include/mlir/Target/LLVMIR/ModuleTranslation.h @@ -22,6 +22,7 @@ #include "llvm/ADT/SetVector.h" #include "llvm/Frontend/OpenMP/OMPIRBuilder.h" +#include "llvm/IR/DataLayout.h" namespace llvm { class BasicBlock; @@ -171,6 +172,14 @@ return ompBuilder.get(); } + /// Returns the DataLayout associated with the LLVM IR module being + /// constructed. + llvm::DataLayout *getDataLayout() { + if (!dataLayout) + dataLayout = std::make_unique(&*llvmModule); + return dataLayout.get(); + } + /// Translates the given location. const llvm::DILocation *translateLoc(Location loc, llvm::DILocalScope *scope); @@ -296,6 +305,9 @@ /// Builder for LLVM IR generation of OpenMP constructs. std::unique_ptr ompBuilder; + /// Provide methods for querying the target data layout string. + std::unique_ptr dataLayout; + /// Mappings between llvm.mlir.global definitions and corresponding globals. DenseMap globalsMapping; Index: mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp =================================================================== --- mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp +++ mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp @@ -1319,6 +1319,41 @@ return success(); } +/// Converts an OpenMP Threadprivate operation into LLVM IR using +/// OpenMPIRBuilder. +static LogicalResult +convertOmpThreadprivate(Operation &opInst, llvm::IRBuilderBase &builder, + LLVM::ModuleTranslation &moduleTranslation) { + llvm::OpenMPIRBuilder::LocationDescription ompLoc( + builder.saveIP(), builder.getCurrentDebugLocation()); + auto threadprivateOp = cast(opInst); + + Value symAddr = threadprivateOp.sym_addr(); + auto symOp = symAddr.getDefiningOp(); + assert(isa(symOp) && "Addressing symbol not found"); + auto addressOfOp = dyn_cast(symOp); + + LLVM::GlobalOp global = addressOfOp.getGlobal(); + llvm::GlobalValue *globalValue = moduleTranslation.lookupGlobal(global); + llvm::LLVMContext &ctx = builder.getContext(); + llvm::Value *data = + builder.CreateBitCast(globalValue, llvm::Type::getInt8PtrTy(ctx)); + llvm::Type *type = globalValue->getValueType(); + llvm::TypeSize typeSize = + moduleTranslation.getDataLayout()->getTypeStoreSize(type); + llvm::ConstantInt *size = llvm::ConstantInt::get(llvm::Type::getInt64Ty(ctx), + typeSize.getFixedSize()); + llvm::StringRef suffix = llvm::StringRef(".cache", 6); + llvm::Twine cacheName = Twine(global.getSymName()).concat(suffix); + // Emit runtime function and bitcast its type (i8*) to real data type. + llvm::Value *callInst = + moduleTranslation.getOpenMPBuilder()->createCachedThreadPrivate( + ompLoc, data, size, cacheName); + llvm::Value *result = builder.CreateBitCast(callInst, globalValue->getType()); + moduleTranslation.mapValue(opInst.getResult(0), result); + return success(); +} + namespace { /// Implementation of the dialect interface that converts operations belonging @@ -1424,6 +1459,9 @@ // name for critical section names. return success(); }) + .Case([&](omp::ThreadprivateOp) { + return convertOmpThreadprivate(*op, builder, moduleTranslation); + }) .Default([&](Operation *inst) { return inst->emitError("unsupported OpenMP operation: ") << inst->getName(); Index: mlir/test/Dialect/OpenMP/ops.mlir =================================================================== --- mlir/test/Dialect/OpenMP/ops.mlir +++ mlir/test/Dialect/OpenMP/ops.mlir @@ -1,4 +1,4 @@ -// RUN: mlir-opt %s | mlir-opt | FileCheck %s +// RUN: mlir-opt -split-input-file %s | mlir-opt | FileCheck %s func @omp_barrier() -> () { // CHECK: omp.barrier @@ -896,3 +896,29 @@ } return } + +// ----- + +func @omp_threadprivate() { + %0 = arith.constant 1 : i32 + %1 = arith.constant 2 : i32 + %2 = arith.constant 3 : i32 + + // CHECK: [[ARG0:%.*]] = llvm.mlir.addressof @_QFsubEx : !llvm.ptr + // CHECK: {{.*}} = omp.threadprivate [[ARG0]] : !llvm.ptr -> !llvm.ptr + %3 = llvm.mlir.addressof @_QFsubEx : !llvm.ptr + %4 = omp.threadprivate %3 : !llvm.ptr -> !llvm.ptr + llvm.store %0, %4 : !llvm.ptr + + // CHECK: omp.parallel + // CHECK: {{.*}} = omp.threadprivate [[ARG0]] : !llvm.ptr -> !llvm.ptr + omp.parallel { + %5 = omp.threadprivate %3 : !llvm.ptr -> !llvm.ptr + llvm.store %1, %5 : !llvm.ptr + omp.terminator + } + llvm.store %2, %4 : !llvm.ptr + return +} + +llvm.mlir.global internal @_QFsubEx() : i32 Index: mlir/test/Target/LLVMIR/openmp-llvm.mlir =================================================================== --- mlir/test/Target/LLVMIR/openmp-llvm.mlir +++ mlir/test/Target/LLVMIR/openmp-llvm.mlir @@ -2050,3 +2050,43 @@ // CHECK: ret void llvm.return } + +// ----- + +// CHECK: @_QFsubEx = internal global i32 undef +// CHECK: @_QFsubEx.cache = common global i8** null + +// CHECK-LABEL: @omp_threadprivate +llvm.func @omp_threadprivate() { +// CHECK: [[THREAD:%.*]] = call i32 @__kmpc_global_thread_num(%struct.ident_t* @[[GLOB:[0-9]+]]) +// CHECK: [[TMP1:%.*]] = call i8* @__kmpc_threadprivate_cached(%struct.ident_t* @[[GLOB]], i32 [[THREAD]], i8* bitcast (i32* @_QFsubEx to i8*), i64 4, i8*** @_QFsubEx.cache) +// CHECK: [[TMP2:%.*]] = bitcast i8* [[TMP1]] to i32* +// CHECK: store i32 1, i32* [[TMP2]], align 4 +// CHECK: store i32 3, i32* [[TMP2]], align 4 + +// CHECK-LABEL: omp.par.region{{.*}} +// CHECK: [[THREAD2:%.*]] = call i32 @__kmpc_global_thread_num(%struct.ident_t* @[[GLOB2:[0-9]+]]) +// CHECK: [[TMP3:%.*]] = call i8* @__kmpc_threadprivate_cached(%struct.ident_t* @[[GLOB2]], i32 [[THREAD2]], i8* bitcast (i32* @_QFsubEx to i8*), i64 4, i8*** @_QFsubEx.cache) +// CHECK: [[TMP4:%.*]] = bitcast i8* [[TMP3]] to i32* +// CHECK: store i32 2, i32* [[TMP4]], align 4 + + %0 = llvm.mlir.constant(1 : i32) : i32 + %1 = llvm.mlir.constant(2 : i32) : i32 + %2 = llvm.mlir.constant(3 : i32) : i32 + + %3 = llvm.mlir.addressof @_QFsubEx : !llvm.ptr + %4 = omp.threadprivate %3 : !llvm.ptr -> !llvm.ptr + + llvm.store %0, %4 : !llvm.ptr + + omp.parallel { + %5 = omp.threadprivate %3 : !llvm.ptr -> !llvm.ptr + llvm.store %1, %5 : !llvm.ptr + omp.terminator + } + + llvm.store %2, %4 : !llvm.ptr + llvm.return +} + +llvm.mlir.global internal @_QFsubEx() : i32