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,29 @@ }]; } +//===----------------------------------------------------------------------===// +// [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); + let parser = [{ return parseThreadprivateOp(parser, result); }]; + let printer = [{ return printThreadprivateOp(p, *this); }]; +} + //===----------------------------------------------------------------------===// // 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/Dialect/OpenMP/IR/OpenMPDialect.cpp =================================================================== --- mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp +++ mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp @@ -949,5 +949,35 @@ #define GET_ATTRDEF_CLASSES #include "mlir/Dialect/OpenMP/OpenMPOpsAttributes.cpp.inc" +//===----------------------------------------------------------------------===// +// ThreadprivateOp +//===----------------------------------------------------------------------===// + +/// Parser for ThreadprivateOp +/// +/// operation ::= `omp.threadprivate` sym_addr `->` result-type +/// address ::= operand `:` type +static ParseResult parseThreadprivateOp(OpAsmParser &parser, + OperationState &result) { + OpAsmParser::OperandType sym_addr; + Type sym_addrType; + SmallVector resultType; + + if (parser.parseOperand(sym_addr) || parser.parseColonType(sym_addrType) || + parser.resolveOperand(sym_addr, sym_addrType, result.operands)) + return failure(); + if (parser.parseArrowTypeList(resultType)) + return failure(); + result.addTypes(resultType); + return success(); +} + +/// Printer for ThreadprivateOp +static void printThreadprivateOp(OpAsmPrinter &p, ThreadprivateOp op) { + p << " " << op.sym_addr() << " : " << op.sym_addr().getType() << " -> " + << op.getType(); + return; +} + #define GET_OP_CLASSES #include "mlir/Dialect/OpenMP/OpenMPOps.cpp.inc" 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.sym_name().str()).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/Target/LLVMIR/openmp-llvm.mlir =================================================================== --- mlir/test/Target/LLVMIR/openmp-llvm.mlir +++ mlir/test/Target/LLVMIR/openmp-llvm.mlir @@ -2050,3 +2050,46 @@ // CHECK: ret void llvm.return } + +// ----- + +// CHECK: @_QFsubEx = internal global i32 undef +// CHECK: @_QFsubEx.cache = common global i8** null + +// CHECK-LABEL: @_QPsub +llvm.func @_QPsub() { +// 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 { + %0 = llvm.mlir.undef : i32 + llvm.return %0 : i32 +}