[llvm] [SPIRV] Preserve Workgroup execution scope for OpenCL barriers (PR #228961)
via llvm-commits
llvm-commits at lists.llvm.org
Sun Oct 4 19:12:18 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-backend-spir-v
Author: Alexander Shaposhnikov (alexander-shaposhnikov)
<details>
<summary>Changes</summary>
Consider a work-group of 64 work-items split into two subgroups of 32:
work_group_barrier(CLK_GLOBAL_MEM_FENCE, memory_scope_sub_group);
All 64 work-items must reach the barrier before any can continue. The memory-scope argument only controls memory ordering.
The lowering starts with Scope = Workgroup and maps the argument to MemScope = Subgroup. However, the old condition includes !(MemFlags & CLK_LOCAL_MEM_FENCE). This is true for a global-only fence, so Scope = MemScope overwrites the execution scope with Subgroup. The resulting OpControlBarrier lets one subgroup continue before the other has arrived.
Restrict that assignment to Opcode == OpMemoryBarrier. work_group_barrier emits OpControlBarrier, so its execution scope now stays Workgroup while its memory scope remains Subgroup. OpMemoryBarrier has no execution-scope operand: its first operand is the memory scope, so it still needs the assignment.
P.S.
The local-memory fence is still handled earlier:
if (MemFlags & SPIRV::CLK_LOCAL_MEM_FENCE)
MemSemantics |= SPIRV::MemorySemantics::WorkgroupMemory;
---
Full diff: https://github.com/llvm/llvm-project/pull/228961.diff
3 Files Affected:
- (modified) llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp (+1-2)
- (modified) llvm/test/CodeGen/SPIRV/extensions/SPV_INTEL_split_barrier/split_work_group_barrier_20.ll (+6)
- (modified) llvm/test/CodeGen/SPIRV/transcoding/OpenCL/work_group_barrier.ll (+14)
``````````diff
diff --git a/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp b/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
index 84196697060e3..c73bb4fab5930 100644
--- a/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
@@ -980,8 +980,7 @@ static bool buildBarrierInst(const SPIRV::IncomingCall *Call, unsigned Opcode,
SPIRV::CLMemoryScope CLScope =
static_cast<SPIRV::CLMemoryScope>(getIConstVal(ScopeArg, MRI));
MemScope = getSPIRVScope(CLScope);
- if (!(MemFlags & SPIRV::CLK_LOCAL_MEM_FENCE) ||
- (Opcode == SPIRV::OpMemoryBarrier))
+ if (Opcode == SPIRV::OpMemoryBarrier)
Scope = MemScope;
if (CLScope == static_cast<unsigned>(Scope))
ScopeReg = Call->Arguments[1];
diff --git a/llvm/test/CodeGen/SPIRV/extensions/SPV_INTEL_split_barrier/split_work_group_barrier_20.ll b/llvm/test/CodeGen/SPIRV/extensions/SPV_INTEL_split_barrier/split_work_group_barrier_20.ll
index 2d94e1f35094b..fe0a59a077a1c 100644
--- a/llvm/test/CodeGen/SPIRV/extensions/SPV_INTEL_split_barrier/split_work_group_barrier_20.ll
+++ b/llvm/test/CodeGen/SPIRV/extensions/SPV_INTEL_split_barrier/split_work_group_barrier_20.ll
@@ -24,6 +24,8 @@
;; intel_work_group_barrier_wait(CLK_LOCAL_MEM_FENCE, memory_scope_all_svm_devices);
;; intel_work_group_barrier_arrive(CLK_LOCAL_MEM_FENCE, memory_scope_sub_group);
;; intel_work_group_barrier_wait(CLK_LOCAL_MEM_FENCE, memory_scope_sub_group);
+;; intel_work_group_barrier_arrive(CLK_GLOBAL_MEM_FENCE, memory_scope_device);
+;; intel_work_group_barrier_wait(CLK_GLOBAL_MEM_FENCE, memory_scope_device);
;;}
; RUN: not llc -O0 -mtriple=spirv64-unknown-unknown %s -o %t.spvt 2>&1 | FileCheck %s --check-prefix=CHECK-ERROR
@@ -88,6 +90,8 @@
; CHECK: OpControlBarrierWaitINTEL %[[#SCOPE_WORK_GROUP]] %[[#SCOPE_CROSS_DEVICE]] %[[#ACQUIRE_LOCAL]]
; CHECK: OpControlBarrierArriveINTEL %[[#SCOPE_WORK_GROUP]] %[[#SCOPE_SUBGROUP]] %[[#RELEASE_LOCAL]]
; CHECK: OpControlBarrierWaitINTEL %[[#SCOPE_WORK_GROUP]] %[[#SCOPE_SUBGROUP]] %[[#ACQUIRE_LOCAL]]
+; CHECK: OpControlBarrierArriveINTEL %[[#SCOPE_WORK_GROUP]] %[[#SCOPE_DEVICE]] %[[#RELEASE_GLOBAL]]
+; CHECK: OpControlBarrierWaitINTEL %[[#SCOPE_WORK_GROUP]] %[[#SCOPE_DEVICE]] %[[#ACQUIRE_GLOBAL]]
; Function Attrs: convergent norecurse nounwind
define dso_local spir_kernel void @test(ptr addrspace(1) nocapture noundef readnone align 4 %0) local_unnamed_addr #0 !kernel_arg_addr_space !4 !kernel_arg_access_qual !5 !kernel_arg_type !6 !kernel_arg_base_type !6 !kernel_arg_type_qual !7 {
@@ -111,6 +115,8 @@ define dso_local spir_kernel void @test(ptr addrspace(1) nocapture noundef readn
tail call spir_func void @_Z29intel_work_group_barrier_waitj12memory_scope(i32 noundef 1, i32 noundef 3) #2
tail call spir_func void @_Z31intel_work_group_barrier_arrivej12memory_scope(i32 noundef 1, i32 noundef 4) #2
tail call spir_func void @_Z29intel_work_group_barrier_waitj12memory_scope(i32 noundef 1, i32 noundef 4) #2
+ tail call spir_func void @_Z31intel_work_group_barrier_arrivej12memory_scope(i32 noundef 2, i32 noundef 2) #2
+ tail call spir_func void @_Z29intel_work_group_barrier_waitj12memory_scope(i32 noundef 2, i32 noundef 2) #2
ret void
}
diff --git a/llvm/test/CodeGen/SPIRV/transcoding/OpenCL/work_group_barrier.ll b/llvm/test/CodeGen/SPIRV/transcoding/OpenCL/work_group_barrier.ll
index a3c79d554a588..dd0ede4b106bd 100644
--- a/llvm/test/CodeGen/SPIRV/transcoding/OpenCL/work_group_barrier.ll
+++ b/llvm/test/CodeGen/SPIRV/transcoding/OpenCL/work_group_barrier.ll
@@ -1,4 +1,5 @@
; RUN: llc -O0 -mtriple=spirv32-unknown-unknown %s -o - | FileCheck %s --check-prefix=CHECK-SPIRV
+; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s --check-prefix=CHECK-SPIRV
; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv32-unknown-unknown %s -o - -filetype=obj | spirv-val %}
;; This test checks that the backend is capable to correctly translate
@@ -22,6 +23,10 @@
;;
;; barrier should also work (preserved for backward compatibility)
;; barrier(CLK_GLOBAL_MEM_FENCE);
+;; work_group_barrier(CLK_GLOBAL_MEM_FENCE, memory_scope_work_group);
+;; work_group_barrier(CLK_GLOBAL_MEM_FENCE, memory_scope_device);
+;; work_group_barrier(CLK_GLOBAL_MEM_FENCE, memory_scope_all_svm_devices);
+;; work_group_barrier(CLK_GLOBAL_MEM_FENCE, memory_scope_sub_group);
;; }
;;
;; __kernel void test_barrier_non_const_flags(cl_mem_fence_flags flags, memory_scope scope) {
@@ -77,6 +82,11 @@
; CHECK-SPIRV: OpControlBarrier %[[#SCOPE_WORK_GROUP]] %[[#SCOPE_CROSS_DEVICE]] %[[#LOCAL]]
; CHECK-SPIRV: OpControlBarrier %[[#SCOPE_WORK_GROUP]] %[[#SCOPE_SUBGROUP]] %[[#LOCAL]]
; CHECK-SPIRV: OpControlBarrier %[[#SCOPE_WORK_GROUP]] %[[#SCOPE_WORK_GROUP]] %[[#GLOBAL]]
+; Explicit memory scope must not change the Workgroup execution scope.
+; CHECK-SPIRV: OpControlBarrier %[[#SCOPE_WORK_GROUP]] %[[#SCOPE_WORK_GROUP]] %[[#GLOBAL]]
+; CHECK-SPIRV: OpControlBarrier %[[#SCOPE_WORK_GROUP]] %[[#SCOPE_DEVICE]] %[[#GLOBAL]]
+; CHECK-SPIRV: OpControlBarrier %[[#SCOPE_WORK_GROUP]] %[[#SCOPE_CROSS_DEVICE]] %[[#GLOBAL]]
+; CHECK-SPIRV: OpControlBarrier %[[#SCOPE_WORK_GROUP]] %[[#SCOPE_SUBGROUP]] %[[#GLOBAL]]
define dso_local spir_kernel void @test_barrier_const_flags() local_unnamed_addr {
entry:
@@ -92,6 +102,10 @@ entry:
tail call spir_func void @_Z18work_group_barrierj12memory_scope(i32 noundef 1, i32 noundef 3)
tail call spir_func void @_Z18work_group_barrierj12memory_scope(i32 noundef 1, i32 noundef 4)
tail call spir_func void @_Z7barrierj(i32 noundef 2)
+ tail call spir_func void @_Z18work_group_barrierj12memory_scope(i32 noundef 2, i32 noundef 1)
+ tail call spir_func void @_Z18work_group_barrierj12memory_scope(i32 noundef 2, i32 noundef 2)
+ tail call spir_func void @_Z18work_group_barrierj12memory_scope(i32 noundef 2, i32 noundef 3)
+ tail call spir_func void @_Z18work_group_barrierj12memory_scope(i32 noundef 2, i32 noundef 4)
ret void
}
``````````
</details>
https://github.com/llvm/llvm-project/pull/228961
More information about the llvm-commits
mailing list