13
13
#include " mlir/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.h"
14
14
#include " mlir/Analysis/TopologicalSortUtils.h"
15
15
#include " mlir/Dialect/LLVMIR/LLVMDialect.h"
16
+ #include " mlir/Dialect/LLVMIR/LLVMTypes.h"
16
17
#include " mlir/Dialect/OpenMP/OpenMPDialect.h"
17
18
#include " mlir/Dialect/OpenMP/OpenMPInterfaces.h"
18
19
#include " mlir/IR/IRMapping.h"
24
25
25
26
#include " llvm/ADT/ArrayRef.h"
26
27
#include " llvm/ADT/SetVector.h"
28
+ #include " llvm/ADT/SmallVector.h"
27
29
#include " llvm/ADT/TypeSwitch.h"
28
30
#include " llvm/Frontend/OpenMP/OMPConstants.h"
29
31
#include " llvm/Frontend/OpenMP/OMPIRBuilder.h"
30
32
#include " llvm/IR/DebugInfoMetadata.h"
33
+ #include " llvm/IR/DerivedTypes.h"
31
34
#include " llvm/IR/IRBuilder.h"
32
35
#include " llvm/IR/ReplaceConstant.h"
33
36
#include " llvm/Support/FileSystem.h"
@@ -1331,19 +1334,16 @@ findAssociatedValue(Value privateVar, llvm::IRBuilderBase &builder,
1331
1334
1332
1335
// / Initialize a single (first)private variable. You probably want to use
1333
1336
// / 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 ) {
1342
1344
Region &initRegion = privDecl.getInitRegion ();
1343
1345
if (initRegion.empty ()) {
1344
- moduleTranslation.mapValue (blockArg, llvmPrivateVar);
1345
- llvmPrivateVars.push_back (llvmPrivateVar);
1346
- return llvm::Error::success ();
1346
+ return llvmPrivateVar;
1347
1347
}
1348
1348
1349
1349
// map initialization region block arguments
@@ -1363,17 +1363,15 @@ initPrivateVar(llvm::IRBuilderBase &builder,
1363
1363
1364
1364
assert (phis.size () == 1 && " expected one allocation to be yielded" );
1365
1365
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
-
1372
1366
// clear init region block argument mapping in case it needs to be
1373
1367
// re-created with a different source for another use of the same
1374
1368
// reduction decl
1375
1369
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 ];
1377
1375
}
1378
1376
1379
1377
// / Allocate and initialize delayed private variables. Returns the basic block
@@ -1415,11 +1413,13 @@ static llvm::Expected<llvm::BasicBlock *> allocateAndInitPrivateVars(
1415
1413
llvm::Value *llvmPrivateVar = builder.CreateAlloca (
1416
1414
llvmAllocType, /* ArraySize=*/ nullptr , " omp.private.alloc" );
1417
1415
1418
- llvm::Error err = initPrivateVar (
1416
+ llvm::Expected<llvm::Value *> privateVarOrError = initPrivateVar (
1419
1417
builder, moduleTranslation, privDecl, mlirPrivVar, blockArg,
1420
- llvmPrivateVar, llvmPrivateVars, privInitBlock, mappedPrivateVars);
1421
- if (err)
1418
+ llvmPrivateVar, privInitBlock, mappedPrivateVars);
1419
+ if (auto err = privateVarOrError. takeError () )
1422
1420
return err;
1421
+ llvmPrivateVars.push_back (privateVarOrError.get ());
1422
+ moduleTranslation.mapValue (blockArg, privateVarOrError.get ());
1423
1423
}
1424
1424
return afterAllocas;
1425
1425
}
@@ -1730,6 +1730,97 @@ buildDependData(std::optional<ArrayAttr> dependKinds, OperandRange dependVars,
1730
1730
}
1731
1731
}
1732
1732
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
+
1733
1824
// / Converts an OpenMP task construct into LLVM IR using OpenMPIRBuilder.
1734
1825
static LogicalResult
1735
1826
convertOmpTaskOp (omp::TaskOp taskOp, llvm::IRBuilderBase &builder,
@@ -1744,6 +1835,7 @@ convertOmpTaskOp(omp::TaskOp taskOp, llvm::IRBuilderBase &builder,
1744
1835
SmallVector<mlir::Value> mlirPrivateVars;
1745
1836
SmallVector<llvm::Value *> llvmPrivateVars;
1746
1837
SmallVector<omp::PrivateClauseOp> privateDecls;
1838
+ TaskContextStructManager taskStructMgr{builder, moduleTranslation};
1747
1839
mlirPrivateVars.reserve (privateBlockArgs.size ());
1748
1840
llvmPrivateVars.reserve (privateBlockArgs.size ());
1749
1841
collectPrivatizationDecls (taskOp, privateDecls);
@@ -1796,27 +1888,50 @@ convertOmpTaskOp(omp::TaskOp taskOp, llvm::IRBuilderBase &builder,
1796
1888
// Allocate and initialize private variables
1797
1889
// TODO: package private variables up in a structure
1798
1890
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 ());
1803
1891
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 =
1811
1907
initPrivateVar (builder, moduleTranslation, privDecl, mlirPrivVar,
1812
- blockArg, llvmPrivateVar, llvmPrivateVars , initBlock);
1813
- if (err)
1908
+ blockArg, llvmPrivateVarAlloc , initBlock);
1909
+ if (auto err = privateVarOrErr. takeError () )
1814
1910
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.
1815
1929
}
1816
1930
1817
1931
// firstprivate copy region
1818
1932
if (failed (initFirstPrivateVars (builder, moduleTranslation, mlirPrivateVars,
1819
- llvmPrivateVars, privateDecls, copyBlock)))
1933
+ privateVarAllocations, privateDecls,
1934
+ copyBlock)))
1820
1935
return llvm::failure ();
1821
1936
1822
1937
// Set up for call to createTask()
@@ -1826,6 +1941,22 @@ convertOmpTaskOp(omp::TaskOp taskOp, llvm::IRBuilderBase &builder,
1826
1941
InsertPointTy codegenIP) -> llvm::Error {
1827
1942
// translate the body of the task:
1828
1943
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
+
1829
1960
auto continuationBlockOrError = convertOmpOpRegions (
1830
1961
taskOp.getRegion (), " omp.task.region" , builder, moduleTranslation);
1831
1962
if (failed (handleError (continuationBlockOrError, *taskOp)))
@@ -1837,6 +1968,9 @@ convertOmpTaskOp(omp::TaskOp taskOp, llvm::IRBuilderBase &builder,
1837
1968
llvmPrivateVars, privateDecls)))
1838
1969
return llvm::make_error<PreviouslyReportedError>();
1839
1970
1971
+ // Free heap allocated task context structure at the end of the task.
1972
+ taskStructMgr.freeStructPtr ();
1973
+
1840
1974
return llvm::Error::success ();
1841
1975
};
1842
1976
0 commit comments