[Mlir-commits] [mlir] 188d13d - [mlir][OpenMP] don't add compiler-generated barrier in single threaded code (#174105)
llvmlistbot at llvm.org
llvmlistbot at llvm.org
Tue Jan 6 02:22:45 PST 2026
Author: Tom Eccles
Date: 2026-01-06T10:22:41Z
New Revision: 188d13db2040ac41485d95e3b16d25873ad77a91
URL: https://github.com/llvm/llvm-project/commit/188d13db2040ac41485d95e3b16d25873ad77a91
DIFF: https://github.com/llvm/llvm-project/commit/188d13db2040ac41485d95e3b16d25873ad77a91.diff
LOG: [mlir][OpenMP] don't add compiler-generated barrier in single threaded code (#174105)
We add barriers to the firstprivate copy region when they are required
to avoid a race condition with the lastprivate clause.
The problem is that these barriers are added by the compiler not implied
by user code so it is the compiler's problem to avoid deadlock.
I came across a testcase whilst working on taskloop support that looks a
bit like this
```
!$omp parallel
!$omp single
!$omp taskloop firstprivate(a) lastprivate(a)
...
!$omp end single
!$omp end parallel
```
This is so that there are multiple threads for the generated tasks to be
distributed over, but we don't generate the tasks afresh in every
thread.
The problem comes when the taskloop requires a barrier to prevent the
datarace between firstprivate and lastprivate. This barrier will then be
generated inside of SINGLE and so only one thread will encounter the
barrier: leading to a deadlock.
This patch works around the problem by detecting this situation
statically and then not generating the barrier. There are cases where we
cannot detect this statically (e.g. if the TASKLOOP is inside a function
call inside of SINGLE). The program will still deadlock in this case
after my patch. I'm unsure what the solution would be for that case. I
want to fix this simple case in LLVM 22 before engaging in a longer
discussion as to whether there is a better way to handle the more
general case.
Testing using wsloop because I want to land this (or not) independently
of taskloop. Note that for wsloop it would be up to the programmer to
remember to use the nowait clause, but nowait cannot be used to control
generation of this barrier because it refers to the barrier after the
construct not after firstprivate copyin (before the construct
execution).
Added:
mlir/test/Target/LLVMIR/openmp-private-barrier-single.mlir
Modified:
mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
Removed:
################################################################################
diff --git a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
index c37af8d7b1673..b7630bd97dca4 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
@@ -1708,6 +1708,25 @@ allocatePrivateVars(llvm::IRBuilderBase &builder,
return afterAllocas;
}
+/// This can't always be determined statically, but when we can, it is good to
+/// avoid generating compiler-added barriers which will deadlock the program.
+static bool opIsInSingleThread(mlir::Operation *op) {
+ while (mlir::Operation *parent = op->getParentOp()) {
+ if (mlir::isa<omp::SingleOp, omp::CriticalOp>(parent))
+ return true;
+
+ // e.g.
+ // omp.single {
+ // omp.parallel {
+ // op
+ // }
+ // }
+ if (mlir::isa<omp::ParallelOp>(parent))
+ return false;
+ }
+ return false;
+}
+
static LogicalResult copyFirstPrivateVars(
mlir::Operation *op, llvm::IRBuilderBase &builder,
LLVM::ModuleTranslation &moduleTranslation,
@@ -1761,7 +1780,7 @@ static LogicalResult copyFirstPrivateVars(
moduleTranslation.forgetMapping(copyRegion);
}
- if (insertBarrier) {
+ if (insertBarrier && !opIsInSingleThread(op)) {
llvm::OpenMPIRBuilder *ompBuilder = moduleTranslation.getOpenMPBuilder();
llvm::OpenMPIRBuilder::InsertPointOrErrorTy res =
ompBuilder->createBarrier(builder.saveIP(), llvm::omp::OMPD_barrier);
diff --git a/mlir/test/Target/LLVMIR/openmp-private-barrier-single.mlir b/mlir/test/Target/LLVMIR/openmp-private-barrier-single.mlir
new file mode 100644
index 0000000000000..9dd66a34a17f8
--- /dev/null
+++ b/mlir/test/Target/LLVMIR/openmp-private-barrier-single.mlir
@@ -0,0 +1,47 @@
+// RUN: mlir-translate -mlir-to-llvmir %s | FileCheck %s
+
+// Tests that no privatization barrier is added for operations inside of SINGLE.
+// The specific combination here (wsloop inside of single) may not be valid
+// OpenMP. This combination was chosen so that this patch did not have to wait
+// for taskloop support. If it ever becomes a problem having a wsloop inside
+// of single, this can be updated to use taskloop.
+
+omp.private {type = private} @_QFwsloop_privateEi_private_ref_i32 : i32
+
+llvm.func @foo_free(!llvm.ptr)
+
+omp.private {type = firstprivate} @_QFwsloop_privateEc_firstprivate_ref_c8 : !llvm.array<1 x i8> copy {
+^bb0(%arg0: !llvm.ptr, %arg1: !llvm.ptr):
+ %0 = llvm.load %arg0 : !llvm.ptr -> !llvm.array<1 x i8>
+ llvm.store %0, %arg1 : !llvm.array<1 x i8>, !llvm.ptr
+ omp.yield(%arg1 : !llvm.ptr)
+} dealloc {
+^bb0(%arg0: !llvm.ptr):
+ llvm.call @foo_free(%arg0) : (!llvm.ptr) -> ()
+ omp.yield
+}
+
+llvm.func @wsloop_private_(%arg0: !llvm.ptr {fir.bindc_name = "y"}) attributes {fir.internal_name = "_QPwsloop_private", frame_pointer = #llvm.framePointerKind<all>, target_cpu = "x86-64"} {
+ %0 = llvm.mlir.constant(1 : i64) : i64
+ %1 = llvm.alloca %0 x f32 {bindc_name = "x"} : (i64) -> !llvm.ptr
+ %3 = llvm.alloca %0 x i32 {bindc_name = "i"} : (i64) -> !llvm.ptr
+ %5 = llvm.alloca %0 x !llvm.array<1 x i8> {bindc_name = "c"} : (i64) -> !llvm.ptr
+ %6 = llvm.mlir.constant(1 : i32) : i32
+ %7 = llvm.mlir.constant(10 : i32) : i32
+ %8 = llvm.mlir.constant(0 : i32) : i32
+ omp.parallel {
+ omp.single {
+ omp.wsloop private(@_QFwsloop_privateEc_firstprivate_ref_c8 %5 -> %arg1, @_QFwsloop_privateEi_private_ref_i32 %3 -> %arg2 : !llvm.ptr, !llvm.ptr) private_barrier {
+ // CHECK: omp.private.copy:
+ // CHECK-NOT: __kmpc_barrier
+ // CHECK: br label
+ omp.loop_nest (%arg4) : i32 = (%8) to (%7) inclusive step (%6) {
+ omp.yield
+ }
+ }
+ omp.terminator
+ }
+ omp.terminator
+ }
+ llvm.return
+}
More information about the Mlir-commits
mailing list