1313#include " mlir/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.h"
1414#include " mlir/Analysis/TopologicalSortUtils.h"
1515#include " mlir/Dialect/LLVMIR/LLVMDialect.h"
16+ #include " mlir/Dialect/LLVMIR/LLVMTypes.h"
1617#include " mlir/Dialect/OpenMP/OpenMPDialect.h"
1718#include " mlir/Dialect/OpenMP/OpenMPInterfaces.h"
1819#include " mlir/IR/IRMapping.h"
2425
2526#include " llvm/ADT/ArrayRef.h"
2627#include " llvm/ADT/SetVector.h"
28+ #include " llvm/ADT/SmallVector.h"
2729#include " llvm/ADT/TypeSwitch.h"
2830#include " llvm/Frontend/OpenMP/OMPConstants.h"
2931#include " llvm/Frontend/OpenMP/OMPIRBuilder.h"
3032#include " llvm/IR/DebugInfoMetadata.h"
33+ #include " llvm/IR/DerivedTypes.h"
3134#include " llvm/IR/IRBuilder.h"
3235#include " llvm/IR/ReplaceConstant.h"
3336#include " llvm/Support/FileSystem.h"
@@ -1331,19 +1334,16 @@ findAssociatedValue(Value privateVar, llvm::IRBuilderBase &builder,
13311334
13321335// / Initialize a single (first)private variable. You probably want to use
13331336// / allocateAndInitPrivateVars instead of this.
1334- static llvm::Error
1335- initPrivateVar (llvm::IRBuilderBase &builder,
1336- LLVM::ModuleTranslation &moduleTranslation,
1337- omp::PrivateClauseOp &privDecl, Value mlirPrivVar,
1338- BlockArgument &blockArg, llvm::Value *llvmPrivateVar,
1339- llvm::SmallVectorImpl<llvm::Value *> &llvmPrivateVars,
1340- llvm::BasicBlock *privInitBlock,
1341- llvm::DenseMap<Value, Value> *mappedPrivateVars = nullptr ) {
1337+ // / This returns the private variable which has been initialized. This
1338+ // / variable should be mapped before constructing the body of the Op.
1339+ static llvm::Expected<llvm::Value *> initPrivateVar (
1340+ llvm::IRBuilderBase &builder, LLVM::ModuleTranslation &moduleTranslation,
1341+ omp::PrivateClauseOp &privDecl, Value mlirPrivVar, BlockArgument &blockArg,
1342+ llvm::Value *llvmPrivateVar, llvm::BasicBlock *privInitBlock,
1343+ llvm::DenseMap<Value, Value> *mappedPrivateVars = nullptr ) {
13421344 Region &initRegion = privDecl.getInitRegion ();
13431345 if (initRegion.empty ()) {
1344- moduleTranslation.mapValue (blockArg, llvmPrivateVar);
1345- llvmPrivateVars.push_back (llvmPrivateVar);
1346- return llvm::Error::success ();
1346+ return llvmPrivateVar;
13471347 }
13481348
13491349 // map initialization region block arguments
@@ -1363,17 +1363,15 @@ initPrivateVar(llvm::IRBuilderBase &builder,
13631363
13641364 assert (phis.size () == 1 && " expected one allocation to be yielded" );
13651365
1366- // prefer the value yielded from the init region to the allocated private
1367- // variable in case the region is operating on arguments by-value (e.g.
1368- // Fortran character boxes).
1369- moduleTranslation.mapValue (blockArg, phis[0 ]);
1370- llvmPrivateVars.push_back (phis[0 ]);
1371-
13721366 // clear init region block argument mapping in case it needs to be
13731367 // re-created with a different source for another use of the same
13741368 // reduction decl
13751369 moduleTranslation.forgetMapping (initRegion);
1376- return llvm::Error::success ();
1370+
1371+ // Prefer the value yielded from the init region to the allocated private
1372+ // variable in case the region is operating on arguments by-value (e.g.
1373+ // Fortran character boxes).
1374+ return phis[0 ];
13771375}
13781376
13791377// / Allocate and initialize delayed private variables. Returns the basic block
@@ -1415,11 +1413,13 @@ static llvm::Expected<llvm::BasicBlock *> allocateAndInitPrivateVars(
14151413 llvm::Value *llvmPrivateVar = builder.CreateAlloca (
14161414 llvmAllocType, /* ArraySize=*/ nullptr , " omp.private.alloc" );
14171415
1418- llvm::Error err = initPrivateVar (
1416+ llvm::Expected<llvm::Value *> privateVarOrError = initPrivateVar (
14191417 builder, moduleTranslation, privDecl, mlirPrivVar, blockArg,
1420- llvmPrivateVar, llvmPrivateVars, privInitBlock, mappedPrivateVars);
1421- if (err)
1418+ llvmPrivateVar, privInitBlock, mappedPrivateVars);
1419+ if (auto err = privateVarOrError. takeError () )
14221420 return err;
1421+ llvmPrivateVars.push_back (privateVarOrError.get ());
1422+ moduleTranslation.mapValue (blockArg, privateVarOrError.get ());
14231423 }
14241424 return afterAllocas;
14251425}
@@ -1730,6 +1730,97 @@ buildDependData(std::optional<ArrayAttr> dependKinds, OperandRange dependVars,
17301730 }
17311731}
17321732
1733+ namespace {
1734+ // / TaskContextStructManager takes care of creating and freeing a structure
1735+ // / containing information needed by the task body to execute.
1736+ class TaskContextStructManager {
1737+ public:
1738+ TaskContextStructManager (llvm::IRBuilderBase &builder,
1739+ LLVM::ModuleTranslation &moduleTranslation)
1740+ : builder{builder}, moduleTranslation{moduleTranslation} {}
1741+
1742+ // / Creates a heap allocated struct containing space for each private
1743+ // / variable. Returns nullptr if there are is no struct needed. Invariant:
1744+ // / privateVarTypes, privateDecls, and the elements of the structure should
1745+ // / all have the same order.
1746+ void
1747+ generateTaskContextStruct (MutableArrayRef<omp::PrivateClauseOp> privateDecls);
1748+
1749+ // / Create GEPs to access each member of the structure representing a private
1750+ // / variable, adding them to llvmPrivateVars.
1751+ void createGEPsToPrivateVars (SmallVectorImpl<llvm::Value *> &llvmPrivateVars);
1752+
1753+ // / De-allocate the task context structure.
1754+ void freeStructPtr ();
1755+
1756+ llvm::Value *getStructPtr () { return structPtr; }
1757+
1758+ private:
1759+ llvm::IRBuilderBase &builder;
1760+ LLVM::ModuleTranslation &moduleTranslation;
1761+
1762+ // / The type of each member of the structure, in order.
1763+ SmallVector<llvm::Type *> privateVarTypes;
1764+
1765+ // / A pointer to the structure containing context for this task.
1766+ llvm::Value *structPtr = nullptr ;
1767+ // / The type of the structure
1768+ llvm::Type *structTy = nullptr ;
1769+ };
1770+ } // namespace
1771+
1772+ void TaskContextStructManager::generateTaskContextStruct (
1773+ MutableArrayRef<omp::PrivateClauseOp> privateDecls) {
1774+ if (privateDecls.empty ())
1775+ return ;
1776+ privateVarTypes.reserve (privateDecls.size ());
1777+
1778+ for (omp::PrivateClauseOp &privOp : privateDecls) {
1779+ Type mlirType = privOp.getType ();
1780+ privateVarTypes.push_back (moduleTranslation.convertType (mlirType));
1781+ }
1782+
1783+ structTy = llvm::StructType::get (moduleTranslation.getLLVMContext (),
1784+ privateVarTypes);
1785+
1786+ llvm::DataLayout dataLayout =
1787+ builder.GetInsertBlock ()->getModule ()->getDataLayout ();
1788+ llvm::Type *intPtrTy = builder.getIntPtrTy (dataLayout);
1789+ llvm::Constant *allocSize = llvm::ConstantExpr::getSizeOf (structTy);
1790+
1791+ // Heap allocate the structure
1792+ structPtr = builder.CreateMalloc (intPtrTy, structTy, allocSize,
1793+ /* ArraySize=*/ nullptr , /* MallocF=*/ nullptr ,
1794+ " omp.task.context_ptr" );
1795+ }
1796+
1797+ void TaskContextStructManager::createGEPsToPrivateVars (
1798+ SmallVectorImpl<llvm::Value *> &llvmPrivateVars) {
1799+ if (!structPtr) {
1800+ assert (privateVarTypes.empty ());
1801+ return ;
1802+ }
1803+
1804+ // Create GEPs for each struct member and initialize llvmPrivateVars to point
1805+ llvmPrivateVars.reserve (privateVarTypes.size ());
1806+ llvm::Value *zero = builder.getInt32 (0 );
1807+ for (auto [i, eleTy] : llvm::enumerate (privateVarTypes)) {
1808+ llvm::Value *iVal = builder.getInt32 (i);
1809+ llvm::Value *gep = builder.CreateGEP (structTy, structPtr, {zero, iVal});
1810+ llvmPrivateVars.push_back (gep);
1811+ }
1812+ }
1813+
1814+ void TaskContextStructManager::freeStructPtr () {
1815+ if (!structPtr)
1816+ return ;
1817+
1818+ llvm::IRBuilderBase::InsertPointGuard guard{builder};
1819+ // Ensure we don't put the call to free() after the terminator
1820+ builder.SetInsertPoint (builder.GetInsertBlock ()->getTerminator ());
1821+ builder.CreateFree (structPtr);
1822+ }
1823+
17331824// / Converts an OpenMP task construct into LLVM IR using OpenMPIRBuilder.
17341825static LogicalResult
17351826convertOmpTaskOp (omp::TaskOp taskOp, llvm::IRBuilderBase &builder,
@@ -1744,6 +1835,7 @@ convertOmpTaskOp(omp::TaskOp taskOp, llvm::IRBuilderBase &builder,
17441835 SmallVector<mlir::Value> mlirPrivateVars;
17451836 SmallVector<llvm::Value *> llvmPrivateVars;
17461837 SmallVector<omp::PrivateClauseOp> privateDecls;
1838+ TaskContextStructManager taskStructMgr{builder, moduleTranslation};
17471839 mlirPrivateVars.reserve (privateBlockArgs.size ());
17481840 llvmPrivateVars.reserve (privateBlockArgs.size ());
17491841 collectPrivatizationDecls (taskOp, privateDecls);
@@ -1796,27 +1888,50 @@ convertOmpTaskOp(omp::TaskOp taskOp, llvm::IRBuilderBase &builder,
17961888 // Allocate and initialize private variables
17971889 // TODO: package private variables up in a structure
17981890 builder.SetInsertPoint (initBlock->getTerminator ());
1799- for (auto [privDecl, mlirPrivVar, blockArg] :
1800- llvm::zip_equal (privateDecls, mlirPrivateVars, privateBlockArgs)) {
1801- llvm::Type *llvmAllocType =
1802- moduleTranslation.convertType (privDecl.getType ());
18031891
1804- // Allocations:
1805- builder.SetInsertPoint (allocaIP.getBlock ()->getTerminator ());
1806- llvm::Value *llvmPrivateVar = builder.CreateAlloca (
1807- llvmAllocType, /* ArraySize=*/ nullptr , " omp.private.alloc" );
1808-
1809- // builder.SetInsertPoint(initBlock->getTerminator());
1810- auto err =
1892+ // Create task variable structure
1893+ llvm::SmallVector<llvm::Value *> privateVarAllocations;
1894+ taskStructMgr.generateTaskContextStruct (privateDecls);
1895+ // GEPs so that we can initialize the variables. Don't use these GEPs inside
1896+ // of the body otherwise it will be the GEP not the struct which is fowarded
1897+ // to the outlined function. GEPs forwarded in this way are passed in a
1898+ // stack-allocated (by OpenMPIRBuilder) structure which is not safe for tasks
1899+ // which may not be executed until after the current stack frame goes out of
1900+ // scope.
1901+ taskStructMgr.createGEPsToPrivateVars (privateVarAllocations);
1902+
1903+ for (auto [privDecl, mlirPrivVar, blockArg, llvmPrivateVarAlloc] :
1904+ llvm::zip_equal (privateDecls, mlirPrivateVars, privateBlockArgs,
1905+ privateVarAllocations)) {
1906+ llvm::Expected<llvm::Value *> privateVarOrErr =
18111907 initPrivateVar (builder, moduleTranslation, privDecl, mlirPrivVar,
1812- blockArg, llvmPrivateVar, llvmPrivateVars , initBlock);
1813- if (err)
1908+ blockArg, llvmPrivateVarAlloc , initBlock);
1909+ if (auto err = privateVarOrErr. takeError () )
18141910 return handleError (std::move (err), *taskOp.getOperation ());
1911+
1912+ llvm::IRBuilderBase::InsertPointGuard guard (builder);
1913+ builder.SetInsertPoint (builder.GetInsertBlock ()->getTerminator ());
1914+
1915+ // TODO: this is a bit of a hack for Fortran character boxes
1916+ if ((privateVarOrErr.get () != llvmPrivateVarAlloc) &&
1917+ !mlir::isa<LLVM::LLVMPointerType>(blockArg.getType ())) {
1918+ builder.CreateStore (privateVarOrErr.get (), llvmPrivateVarAlloc);
1919+ // Load it so we have the value pointed to by the GEP
1920+ llvmPrivateVarAlloc = builder.CreateLoad (privateVarOrErr.get ()->getType (),
1921+ llvmPrivateVarAlloc);
1922+ }
1923+ assert (llvmPrivateVarAlloc->getType () ==
1924+ moduleTranslation.convertType (blockArg.getType ()));
1925+
1926+ // Mapping blockArg -> llvmPrivateVarAlloc is done inside the body callback
1927+ // so that OpenMPIRBuilder doesn't try to pass each GEP address through a
1928+ // stack allocated structure.
18151929 }
18161930
18171931 // firstprivate copy region
18181932 if (failed (initFirstPrivateVars (builder, moduleTranslation, mlirPrivateVars,
1819- llvmPrivateVars, privateDecls, copyBlock)))
1933+ privateVarAllocations, privateDecls,
1934+ copyBlock)))
18201935 return llvm::failure ();
18211936
18221937 // Set up for call to createTask()
@@ -1826,6 +1941,22 @@ convertOmpTaskOp(omp::TaskOp taskOp, llvm::IRBuilderBase &builder,
18261941 InsertPointTy codegenIP) -> llvm::Error {
18271942 // translate the body of the task:
18281943 builder.restoreIP (codegenIP);
1944+
1945+ // Find and map the addresses of each variable within the task context
1946+ // structure
1947+ taskStructMgr.createGEPsToPrivateVars (llvmPrivateVars);
1948+ for (auto [blockArg, llvmPrivateVar] :
1949+ llvm::zip_equal (privateBlockArgs, llvmPrivateVars)) {
1950+ // Fix broken pass-by-value case for Fortran character boxes
1951+ if (!mlir::isa<LLVM::LLVMPointerType>(blockArg.getType ())) {
1952+ llvmPrivateVar = builder.CreateLoad (
1953+ moduleTranslation.convertType (blockArg.getType ()), llvmPrivateVar);
1954+ }
1955+ assert (llvmPrivateVar->getType () ==
1956+ moduleTranslation.convertType (blockArg.getType ()));
1957+ moduleTranslation.mapValue (blockArg, llvmPrivateVar);
1958+ }
1959+
18291960 auto continuationBlockOrError = convertOmpOpRegions (
18301961 taskOp.getRegion (), " omp.task.region" , builder, moduleTranslation);
18311962 if (failed (handleError (continuationBlockOrError, *taskOp)))
@@ -1837,6 +1968,9 @@ convertOmpTaskOp(omp::TaskOp taskOp, llvm::IRBuilderBase &builder,
18371968 llvmPrivateVars, privateDecls)))
18381969 return llvm::make_error<PreviouslyReportedError>();
18391970
1971+ // Free heap allocated task context structure at the end of the task.
1972+ taskStructMgr.freeStructPtr ();
1973+
18401974 return llvm::Error::success ();
18411975 };
18421976
0 commit comments