-
Notifications
You must be signed in to change notification settings - Fork 13.6k
[mlir][OpenMP] Pack task private variables into a heap-allocated context struct #125307
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
f2b7ba9
to
f8a0b71
Compare
This should now be ready for review. |
@llvm/pr-subscribers-flang-fir-hlfir @llvm/pr-subscribers-mlir-openmp Author: Tom Eccles (tblah) ChangesThe aim here is to ensure that tasks which are not executed for a while after they are created do not try to reference any data which are now out of scope. This is done by packing the data referred to by the task into a heap allocated structure (freed at the end of the task). I decided to create the task context structure in Patch is 22.85 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/125307.diff 3 Files Affected:
diff --git a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
index 8a9a69cefad8ee..1d36181a2fa8fa 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
@@ -13,6 +13,7 @@
#include "mlir/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.h"
#include "mlir/Analysis/TopologicalSortUtils.h"
#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
+#include "mlir/Dialect/LLVMIR/LLVMTypes.h"
#include "mlir/Dialect/OpenMP/OpenMPDialect.h"
#include "mlir/Dialect/OpenMP/OpenMPInterfaces.h"
#include "mlir/IR/IRMapping.h"
@@ -24,10 +25,12 @@
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/SetVector.h"
+#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/TypeSwitch.h"
#include "llvm/Frontend/OpenMP/OMPConstants.h"
#include "llvm/Frontend/OpenMP/OMPIRBuilder.h"
#include "llvm/IR/DebugInfoMetadata.h"
+#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/ReplaceConstant.h"
#include "llvm/Support/FileSystem.h"
@@ -1331,19 +1334,16 @@ findAssociatedValue(Value privateVar, llvm::IRBuilderBase &builder,
/// Initialize a single (first)private variable. You probably want to use
/// allocateAndInitPrivateVars instead of this.
-static llvm::Error
-initPrivateVar(llvm::IRBuilderBase &builder,
- LLVM::ModuleTranslation &moduleTranslation,
- omp::PrivateClauseOp &privDecl, Value mlirPrivVar,
- BlockArgument &blockArg, llvm::Value *llvmPrivateVar,
- llvm::SmallVectorImpl<llvm::Value *> &llvmPrivateVars,
- llvm::BasicBlock *privInitBlock,
- llvm::DenseMap<Value, Value> *mappedPrivateVars = nullptr) {
+/// This returns the private variable which has been initialized. This
+/// variable should be mapped before constructing the body of the Op.
+static llvm::Expected<llvm::Value *> initPrivateVar(
+ llvm::IRBuilderBase &builder, LLVM::ModuleTranslation &moduleTranslation,
+ omp::PrivateClauseOp &privDecl, Value mlirPrivVar, BlockArgument &blockArg,
+ llvm::Value *llvmPrivateVar, llvm::BasicBlock *privInitBlock,
+ llvm::DenseMap<Value, Value> *mappedPrivateVars = nullptr) {
Region &initRegion = privDecl.getInitRegion();
if (initRegion.empty()) {
- moduleTranslation.mapValue(blockArg, llvmPrivateVar);
- llvmPrivateVars.push_back(llvmPrivateVar);
- return llvm::Error::success();
+ return llvmPrivateVar;
}
// map initialization region block arguments
@@ -1363,17 +1363,15 @@ initPrivateVar(llvm::IRBuilderBase &builder,
assert(phis.size() == 1 && "expected one allocation to be yielded");
- // prefer the value yielded from the init region to the allocated private
- // variable in case the region is operating on arguments by-value (e.g.
- // Fortran character boxes).
- moduleTranslation.mapValue(blockArg, phis[0]);
- llvmPrivateVars.push_back(phis[0]);
-
// clear init region block argument mapping in case it needs to be
// re-created with a different source for another use of the same
// reduction decl
moduleTranslation.forgetMapping(initRegion);
- return llvm::Error::success();
+
+ // Prefer the value yielded from the init region to the allocated private
+ // variable in case the region is operating on arguments by-value (e.g.
+ // Fortran character boxes).
+ return phis[0];
}
/// Allocate and initialize delayed private variables. Returns the basic block
@@ -1415,11 +1413,13 @@ static llvm::Expected<llvm::BasicBlock *> allocateAndInitPrivateVars(
llvm::Value *llvmPrivateVar = builder.CreateAlloca(
llvmAllocType, /*ArraySize=*/nullptr, "omp.private.alloc");
- llvm::Error err = initPrivateVar(
+ llvm::Expected<llvm::Value *> privateVarOrError = initPrivateVar(
builder, moduleTranslation, privDecl, mlirPrivVar, blockArg,
- llvmPrivateVar, llvmPrivateVars, privInitBlock, mappedPrivateVars);
- if (err)
+ llvmPrivateVar, privInitBlock, mappedPrivateVars);
+ if (auto err = privateVarOrError.takeError())
return err;
+ llvmPrivateVars.push_back(privateVarOrError.get());
+ moduleTranslation.mapValue(blockArg, privateVarOrError.get());
}
return afterAllocas;
}
@@ -1730,6 +1730,126 @@ buildDependData(std::optional<ArrayAttr> dependKinds, OperandRange dependVars,
}
}
+static bool privatizerReadsSourceVariable(omp::PrivateClauseOp &priv) {
+ if (priv.getDataSharingType() == omp::DataSharingClauseType::FirstPrivate)
+ return true;
+
+ Region &initRegion = priv.getInitRegion();
+ if (initRegion.empty())
+ return false;
+
+ BlockArgument sourceVariable = priv.getInitMoldArg();
+ if (!sourceVariable)
+ return false;
+ return !sourceVariable.use_empty();
+}
+
+namespace {
+/// TaskContextStructManager takes care of creating and freeing a structure
+/// containing information needed by the task body to execute.
+class TaskContextStructManager {
+public:
+ TaskContextStructManager(llvm::IRBuilderBase &builder,
+ LLVM::ModuleTranslation &moduleTranslation,
+ MutableArrayRef<omp::PrivateClauseOp> privateDecls)
+ : builder{builder}, moduleTranslation{moduleTranslation},
+ privateDecls{privateDecls} {}
+
+ /// Creates a heap allocated struct containing space for each private
+ /// variable. Returns nullptr if there are is no struct needed. Invariant:
+ /// privateVarTypes, privateDecls, and the elements of the structure should
+ /// all have the same order (although privateDecls which do not read from the
+ /// mold argument are skipped).
+ void generateTaskContextStruct();
+
+ /// Create GEPs to access each member of the structure representing a private
+ /// variable, adding them to llvmPrivateVars. Null values are added where
+ /// private decls were skipped so that the ordering continues to match the
+ /// private decls.
+ void createGEPsToPrivateVars(SmallVectorImpl<llvm::Value *> &llvmPrivateVars);
+
+ /// De-allocate the task context structure.
+ void freeStructPtr();
+
+ llvm::Value *getStructPtr() { return structPtr; }
+
+private:
+ llvm::IRBuilderBase &builder;
+ LLVM::ModuleTranslation &moduleTranslation;
+ MutableArrayRef<omp::PrivateClauseOp> privateDecls;
+
+ /// The type of each member of the structure, in order.
+ SmallVector<llvm::Type *> privateVarTypes;
+
+ /// A pointer to the structure containing context for this task.
+ llvm::Value *structPtr = nullptr;
+ /// The type of the structure
+ llvm::Type *structTy = nullptr;
+};
+} // namespace
+
+void TaskContextStructManager::generateTaskContextStruct() {
+ if (privateDecls.empty())
+ return;
+ privateVarTypes.reserve(privateDecls.size());
+
+ for (omp::PrivateClauseOp &privOp : privateDecls) {
+ // Skip private variables which can safely be allocated and initialised
+ // inside of the task
+ if (!privatizerReadsSourceVariable(privOp))
+ continue;
+ Type mlirType = privOp.getType();
+ privateVarTypes.push_back(moduleTranslation.convertType(mlirType));
+ }
+
+ structTy = llvm::StructType::get(moduleTranslation.getLLVMContext(),
+ privateVarTypes);
+
+ llvm::DataLayout dataLayout =
+ builder.GetInsertBlock()->getModule()->getDataLayout();
+ llvm::Type *intPtrTy = builder.getIntPtrTy(dataLayout);
+ llvm::Constant *allocSize = llvm::ConstantExpr::getSizeOf(structTy);
+
+ // Heap allocate the structure
+ structPtr = builder.CreateMalloc(intPtrTy, structTy, allocSize,
+ /*ArraySize=*/nullptr, /*MallocF=*/nullptr,
+ "omp.task.context_ptr");
+}
+
+void TaskContextStructManager::createGEPsToPrivateVars(
+ SmallVectorImpl<llvm::Value *> &llvmPrivateVars) {
+ if (!structPtr) {
+ assert(privateVarTypes.empty());
+ return;
+ }
+
+ // Create GEPs for each struct member and initialize llvmPrivateVars to point
+ llvmPrivateVars.reserve(privateVarTypes.size());
+ llvm::Value *zero = builder.getInt32(0);
+ unsigned i = 0;
+ for (auto privDecl : privateDecls) {
+ if (!privatizerReadsSourceVariable(privDecl)) {
+ // Handle this inside of the task so we don't pass unnessecary vars in
+ llvmPrivateVars.push_back(nullptr);
+ continue;
+ }
+ llvm::Value *iVal = builder.getInt32(i);
+ llvm::Value *gep = builder.CreateGEP(structTy, structPtr, {zero, iVal});
+ llvmPrivateVars.push_back(gep);
+ i += 1;
+ }
+}
+
+void TaskContextStructManager::freeStructPtr() {
+ if (!structPtr)
+ return;
+
+ llvm::IRBuilderBase::InsertPointGuard guard{builder};
+ // Ensure we don't put the call to free() after the terminator
+ builder.SetInsertPoint(builder.GetInsertBlock()->getTerminator());
+ builder.CreateFree(structPtr);
+}
+
/// Converts an OpenMP task construct into LLVM IR using OpenMPIRBuilder.
static LogicalResult
convertOmpTaskOp(omp::TaskOp taskOp, llvm::IRBuilderBase &builder,
@@ -1747,6 +1867,8 @@ convertOmpTaskOp(omp::TaskOp taskOp, llvm::IRBuilderBase &builder,
mlirPrivateVars.reserve(privateBlockArgs.size());
llvmPrivateVars.reserve(privateBlockArgs.size());
collectPrivatizationDecls(taskOp, privateDecls);
+ TaskContextStructManager taskStructMgr{builder, moduleTranslation,
+ privateDecls};
for (mlir::Value privateVar : taskOp.getPrivateVars())
mlirPrivateVars.push_back(privateVar);
@@ -1796,27 +1918,54 @@ convertOmpTaskOp(omp::TaskOp taskOp, llvm::IRBuilderBase &builder,
// Allocate and initialize private variables
// TODO: package private variables up in a structure
builder.SetInsertPoint(initBlock->getTerminator());
- for (auto [privDecl, mlirPrivVar, blockArg] :
- llvm::zip_equal(privateDecls, mlirPrivateVars, privateBlockArgs)) {
- llvm::Type *llvmAllocType =
- moduleTranslation.convertType(privDecl.getType());
- // Allocations:
- builder.SetInsertPoint(allocaIP.getBlock()->getTerminator());
- llvm::Value *llvmPrivateVar = builder.CreateAlloca(
- llvmAllocType, /*ArraySize=*/nullptr, "omp.private.alloc");
+ // Create task variable structure
+ llvm::SmallVector<llvm::Value *> privateVarAllocations;
+ taskStructMgr.generateTaskContextStruct();
+ // GEPs so that we can initialize the variables. Don't use these GEPs inside
+ // of the body otherwise it will be the GEP not the struct which is fowarded
+ // to the outlined function. GEPs forwarded in this way are passed in a
+ // stack-allocated (by OpenMPIRBuilder) structure which is not safe for tasks
+ // which may not be executed until after the current stack frame goes out of
+ // scope.
+ taskStructMgr.createGEPsToPrivateVars(privateVarAllocations);
+
+ for (auto [privDecl, mlirPrivVar, blockArg, llvmPrivateVarAlloc] :
+ llvm::zip_equal(privateDecls, mlirPrivateVars, privateBlockArgs,
+ privateVarAllocations)) {
+ if (!llvmPrivateVarAlloc)
+ // to be handled inside the task
+ continue;
- // builder.SetInsertPoint(initBlock->getTerminator());
- auto err =
+ llvm::Expected<llvm::Value *> privateVarOrErr =
initPrivateVar(builder, moduleTranslation, privDecl, mlirPrivVar,
- blockArg, llvmPrivateVar, llvmPrivateVars, initBlock);
- if (err)
+ blockArg, llvmPrivateVarAlloc, initBlock);
+ if (auto err = privateVarOrErr.takeError())
return handleError(std::move(err), *taskOp.getOperation());
+
+ llvm::IRBuilderBase::InsertPointGuard guard(builder);
+ builder.SetInsertPoint(builder.GetInsertBlock()->getTerminator());
+
+ // TODO: this is a bit of a hack for Fortran character boxes
+ if ((privateVarOrErr.get() != llvmPrivateVarAlloc) &&
+ !mlir::isa<LLVM::LLVMPointerType>(blockArg.getType())) {
+ builder.CreateStore(privateVarOrErr.get(), llvmPrivateVarAlloc);
+ // Load it so we have the value pointed to by the GEP
+ llvmPrivateVarAlloc = builder.CreateLoad(privateVarOrErr.get()->getType(),
+ llvmPrivateVarAlloc);
+ }
+ assert(llvmPrivateVarAlloc->getType() ==
+ moduleTranslation.convertType(blockArg.getType()));
+
+ // Mapping blockArg -> llvmPrivateVarAlloc is done inside the body callback
+ // so that OpenMPIRBuilder doesn't try to pass each GEP address through a
+ // stack allocated structure.
}
// firstprivate copy region
if (failed(initFirstPrivateVars(builder, moduleTranslation, mlirPrivateVars,
- llvmPrivateVars, privateDecls, copyBlock)))
+ privateVarAllocations, privateDecls,
+ copyBlock)))
return llvm::failure();
// Set up for call to createTask()
@@ -1824,8 +1973,55 @@ convertOmpTaskOp(omp::TaskOp taskOp, llvm::IRBuilderBase &builder,
auto bodyCB = [&](InsertPointTy allocaIP,
InsertPointTy codegenIP) -> llvm::Error {
+ // Save the alloca insertion point on ModuleTranslation stack for use in
+ // nested regions.
+ LLVM::ModuleTranslation::SaveStack<OpenMPAllocaStackFrame> frame(
+ moduleTranslation, allocaIP);
+
// translate the body of the task:
builder.restoreIP(codegenIP);
+
+ llvm::BasicBlock *privInitBlock = nullptr;
+ for (auto [blockArg, privDecl, mlirPrivVar] :
+ llvm::zip_equal(privateBlockArgs, privateDecls, mlirPrivateVars)) {
+ if (privatizerReadsSourceVariable(privDecl))
+ // This is handled before the task executes
+ continue;
+
+ auto codegenInsertionPt = builder.saveIP();
+ llvm::Type *llvmAllocType =
+ moduleTranslation.convertType(privDecl.getType());
+ builder.SetInsertPoint(allocaIP.getBlock()->getTerminator());
+ llvm::Value *llvmPrivateVar = builder.CreateAlloca(
+ llvmAllocType, /*ArraySize=*/nullptr, "omp.private.alloc");
+
+ llvm::Expected<llvm::Value *> privateVarOrError =
+ initPrivateVar(builder, moduleTranslation, privDecl, mlirPrivVar,
+ blockArg, llvmPrivateVar, privInitBlock);
+ if (auto err = privateVarOrError.takeError())
+ return err;
+ moduleTranslation.mapValue(blockArg, privateVarOrError.get());
+ builder.restoreIP(codegenInsertionPt);
+ }
+
+ // Find and map the addresses of each variable within the task context
+ // structure
+ taskStructMgr.createGEPsToPrivateVars(llvmPrivateVars);
+ for (auto [blockArg, llvmPrivateVar] :
+ llvm::zip_equal(privateBlockArgs, llvmPrivateVars)) {
+ if (!llvmPrivateVar)
+ // This was handled above
+ continue;
+ // Fix broken pass-by-value case for Fortran character boxes
+ if (!mlir::isa<LLVM::LLVMPointerType>(blockArg.getType())) {
+ llvmPrivateVar = builder.CreateLoad(
+ moduleTranslation.convertType(blockArg.getType()), llvmPrivateVar);
+ }
+ assert(llvmPrivateVar->getType() ==
+ moduleTranslation.convertType(blockArg.getType()));
+ moduleTranslation.mapValue(blockArg, llvmPrivateVar);
+ }
+
auto continuationBlockOrError = convertOmpOpRegions(
taskOp.getRegion(), "omp.task.region", builder, moduleTranslation);
if (failed(handleError(continuationBlockOrError, *taskOp)))
@@ -1837,6 +2033,9 @@ convertOmpTaskOp(omp::TaskOp taskOp, llvm::IRBuilderBase &builder,
llvmPrivateVars, privateDecls)))
return llvm::make_error<PreviouslyReportedError>();
+ // Free heap allocated task context structure at the end of the task.
+ taskStructMgr.freeStructPtr();
+
return llvm::Error::success();
};
diff --git a/mlir/test/Target/LLVMIR/openmp-llvm.mlir b/mlir/test/Target/LLVMIR/openmp-llvm.mlir
index 503ddf10c80cdc..ba05d97c9a4eda 100644
--- a/mlir/test/Target/LLVMIR/openmp-llvm.mlir
+++ b/mlir/test/Target/LLVMIR/openmp-llvm.mlir
@@ -2795,9 +2795,10 @@ llvm.func @par_task_(%arg0: !llvm.ptr {fir.bindc_name = "a"}) {
// CHECK: call i32 @__kmpc_omp_task({{.*}}, ptr %[[TASK_ALLOC]])
// CHECK: define internal void @[[task_outlined_fn]](i32 %[[GLOBAL_TID_VAL:.*]], ptr %[[STRUCT_ARG:.*]])
// CHECK: %[[LOADED_STRUCT_PTR:.*]] = load ptr, ptr %[[STRUCT_ARG]], align 8
-// CHECK: %[[GEP_STRUCTARG:.*]] = getelementptr { ptr, ptr }, ptr %[[LOADED_STRUCT_PTR]], i32 0, i32 0
+// CHECK: %[[GEP_STRUCTARG:.*]] = getelementptr { ptr }, ptr %[[LOADED_STRUCT_PTR]], i32 0, i32 0
// CHECK: %[[LOADGEP_STRUCTARG:.*]] = load ptr, ptr %[[GEP_STRUCTARG]], align 8
-// CHECK: call void ({{.*}}) @__kmpc_fork_call({{.*}}, ptr @[[parallel_outlined_fn:.+]], ptr %[[LOADGEP_STRUCTARG]])
+// CHEKC: %[[NEW_STRUCTARG:.*]] = alloca { ptr }, align 8
+// CHECK: call void ({{.*}}) @__kmpc_fork_call({{.*}}, ptr @[[parallel_outlined_fn:.+]],
// CHECK: define internal void @[[parallel_outlined_fn]]
// -----
@@ -2825,10 +2826,11 @@ llvm.func @task(%arg0 : !llvm.ptr) {
// CHECK-LABEL: @task
// CHECK-SAME: (ptr %[[ARG:.*]])
// CHECK: %[[STRUCT_ARG:.*]] = alloca { ptr }, align 8
-// CHECK: %[[OMP_PRIVATE_ALLOC:.*]] = alloca i32, align 4
// ...
// CHECK: br label %omp.private.init
// CHECK: omp.private.init:
+// CHECK: %[[OMP_TASK_CONTEXT_PTR:.*]] = tail call ptr @malloc(
+// CHECK: %[[OMP_PRIVATE_ALLOC:.*]] = getelementptr { i32 }, ptr %[[OMP_TASK_CONTEXT_PTR]]
// CHECK: br label %omp.private.copy1
// CHECK: omp.private.copy1:
// CHECK: %[[LOADED:.*]] = load i32, ptr %[[ARG]], align 4
@@ -2846,12 +2848,13 @@ llvm.func @task(%arg0 : !llvm.ptr) {
// CHECK: %[[VAL_14:.*]] = load ptr, ptr %[[VAL_13]], align 8
// CHECK: br label %task.body
// CHECK: task.body: ; preds = %task.alloca
+// CHECK: %[[VAL_15:.*]] = getelementptr { i32 }, ptr %[[VAL_14]], i32 0, i32 0
// CHECK: br label %omp.task.region
// CHECK: omp.task.region: ; preds = %task.body
-// CHECK: call void @foo(ptr %[[VAL_14]])
+// CHECK: call void @foo(ptr %[[VAL_15]])
// CHECK: br label %omp.region.cont
// CHECK: omp.region.cont: ; preds = %omp.task.region
-// CHECK: call void @destroy(ptr %[[VAL_14]])
+// CHECK: call void @destroy(ptr %[[VAL_15]])
// CHECK: br label %task.exit.exitStub
// CHECK: task.exit.exitStub: ; preds = %omp.region.cont
// CHECK: ret void
diff --git a/mlir/test/Target/LLVMIR/openmp-task-privatization.mlir b/mlir/test/Target/LLVMIR/openmp-task-privatization.mlir
new file mode 100644
index 00000000000000..af469460f3cee0
--- /dev/null
+++ b/mlir/test/Target/LLVMIR/openmp-task-privatization.mlir
@@ -0,0 +1,81 @@
+// RUN: mlir-translate -mlir-to-llvmir %s | FileCheck %s
+
+omp.private {type = private} @privatizer : i32
+
+omp.private {type = firstprivate} @firstprivatizer : i32 copy {
+^bb0(%arg0: !llvm.ptr, %arg1: !llvm.ptr):
+ %0 = llvm.load %arg0 : !llvm.ptr -> i32
+ llvm.store %0, %arg1 : i32, !llvm.ptr
+ omp.yield(%arg1 : !llvm.ptr)
+}
+
+llvm.func @task_privatization_test() {
+ %c0 = llvm.mlir.constant(0: i32) : i32
+ %c1 = llvm.mlir.constant(1: i32) : i32
+ %0 = llvm.alloca %c1 x i32 : (i32) -> !llvm.ptr
+ %1 = llvm.alloca %c1 x i32 : (i32) -> !llvm.ptr
+ llvm.store %c0, %0 : i32, !llvm.ptr
+ llvm.store %c1, %1 : i32, !llvm.ptr
+
+ omp.task private(@privatizer %0 -> %arg0, @firstprivatizer %1 -> %arg1 : !llvm.ptr, !llvm.ptr) {
+ %2 = llvm.load %arg1 : !llvm.ptr -> i32
+ llvm.store %2, %arg0 : i32, !llvm.ptr
+ omp.terminator
+ }
+ llvm.return
+}
+
+// CHECK: define void @task_privatization_test()
+// CHECK: %[[STRUCT_ARG:.*]] = alloca { ptr }, align 8
+// CHECK: %[[VAL_0:.*]] = alloca i32, align 4
+// CHECK: %[[VAL_1:.*]] = alloca i32, align 4
+// CHECK: store i32 0, ptr %[[VAL_0]], align 4
+// CHECK: store i32 1, ptr %[[VAL_1]], align 4
+// CHECK: br label %entry
+// CHECK: entry:
+// CHECK: br label %omp.private.init
+// CHECK: omp.private.init:
+// CHECK: %[[VAL_5:.*]] = tail call ptr @malloc(i64 ptrtoint (ptr getelementptr ([[STRUCT_KMP_PRIVATES_T:.*]]...
[truncated]
|
@llvm/pr-subscribers-mlir Author: Tom Eccles (tblah) ChangesThe aim here is to ensure that tasks which are not executed for a while after they are created do not try to reference any data which are now out of scope. This is done by packing the data referred to by the task into a heap allocated structure (freed at the end of the task). I decided to create the task context structure in Patch is 22.85 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/125307.diff 3 Files Affected:
diff --git a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
index 8a9a69cefad8ee..1d36181a2fa8fa 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
@@ -13,6 +13,7 @@
#include "mlir/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.h"
#include "mlir/Analysis/TopologicalSortUtils.h"
#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
+#include "mlir/Dialect/LLVMIR/LLVMTypes.h"
#include "mlir/Dialect/OpenMP/OpenMPDialect.h"
#include "mlir/Dialect/OpenMP/OpenMPInterfaces.h"
#include "mlir/IR/IRMapping.h"
@@ -24,10 +25,12 @@
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/SetVector.h"
+#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/TypeSwitch.h"
#include "llvm/Frontend/OpenMP/OMPConstants.h"
#include "llvm/Frontend/OpenMP/OMPIRBuilder.h"
#include "llvm/IR/DebugInfoMetadata.h"
+#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/ReplaceConstant.h"
#include "llvm/Support/FileSystem.h"
@@ -1331,19 +1334,16 @@ findAssociatedValue(Value privateVar, llvm::IRBuilderBase &builder,
/// Initialize a single (first)private variable. You probably want to use
/// allocateAndInitPrivateVars instead of this.
-static llvm::Error
-initPrivateVar(llvm::IRBuilderBase &builder,
- LLVM::ModuleTranslation &moduleTranslation,
- omp::PrivateClauseOp &privDecl, Value mlirPrivVar,
- BlockArgument &blockArg, llvm::Value *llvmPrivateVar,
- llvm::SmallVectorImpl<llvm::Value *> &llvmPrivateVars,
- llvm::BasicBlock *privInitBlock,
- llvm::DenseMap<Value, Value> *mappedPrivateVars = nullptr) {
+/// This returns the private variable which has been initialized. This
+/// variable should be mapped before constructing the body of the Op.
+static llvm::Expected<llvm::Value *> initPrivateVar(
+ llvm::IRBuilderBase &builder, LLVM::ModuleTranslation &moduleTranslation,
+ omp::PrivateClauseOp &privDecl, Value mlirPrivVar, BlockArgument &blockArg,
+ llvm::Value *llvmPrivateVar, llvm::BasicBlock *privInitBlock,
+ llvm::DenseMap<Value, Value> *mappedPrivateVars = nullptr) {
Region &initRegion = privDecl.getInitRegion();
if (initRegion.empty()) {
- moduleTranslation.mapValue(blockArg, llvmPrivateVar);
- llvmPrivateVars.push_back(llvmPrivateVar);
- return llvm::Error::success();
+ return llvmPrivateVar;
}
// map initialization region block arguments
@@ -1363,17 +1363,15 @@ initPrivateVar(llvm::IRBuilderBase &builder,
assert(phis.size() == 1 && "expected one allocation to be yielded");
- // prefer the value yielded from the init region to the allocated private
- // variable in case the region is operating on arguments by-value (e.g.
- // Fortran character boxes).
- moduleTranslation.mapValue(blockArg, phis[0]);
- llvmPrivateVars.push_back(phis[0]);
-
// clear init region block argument mapping in case it needs to be
// re-created with a different source for another use of the same
// reduction decl
moduleTranslation.forgetMapping(initRegion);
- return llvm::Error::success();
+
+ // Prefer the value yielded from the init region to the allocated private
+ // variable in case the region is operating on arguments by-value (e.g.
+ // Fortran character boxes).
+ return phis[0];
}
/// Allocate and initialize delayed private variables. Returns the basic block
@@ -1415,11 +1413,13 @@ static llvm::Expected<llvm::BasicBlock *> allocateAndInitPrivateVars(
llvm::Value *llvmPrivateVar = builder.CreateAlloca(
llvmAllocType, /*ArraySize=*/nullptr, "omp.private.alloc");
- llvm::Error err = initPrivateVar(
+ llvm::Expected<llvm::Value *> privateVarOrError = initPrivateVar(
builder, moduleTranslation, privDecl, mlirPrivVar, blockArg,
- llvmPrivateVar, llvmPrivateVars, privInitBlock, mappedPrivateVars);
- if (err)
+ llvmPrivateVar, privInitBlock, mappedPrivateVars);
+ if (auto err = privateVarOrError.takeError())
return err;
+ llvmPrivateVars.push_back(privateVarOrError.get());
+ moduleTranslation.mapValue(blockArg, privateVarOrError.get());
}
return afterAllocas;
}
@@ -1730,6 +1730,126 @@ buildDependData(std::optional<ArrayAttr> dependKinds, OperandRange dependVars,
}
}
+static bool privatizerReadsSourceVariable(omp::PrivateClauseOp &priv) {
+ if (priv.getDataSharingType() == omp::DataSharingClauseType::FirstPrivate)
+ return true;
+
+ Region &initRegion = priv.getInitRegion();
+ if (initRegion.empty())
+ return false;
+
+ BlockArgument sourceVariable = priv.getInitMoldArg();
+ if (!sourceVariable)
+ return false;
+ return !sourceVariable.use_empty();
+}
+
+namespace {
+/// TaskContextStructManager takes care of creating and freeing a structure
+/// containing information needed by the task body to execute.
+class TaskContextStructManager {
+public:
+ TaskContextStructManager(llvm::IRBuilderBase &builder,
+ LLVM::ModuleTranslation &moduleTranslation,
+ MutableArrayRef<omp::PrivateClauseOp> privateDecls)
+ : builder{builder}, moduleTranslation{moduleTranslation},
+ privateDecls{privateDecls} {}
+
+ /// Creates a heap allocated struct containing space for each private
+ /// variable. Returns nullptr if there are is no struct needed. Invariant:
+ /// privateVarTypes, privateDecls, and the elements of the structure should
+ /// all have the same order (although privateDecls which do not read from the
+ /// mold argument are skipped).
+ void generateTaskContextStruct();
+
+ /// Create GEPs to access each member of the structure representing a private
+ /// variable, adding them to llvmPrivateVars. Null values are added where
+ /// private decls were skipped so that the ordering continues to match the
+ /// private decls.
+ void createGEPsToPrivateVars(SmallVectorImpl<llvm::Value *> &llvmPrivateVars);
+
+ /// De-allocate the task context structure.
+ void freeStructPtr();
+
+ llvm::Value *getStructPtr() { return structPtr; }
+
+private:
+ llvm::IRBuilderBase &builder;
+ LLVM::ModuleTranslation &moduleTranslation;
+ MutableArrayRef<omp::PrivateClauseOp> privateDecls;
+
+ /// The type of each member of the structure, in order.
+ SmallVector<llvm::Type *> privateVarTypes;
+
+ /// A pointer to the structure containing context for this task.
+ llvm::Value *structPtr = nullptr;
+ /// The type of the structure
+ llvm::Type *structTy = nullptr;
+};
+} // namespace
+
+void TaskContextStructManager::generateTaskContextStruct() {
+ if (privateDecls.empty())
+ return;
+ privateVarTypes.reserve(privateDecls.size());
+
+ for (omp::PrivateClauseOp &privOp : privateDecls) {
+ // Skip private variables which can safely be allocated and initialised
+ // inside of the task
+ if (!privatizerReadsSourceVariable(privOp))
+ continue;
+ Type mlirType = privOp.getType();
+ privateVarTypes.push_back(moduleTranslation.convertType(mlirType));
+ }
+
+ structTy = llvm::StructType::get(moduleTranslation.getLLVMContext(),
+ privateVarTypes);
+
+ llvm::DataLayout dataLayout =
+ builder.GetInsertBlock()->getModule()->getDataLayout();
+ llvm::Type *intPtrTy = builder.getIntPtrTy(dataLayout);
+ llvm::Constant *allocSize = llvm::ConstantExpr::getSizeOf(structTy);
+
+ // Heap allocate the structure
+ structPtr = builder.CreateMalloc(intPtrTy, structTy, allocSize,
+ /*ArraySize=*/nullptr, /*MallocF=*/nullptr,
+ "omp.task.context_ptr");
+}
+
+void TaskContextStructManager::createGEPsToPrivateVars(
+ SmallVectorImpl<llvm::Value *> &llvmPrivateVars) {
+ if (!structPtr) {
+ assert(privateVarTypes.empty());
+ return;
+ }
+
+ // Create GEPs for each struct member and initialize llvmPrivateVars to point
+ llvmPrivateVars.reserve(privateVarTypes.size());
+ llvm::Value *zero = builder.getInt32(0);
+ unsigned i = 0;
+ for (auto privDecl : privateDecls) {
+ if (!privatizerReadsSourceVariable(privDecl)) {
+ // Handle this inside of the task so we don't pass unnessecary vars in
+ llvmPrivateVars.push_back(nullptr);
+ continue;
+ }
+ llvm::Value *iVal = builder.getInt32(i);
+ llvm::Value *gep = builder.CreateGEP(structTy, structPtr, {zero, iVal});
+ llvmPrivateVars.push_back(gep);
+ i += 1;
+ }
+}
+
+void TaskContextStructManager::freeStructPtr() {
+ if (!structPtr)
+ return;
+
+ llvm::IRBuilderBase::InsertPointGuard guard{builder};
+ // Ensure we don't put the call to free() after the terminator
+ builder.SetInsertPoint(builder.GetInsertBlock()->getTerminator());
+ builder.CreateFree(structPtr);
+}
+
/// Converts an OpenMP task construct into LLVM IR using OpenMPIRBuilder.
static LogicalResult
convertOmpTaskOp(omp::TaskOp taskOp, llvm::IRBuilderBase &builder,
@@ -1747,6 +1867,8 @@ convertOmpTaskOp(omp::TaskOp taskOp, llvm::IRBuilderBase &builder,
mlirPrivateVars.reserve(privateBlockArgs.size());
llvmPrivateVars.reserve(privateBlockArgs.size());
collectPrivatizationDecls(taskOp, privateDecls);
+ TaskContextStructManager taskStructMgr{builder, moduleTranslation,
+ privateDecls};
for (mlir::Value privateVar : taskOp.getPrivateVars())
mlirPrivateVars.push_back(privateVar);
@@ -1796,27 +1918,54 @@ convertOmpTaskOp(omp::TaskOp taskOp, llvm::IRBuilderBase &builder,
// Allocate and initialize private variables
// TODO: package private variables up in a structure
builder.SetInsertPoint(initBlock->getTerminator());
- for (auto [privDecl, mlirPrivVar, blockArg] :
- llvm::zip_equal(privateDecls, mlirPrivateVars, privateBlockArgs)) {
- llvm::Type *llvmAllocType =
- moduleTranslation.convertType(privDecl.getType());
- // Allocations:
- builder.SetInsertPoint(allocaIP.getBlock()->getTerminator());
- llvm::Value *llvmPrivateVar = builder.CreateAlloca(
- llvmAllocType, /*ArraySize=*/nullptr, "omp.private.alloc");
+ // Create task variable structure
+ llvm::SmallVector<llvm::Value *> privateVarAllocations;
+ taskStructMgr.generateTaskContextStruct();
+ // GEPs so that we can initialize the variables. Don't use these GEPs inside
+ // of the body otherwise it will be the GEP not the struct which is fowarded
+ // to the outlined function. GEPs forwarded in this way are passed in a
+ // stack-allocated (by OpenMPIRBuilder) structure which is not safe for tasks
+ // which may not be executed until after the current stack frame goes out of
+ // scope.
+ taskStructMgr.createGEPsToPrivateVars(privateVarAllocations);
+
+ for (auto [privDecl, mlirPrivVar, blockArg, llvmPrivateVarAlloc] :
+ llvm::zip_equal(privateDecls, mlirPrivateVars, privateBlockArgs,
+ privateVarAllocations)) {
+ if (!llvmPrivateVarAlloc)
+ // to be handled inside the task
+ continue;
- // builder.SetInsertPoint(initBlock->getTerminator());
- auto err =
+ llvm::Expected<llvm::Value *> privateVarOrErr =
initPrivateVar(builder, moduleTranslation, privDecl, mlirPrivVar,
- blockArg, llvmPrivateVar, llvmPrivateVars, initBlock);
- if (err)
+ blockArg, llvmPrivateVarAlloc, initBlock);
+ if (auto err = privateVarOrErr.takeError())
return handleError(std::move(err), *taskOp.getOperation());
+
+ llvm::IRBuilderBase::InsertPointGuard guard(builder);
+ builder.SetInsertPoint(builder.GetInsertBlock()->getTerminator());
+
+ // TODO: this is a bit of a hack for Fortran character boxes
+ if ((privateVarOrErr.get() != llvmPrivateVarAlloc) &&
+ !mlir::isa<LLVM::LLVMPointerType>(blockArg.getType())) {
+ builder.CreateStore(privateVarOrErr.get(), llvmPrivateVarAlloc);
+ // Load it so we have the value pointed to by the GEP
+ llvmPrivateVarAlloc = builder.CreateLoad(privateVarOrErr.get()->getType(),
+ llvmPrivateVarAlloc);
+ }
+ assert(llvmPrivateVarAlloc->getType() ==
+ moduleTranslation.convertType(blockArg.getType()));
+
+ // Mapping blockArg -> llvmPrivateVarAlloc is done inside the body callback
+ // so that OpenMPIRBuilder doesn't try to pass each GEP address through a
+ // stack allocated structure.
}
// firstprivate copy region
if (failed(initFirstPrivateVars(builder, moduleTranslation, mlirPrivateVars,
- llvmPrivateVars, privateDecls, copyBlock)))
+ privateVarAllocations, privateDecls,
+ copyBlock)))
return llvm::failure();
// Set up for call to createTask()
@@ -1824,8 +1973,55 @@ convertOmpTaskOp(omp::TaskOp taskOp, llvm::IRBuilderBase &builder,
auto bodyCB = [&](InsertPointTy allocaIP,
InsertPointTy codegenIP) -> llvm::Error {
+ // Save the alloca insertion point on ModuleTranslation stack for use in
+ // nested regions.
+ LLVM::ModuleTranslation::SaveStack<OpenMPAllocaStackFrame> frame(
+ moduleTranslation, allocaIP);
+
// translate the body of the task:
builder.restoreIP(codegenIP);
+
+ llvm::BasicBlock *privInitBlock = nullptr;
+ for (auto [blockArg, privDecl, mlirPrivVar] :
+ llvm::zip_equal(privateBlockArgs, privateDecls, mlirPrivateVars)) {
+ if (privatizerReadsSourceVariable(privDecl))
+ // This is handled before the task executes
+ continue;
+
+ auto codegenInsertionPt = builder.saveIP();
+ llvm::Type *llvmAllocType =
+ moduleTranslation.convertType(privDecl.getType());
+ builder.SetInsertPoint(allocaIP.getBlock()->getTerminator());
+ llvm::Value *llvmPrivateVar = builder.CreateAlloca(
+ llvmAllocType, /*ArraySize=*/nullptr, "omp.private.alloc");
+
+ llvm::Expected<llvm::Value *> privateVarOrError =
+ initPrivateVar(builder, moduleTranslation, privDecl, mlirPrivVar,
+ blockArg, llvmPrivateVar, privInitBlock);
+ if (auto err = privateVarOrError.takeError())
+ return err;
+ moduleTranslation.mapValue(blockArg, privateVarOrError.get());
+ builder.restoreIP(codegenInsertionPt);
+ }
+
+ // Find and map the addresses of each variable within the task context
+ // structure
+ taskStructMgr.createGEPsToPrivateVars(llvmPrivateVars);
+ for (auto [blockArg, llvmPrivateVar] :
+ llvm::zip_equal(privateBlockArgs, llvmPrivateVars)) {
+ if (!llvmPrivateVar)
+ // This was handled above
+ continue;
+ // Fix broken pass-by-value case for Fortran character boxes
+ if (!mlir::isa<LLVM::LLVMPointerType>(blockArg.getType())) {
+ llvmPrivateVar = builder.CreateLoad(
+ moduleTranslation.convertType(blockArg.getType()), llvmPrivateVar);
+ }
+ assert(llvmPrivateVar->getType() ==
+ moduleTranslation.convertType(blockArg.getType()));
+ moduleTranslation.mapValue(blockArg, llvmPrivateVar);
+ }
+
auto continuationBlockOrError = convertOmpOpRegions(
taskOp.getRegion(), "omp.task.region", builder, moduleTranslation);
if (failed(handleError(continuationBlockOrError, *taskOp)))
@@ -1837,6 +2033,9 @@ convertOmpTaskOp(omp::TaskOp taskOp, llvm::IRBuilderBase &builder,
llvmPrivateVars, privateDecls)))
return llvm::make_error<PreviouslyReportedError>();
+ // Free heap allocated task context structure at the end of the task.
+ taskStructMgr.freeStructPtr();
+
return llvm::Error::success();
};
diff --git a/mlir/test/Target/LLVMIR/openmp-llvm.mlir b/mlir/test/Target/LLVMIR/openmp-llvm.mlir
index 503ddf10c80cdc..ba05d97c9a4eda 100644
--- a/mlir/test/Target/LLVMIR/openmp-llvm.mlir
+++ b/mlir/test/Target/LLVMIR/openmp-llvm.mlir
@@ -2795,9 +2795,10 @@ llvm.func @par_task_(%arg0: !llvm.ptr {fir.bindc_name = "a"}) {
// CHECK: call i32 @__kmpc_omp_task({{.*}}, ptr %[[TASK_ALLOC]])
// CHECK: define internal void @[[task_outlined_fn]](i32 %[[GLOBAL_TID_VAL:.*]], ptr %[[STRUCT_ARG:.*]])
// CHECK: %[[LOADED_STRUCT_PTR:.*]] = load ptr, ptr %[[STRUCT_ARG]], align 8
-// CHECK: %[[GEP_STRUCTARG:.*]] = getelementptr { ptr, ptr }, ptr %[[LOADED_STRUCT_PTR]], i32 0, i32 0
+// CHECK: %[[GEP_STRUCTARG:.*]] = getelementptr { ptr }, ptr %[[LOADED_STRUCT_PTR]], i32 0, i32 0
// CHECK: %[[LOADGEP_STRUCTARG:.*]] = load ptr, ptr %[[GEP_STRUCTARG]], align 8
-// CHECK: call void ({{.*}}) @__kmpc_fork_call({{.*}}, ptr @[[parallel_outlined_fn:.+]], ptr %[[LOADGEP_STRUCTARG]])
+// CHEKC: %[[NEW_STRUCTARG:.*]] = alloca { ptr }, align 8
+// CHECK: call void ({{.*}}) @__kmpc_fork_call({{.*}}, ptr @[[parallel_outlined_fn:.+]],
// CHECK: define internal void @[[parallel_outlined_fn]]
// -----
@@ -2825,10 +2826,11 @@ llvm.func @task(%arg0 : !llvm.ptr) {
// CHECK-LABEL: @task
// CHECK-SAME: (ptr %[[ARG:.*]])
// CHECK: %[[STRUCT_ARG:.*]] = alloca { ptr }, align 8
-// CHECK: %[[OMP_PRIVATE_ALLOC:.*]] = alloca i32, align 4
// ...
// CHECK: br label %omp.private.init
// CHECK: omp.private.init:
+// CHECK: %[[OMP_TASK_CONTEXT_PTR:.*]] = tail call ptr @malloc(
+// CHECK: %[[OMP_PRIVATE_ALLOC:.*]] = getelementptr { i32 }, ptr %[[OMP_TASK_CONTEXT_PTR]]
// CHECK: br label %omp.private.copy1
// CHECK: omp.private.copy1:
// CHECK: %[[LOADED:.*]] = load i32, ptr %[[ARG]], align 4
@@ -2846,12 +2848,13 @@ llvm.func @task(%arg0 : !llvm.ptr) {
// CHECK: %[[VAL_14:.*]] = load ptr, ptr %[[VAL_13]], align 8
// CHECK: br label %task.body
// CHECK: task.body: ; preds = %task.alloca
+// CHECK: %[[VAL_15:.*]] = getelementptr { i32 }, ptr %[[VAL_14]], i32 0, i32 0
// CHECK: br label %omp.task.region
// CHECK: omp.task.region: ; preds = %task.body
-// CHECK: call void @foo(ptr %[[VAL_14]])
+// CHECK: call void @foo(ptr %[[VAL_15]])
// CHECK: br label %omp.region.cont
// CHECK: omp.region.cont: ; preds = %omp.task.region
-// CHECK: call void @destroy(ptr %[[VAL_14]])
+// CHECK: call void @destroy(ptr %[[VAL_15]])
// CHECK: br label %task.exit.exitStub
// CHECK: task.exit.exitStub: ; preds = %omp.region.cont
// CHECK: ret void
diff --git a/mlir/test/Target/LLVMIR/openmp-task-privatization.mlir b/mlir/test/Target/LLVMIR/openmp-task-privatization.mlir
new file mode 100644
index 00000000000000..af469460f3cee0
--- /dev/null
+++ b/mlir/test/Target/LLVMIR/openmp-task-privatization.mlir
@@ -0,0 +1,81 @@
+// RUN: mlir-translate -mlir-to-llvmir %s | FileCheck %s
+
+omp.private {type = private} @privatizer : i32
+
+omp.private {type = firstprivate} @firstprivatizer : i32 copy {
+^bb0(%arg0: !llvm.ptr, %arg1: !llvm.ptr):
+ %0 = llvm.load %arg0 : !llvm.ptr -> i32
+ llvm.store %0, %arg1 : i32, !llvm.ptr
+ omp.yield(%arg1 : !llvm.ptr)
+}
+
+llvm.func @task_privatization_test() {
+ %c0 = llvm.mlir.constant(0: i32) : i32
+ %c1 = llvm.mlir.constant(1: i32) : i32
+ %0 = llvm.alloca %c1 x i32 : (i32) -> !llvm.ptr
+ %1 = llvm.alloca %c1 x i32 : (i32) -> !llvm.ptr
+ llvm.store %c0, %0 : i32, !llvm.ptr
+ llvm.store %c1, %1 : i32, !llvm.ptr
+
+ omp.task private(@privatizer %0 -> %arg0, @firstprivatizer %1 -> %arg1 : !llvm.ptr, !llvm.ptr) {
+ %2 = llvm.load %arg1 : !llvm.ptr -> i32
+ llvm.store %2, %arg0 : i32, !llvm.ptr
+ omp.terminator
+ }
+ llvm.return
+}
+
+// CHECK: define void @task_privatization_test()
+// CHECK: %[[STRUCT_ARG:.*]] = alloca { ptr }, align 8
+// CHECK: %[[VAL_0:.*]] = alloca i32, align 4
+// CHECK: %[[VAL_1:.*]] = alloca i32, align 4
+// CHECK: store i32 0, ptr %[[VAL_0]], align 4
+// CHECK: store i32 1, ptr %[[VAL_1]], align 4
+// CHECK: br label %entry
+// CHECK: entry:
+// CHECK: br label %omp.private.init
+// CHECK: omp.private.init:
+// CHECK: %[[VAL_5:.*]] = tail call ptr @malloc(i64 ptrtoint (ptr getelementptr ([[STRUCT_KMP_PRIVATES_T:.*]]...
[truncated]
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks Tom! I have a few comments. Not much of an expert when it comes to tasks so I might have missed something worth noting.
mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
Outdated
Show resolved
Hide resolved
mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
Outdated
Show resolved
Hide resolved
mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
Outdated
Show resolved
Hide resolved
mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for the PR, Tom. I have some minor comments that may amount to nitpicking.
mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
Outdated
Show resolved
Hide resolved
mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
Outdated
Show resolved
Hide resolved
mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
Outdated
Show resolved
Hide resolved
mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
Outdated
Show resolved
Hide resolved
dd07fa7
to
081fb2e
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you Tom, I have some minor comments but otherwise this LGTM.
mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
Outdated
Show resolved
Hide resolved
mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
Outdated
Show resolved
Hide resolved
mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
Outdated
Show resolved
Hide resolved
mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
Outdated
Show resolved
Hide resolved
mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
Outdated
Show resolved
Hide resolved
mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
Outdated
Show resolved
Hide resolved
mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
Outdated
Show resolved
Hide resolved
// structure | ||
taskStructMgr.createGEPsToPrivateVars(); | ||
llvm::copy(taskStructMgr.getLLVMPrivateVarGEPs(), | ||
std::back_inserter(llvmPrivateVars)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could this cause null
pointer dereferences on other uses of llvmPrivateVars
after the new loop introduced below? For example, that list is passed to cleanupPrivateVars
, which doesn't seem to check for null
elements.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great point. Yeah I think it could have done. I have fixed this in 459d2d5
f8b1812
to
154b4b5
Compare
081fb2e
to
459d2d5
Compare
This is now rebased on top of current main and ready for review. Apologies for the delay, I was on holiday and rebasing required rethinking how insertion points worked. @ergawy I changed the signature of |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM! Thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It appears a couple of the changes got somehow lost, but no need for another review from me. LGTM, thanks!
mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
Outdated
Show resolved
Hide resolved
mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
Outdated
Show resolved
Hide resolved
mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
Outdated
Show resolved
Hide resolved
Apologies for the comments I missed. I'm not sure how that happened. Thanks for carefully checking each one. |
…ext struct See RFC: https://discourse.llvm.org/t/rfc-openmp-supporting-delayed-task-execution-with-firstprivate-variables/83084 The aim here is to ensure that tasks which are not executed for a while after they are created do not try to reference any data which are now out of scope. This is done by packing the data referred to by the task into a heap allocated structure (freed at the end of the task). I decided to create the task context structure in OpenMPToLLVMIRTranslation instead of adapting how it is done CodeExtractor (via OpenMPIRBuilder] because CodeExtractor is (at least in theory) generic code which could have other unrelated uses.
There's no need to put the variable in the context structure if it can be initialized without reading from the mold variable. This leads to much simpler code generation for nested tasks which only privatize simple scalar variables and do not use firstprivate. This works around the failure in 0226_0013 (Fujitsu test).
ef5a597
to
18daec3
Compare
…ext struct (llvm#125307) See RFC: https://discourse.llvm.org/t/rfc-openmp-supporting-delayed-task-execution-with-firstprivate-variables/83084 The aim here is to ensure that tasks which are not executed for a while after they are created do not try to reference any data which are now out of scope. This is done by packing the data referred to by the task into a heap allocated structure (freed at the end of the task). I decided to create the task context structure in OpenMPToLLVMIRTranslation instead of adapting how it is done CodeExtractor (via OpenMPIRBuilder] because CodeExtractor is (at least in theory) generic code which could have other unrelated uses.
See RFC:
https://discourse.llvm.org/t/rfc-openmp-supporting-delayed-task-execution-with-firstprivate-variables/83084
The aim here is to ensure that tasks which are not executed for a while after they are created do not try to reference any data which are now out of scope. This is done by packing the data referred to by the task into a heap allocated structure (freed at the end of the task).
I decided to create the task context structure in
OpenMPToLLVMIRTranslation instead of adapting how it is done CodeExtractor (via OpenMPIRBuilder] because CodeExtractor is (at least in theory) generic code which could have other unrelated uses.