Skip to content

Commit e01b798

Browse files
committed
Fix indvar pass to skip on unfolding predicates on control convergence operations
1 parent ca84e9e commit e01b798

File tree

3 files changed

+268
-0
lines changed

3 files changed

+268
-0
lines changed

llvm/lib/Transforms/Scalar/IndVarSimplify.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1859,6 +1859,37 @@ bool IndVarSimplify::predicateLoopExits(Loop *L, SCEVExpander &Rewriter) {
18591859
}
18601860
}
18611861

1862+
// If the loop body uses a convergence token defined within the loop, skip
1863+
// predication. This is to avoid changing the convergence behavior of the
1864+
// loop.
1865+
SmallVector<BasicBlock *, 16> blocks = ExitingBlocks;
1866+
SmallVector<Value *, 16> tokens = {};
1867+
size_t index = 0; // Assume Exiting Blocks are sorted.
1868+
while (index < blocks.size()) {
1869+
BasicBlock *BB = blocks[index];
1870+
index++;
1871+
const auto exitingBlockName = BB->getName();
1872+
for (Instruction &I : *BB) {
1873+
// Check if the instruction uses any convergence tokens.
1874+
if (auto *CB = dyn_cast<CallBase>(&I);
1875+
CB && !isa<ConvergenceControlInst>(&I)) {
1876+
auto token = CB->getConvergenceControlToken();
1877+
if (token && llvm::is_contained(tokens, token)) {
1878+
return false;
1879+
}
1880+
}
1881+
if (isa<ConvergenceControlInst>(&I)) {
1882+
tokens.push_back(cast<Value>(&I));
1883+
}
1884+
}
1885+
1886+
for (BasicBlock *Succ : successors(BB)) {
1887+
const auto succName = Succ->getName();
1888+
if (Succ != L->getLoopLatch() && !llvm::is_contained(blocks, Succ))
1889+
blocks.push_back(Succ);
1890+
}
1891+
}
1892+
18621893
bool Changed = false;
18631894
// Finally, do the actual predication for all predicatable blocks. A couple
18641895
// of notes here:
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
2+
; RUN: opt < %s -passes=indvars -indvars-predicate-loops=1 -S | FileCheck %s
3+
4+
; Loop with body using loop convergence token should be skipped by IndVarSimplify.
5+
6+
%"class.hlsl::RWStructuredBuffer" = type { target("spirv.VulkanBuffer", [0 x i32], 12, 1), target("spirv.VulkanBuffer", i32, 12, 1) }
7+
8+
@_ZL3Out = internal global %"class.hlsl::RWStructuredBuffer" poison, align 8
9+
@.str = private unnamed_addr constant [4 x i8] c"Out\00", align 1
10+
11+
declare token @llvm.experimental.convergence.entry() #0
12+
13+
define void @loop() local_unnamed_addr #1 {
14+
; CHECK-LABEL: @loop(
15+
; CHECK-NEXT: entry:
16+
; CHECK-NEXT: [[TMP0:%.*]] = tail call token @llvm.experimental.convergence.entry()
17+
; CHECK-NEXT: [[TMP1:%.*]] = tail call target("spirv.VulkanBuffer", [0 x i32], 12, 1) @llvm.spv.resource.handlefrombinding.tspirv.VulkanBuffer_a0i32_12_1t(i32 0, i32 0, i32 1, i32 0, ptr nonnull @.str)
18+
; CHECK-NEXT: [[TMP2:%.*]] = tail call target("spirv.VulkanBuffer", i32, 12, 1) @llvm.spv.resource.counterhandlefromimplicitbinding.tspirv.VulkanBuffer_i32_12_1t.tspirv.VulkanBuffer_a0i32_12_1t(target("spirv.VulkanBuffer", [0 x i32], 12, 1) [[TMP1]], i32 0, i32 0)
19+
; CHECK-NEXT: store target("spirv.VulkanBuffer", [0 x i32], 12, 1) [[TMP1]], ptr @_ZL3Out, align 8
20+
; CHECK-NEXT: store target("spirv.VulkanBuffer", i32, 12, 1) [[TMP2]], ptr getelementptr inbounds nuw (i8, ptr @_ZL3Out, i64 8), align 8
21+
; CHECK-NEXT: [[TMP3:%.*]] = tail call i32 @llvm.spv.thread.id.in.group.i32(i32 0)
22+
; CHECK-NEXT: br label [[FOR_COND_I:%.*]]
23+
; CHECK: for.cond.i:
24+
; CHECK-NEXT: [[I_0_I:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC_I:%.*]], [[FOR_BODY_I:%.*]] ]
25+
; CHECK-NEXT: [[TMP4:%.*]] = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token [[TMP0]]) ]
26+
; CHECK-NEXT: [[CMP_I:%.*]] = icmp ult i32 [[I_0_I]], 8
27+
; CHECK-NEXT: br i1 [[CMP_I]], label [[FOR_BODY_I]], label [[_Z4LOOPDV3_J_EXIT_LOOPEXIT:%.*]]
28+
; CHECK: for.body.i:
29+
; CHECK-NEXT: [[CMP1_I:%.*]] = icmp eq i32 [[I_0_I]], [[TMP3]]
30+
; CHECK-NEXT: [[INC_I]] = add nuw nsw i32 [[I_0_I]], 1
31+
; CHECK-NEXT: br i1 [[CMP1_I]], label [[IF_THEN_I:%.*]], label [[FOR_COND_I]]
32+
; CHECK: _Z4loopDv3_j.exit.loopexit:
33+
; CHECK-NEXT: br label [[_Z4LOOPDV3_J_EXIT:%.*]]
34+
; CHECK: if.then.i:
35+
; CHECK-NEXT: [[HLSL_WAVE_ACTIVE_MAX2_I:%.*]] = call spir_func i32 @llvm.spv.wave.reduce.umax.i32(i32 [[TMP3]]) [ "convergencectrl"(token [[TMP4]]) ]
36+
; CHECK-NEXT: [[TMP5:%.*]] = tail call noundef align 4 dereferenceable(4) ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.VulkanBuffer_a0i32_12_1t(target("spirv.VulkanBuffer", [0 x i32], 12, 1) [[TMP1]], i32 [[TMP3]])
37+
; CHECK-NEXT: store i32 [[HLSL_WAVE_ACTIVE_MAX2_I]], ptr addrspace(11) [[TMP5]], align 4
38+
; CHECK-NEXT: br label [[_Z4LOOPDV3_J_EXIT]]
39+
; CHECK: _Z4loopDv3_j.exit:
40+
; CHECK-NEXT: ret void
41+
;
42+
entry:
43+
%0 = tail call token @llvm.experimental.convergence.entry()
44+
%1 = tail call target("spirv.VulkanBuffer", [0 x i32], 12, 1) @llvm.spv.resource.handlefrombinding.tspirv.VulkanBuffer_a0i32_12_1t(i32 0, i32 0, i32 1, i32 0, ptr nonnull @.str)
45+
%2 = tail call target("spirv.VulkanBuffer", i32, 12, 1) @llvm.spv.resource.counterhandlefromimplicitbinding.tspirv.VulkanBuffer_i32_12_1t.tspirv.VulkanBuffer_a0i32_12_1t(target("spirv.VulkanBuffer", [0 x i32], 12, 1) %1, i32 0, i32 0)
46+
store target("spirv.VulkanBuffer", [0 x i32], 12, 1) %1, ptr @_ZL3Out, align 8
47+
store target("spirv.VulkanBuffer", i32, 12, 1) %2, ptr getelementptr inbounds nuw (i8, ptr @_ZL3Out, i64 8), align 8
48+
%3 = tail call i32 @llvm.spv.thread.id.in.group.i32(i32 0)
49+
br label %for.cond.i
50+
51+
; Loop:
52+
for.cond.i: ; preds = %for.body.i, %entry
53+
%i.0.i = phi i32 [ 0, %entry ], [ %inc.i, %for.body.i ]
54+
%4 = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token %0) ]
55+
%cmp.i = icmp ult i32 %i.0.i, 8
56+
br i1 %cmp.i, label %for.body.i, label %_Z4loopDv3_j.exit.loopexit
57+
58+
for.body.i: ; preds = %for.cond.i
59+
%cmp1.i = icmp eq i32 %i.0.i, %3
60+
%inc.i = add nuw nsw i32 %i.0.i, 1
61+
br i1 %cmp1.i, label %if.then.i, label %for.cond.i
62+
63+
; Exit blocks
64+
_Z4loopDv3_j.exit.loopexit: ; preds = %for.cond.i
65+
br label %_Z4loopDv3_j.exit
66+
67+
if.then.i: ; preds = %for.body.i
68+
%hlsl.wave.active.max2.i = call spir_func i32 @llvm.spv.wave.reduce.umax.i32(i32 %3) [ "convergencectrl"(token %4) ]
69+
%5 = tail call noundef align 4 dereferenceable(4) ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.VulkanBuffer_a0i32_12_1t(target("spirv.VulkanBuffer", [0 x i32], 12, 1) %1, i32 %3)
70+
store i32 %hlsl.wave.active.max2.i, ptr addrspace(11) %5, align 4
71+
br label %_Z4loopDv3_j.exit
72+
73+
_Z4loopDv3_j.exit: ; preds = %_Z4loopDv3_j.exit.loopexit, %if.then.i
74+
ret void
75+
}
76+
77+
; Function Attrs: mustprogress nofree nosync nounwind willreturn memory(none)
78+
declare i32 @llvm.spv.thread.id.in.group.i32(i32) #2
79+
80+
; Function Attrs: convergent mustprogress nocallback nofree nosync nounwind willreturn memory(none)
81+
declare token @llvm.experimental.convergence.loop() #0
82+
83+
; Function Attrs: convergent mustprogress nocallback nofree nosync nounwind willreturn memory(none)
84+
declare i32 @llvm.spv.wave.reduce.umax.i32(i32) #0
85+
86+
; Function Attrs: mustprogress nocallback nofree nosync nounwind willreturn memory(none)
87+
declare target("spirv.VulkanBuffer", [0 x i32], 12, 1) @llvm.spv.resource.handlefrombinding.tspirv.VulkanBuffer_a0i32_12_1t(i32, i32, i32, i32, ptr) #4
88+
89+
; Function Attrs: mustprogress nocallback nofree nosync nounwind willreturn memory(none)
90+
declare target("spirv.VulkanBuffer", i32, 12, 1) @llvm.spv.resource.counterhandlefromimplicitbinding.tspirv.VulkanBuffer_i32_12_1t.tspirv.VulkanBuffer_a0i32_12_1t(target("spirv.VulkanBuffer", [0 x i32], 12, 1), i32, i32) #4
91+
92+
; Function Attrs: mustprogress nocallback nofree nosync nounwind willreturn memory(none)
93+
declare ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.VulkanBuffer_a0i32_12_1t(target("spirv.VulkanBuffer", [0 x i32], 12, 1), i32) #4
94+
95+
attributes #0 = { convergent mustprogress nocallback nofree nosync nounwind willreturn memory(none) }
96+
attributes #1 = { convergent noinline norecurse "frame-pointer"="all" "hlsl.numthreads"="8,1,1" "hlsl.shader"="compute" "no-infs-fp-math"="true" "no-nans-fp-math"="true" "no-signed-zeros-fp-math"="true" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
97+
attributes #2 = { mustprogress nofree nosync nounwind willreturn memory(none) }
98+
attributes #4 = { mustprogress nocallback nofree nosync nounwind willreturn memory(none) }
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
2+
; RUN: opt < %s -passes=indvars -indvars-predicate-loops=1 -S | FileCheck %s
3+
4+
; Nested loops with body using loop convergence token should be skipped by IndVarSimplify.
5+
6+
%"class.hlsl::RWStructuredBuffer" = type { target("spirv.VulkanBuffer", [0 x i32], 12, 1), target("spirv.VulkanBuffer", i32, 12, 1) }
7+
8+
@_ZL3Out = internal global %"class.hlsl::RWStructuredBuffer" poison, align 8
9+
@.str = private unnamed_addr constant [4 x i8] c"Out\00", align 1
10+
11+
; Function Attrs: convergent mustprogress nocallback nofree nosync nounwind willreturn memory(none)
12+
declare token @llvm.experimental.convergence.entry() #0
13+
14+
define void @nested() local_unnamed_addr #1 {
15+
; CHECK-LABEL: @nested(
16+
; CHECK-NEXT: entry:
17+
; CHECK-NEXT: [[TMP0:%.*]] = tail call token @llvm.experimental.convergence.entry()
18+
; CHECK-NEXT: [[TMP1:%.*]] = tail call target("spirv.VulkanBuffer", [0 x i32], 12, 1) @llvm.spv.resource.handlefrombinding.tspirv.VulkanBuffer_a0i32_12_1t(i32 0, i32 0, i32 1, i32 0, ptr nonnull @.str)
19+
; CHECK-NEXT: [[TMP2:%.*]] = tail call target("spirv.VulkanBuffer", i32, 12, 1) @llvm.spv.resource.counterhandlefromimplicitbinding.tspirv.VulkanBuffer_i32_12_1t.tspirv.VulkanBuffer_a0i32_12_1t(target("spirv.VulkanBuffer", [0 x i32], 12, 1) [[TMP1]], i32 0, i32 0)
20+
; CHECK-NEXT: store target("spirv.VulkanBuffer", [0 x i32], 12, 1) [[TMP1]], ptr @_ZL3Out, align 8
21+
; CHECK-NEXT: store target("spirv.VulkanBuffer", i32, 12, 1) [[TMP2]], ptr getelementptr inbounds nuw (i8, ptr @_ZL3Out, i64 8), align 8
22+
; CHECK-NEXT: [[TMP3:%.*]] = tail call i32 @llvm.spv.thread.id.in.group.i32(i32 0)
23+
; CHECK-NEXT: [[TMP4:%.*]] = tail call i32 @llvm.spv.thread.id.in.group.i32(i32 1)
24+
; CHECK-NEXT: [[MUL_I:%.*]] = shl nuw nsw i32 [[TMP3]], 3
25+
; CHECK-NEXT: [[ADD_I:%.*]] = add nuw nsw i32 [[MUL_I]], [[TMP4]]
26+
; CHECK-NEXT: br label [[FOR_COND_I:%.*]]
27+
; CHECK: for.cond.i:
28+
; CHECK-NEXT: [[I_0_I:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC10_I:%.*]], [[CLEANUP_I:%.*]] ]
29+
; CHECK-NEXT: [[TMP5:%.*]] = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token [[TMP0]]) ]
30+
; CHECK-NEXT: [[CMP_I:%.*]] = icmp ult i32 [[I_0_I]], 8
31+
; CHECK-NEXT: br i1 [[CMP_I]], label [[FOR_COND1_I_PREHEADER:%.*]], label [[_Z4NESTEDDV3_J_EXIT:%.*]]
32+
; CHECK: for.cond1.i.preheader:
33+
; CHECK-NEXT: [[CMP5_I:%.*]] = icmp eq i32 [[I_0_I]], [[TMP3]]
34+
; CHECK-NEXT: br label [[FOR_COND1_I:%.*]]
35+
; CHECK: for.cond1.i:
36+
; CHECK-NEXT: [[J_0_I:%.*]] = phi i32 [ [[INC_I:%.*]], [[FOR_BODY4_I:%.*]] ], [ 0, [[FOR_COND1_I_PREHEADER]] ]
37+
; CHECK-NEXT: [[TMP6:%.*]] = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token [[TMP5]]) ]
38+
; CHECK-NEXT: [[CMP2_I:%.*]] = icmp ult i32 [[J_0_I]], 8
39+
; CHECK-NEXT: br i1 [[CMP2_I]], label [[FOR_BODY4_I]], label [[CLEANUP_I_LOOPEXIT:%.*]]
40+
; CHECK: for.body4.i:
41+
; CHECK-NEXT: [[CMP6_I:%.*]] = icmp eq i32 [[J_0_I]], [[TMP4]]
42+
; CHECK-NEXT: [[OR_COND:%.*]] = select i1 [[CMP5_I]], i1 [[CMP6_I]], i1 false
43+
; CHECK-NEXT: [[INC_I]] = add nuw nsw i32 [[J_0_I]], 1
44+
; CHECK-NEXT: br i1 [[OR_COND]], label [[IF_THEN_I:%.*]], label [[FOR_COND1_I]]
45+
; CHECK: cleanup.i.loopexit:
46+
; CHECK-NEXT: br label [[CLEANUP_I]]
47+
; CHECK: if.then.i:
48+
; CHECK-NEXT: [[HLSL_WAVE_ACTIVE_MAX7_I:%.*]] = call spir_func i32 @llvm.spv.wave.reduce.umax.i32(i32 [[ADD_I]]) [ "convergencectrl"(token [[TMP6]]) ]
49+
; CHECK-NEXT: [[TMP7:%.*]] = tail call noundef align 4 dereferenceable(4) ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.VulkanBuffer_a0i32_12_1t(target("spirv.VulkanBuffer", [0 x i32], 12, 1) [[TMP1]], i32 [[ADD_I]])
50+
; CHECK-NEXT: store i32 [[HLSL_WAVE_ACTIVE_MAX7_I]], ptr addrspace(11) [[TMP7]], align 4
51+
; CHECK-NEXT: br label [[CLEANUP_I]]
52+
; CHECK: cleanup.i:
53+
; CHECK-NEXT: [[INC10_I]] = add nuw nsw i32 [[I_0_I]], 1
54+
; CHECK-NEXT: br label [[FOR_COND_I]]
55+
; CHECK: _Z4nestedDv3_j.exit:
56+
; CHECK-NEXT: ret void
57+
;
58+
entry:
59+
%0 = tail call token @llvm.experimental.convergence.entry()
60+
%1 = tail call target("spirv.VulkanBuffer", [0 x i32], 12, 1) @llvm.spv.resource.handlefrombinding.tspirv.VulkanBuffer_a0i32_12_1t(i32 0, i32 0, i32 1, i32 0, ptr nonnull @.str)
61+
%2 = tail call target("spirv.VulkanBuffer", i32, 12, 1) @llvm.spv.resource.counterhandlefromimplicitbinding.tspirv.VulkanBuffer_i32_12_1t.tspirv.VulkanBuffer_a0i32_12_1t(target("spirv.VulkanBuffer", [0 x i32], 12, 1) %1, i32 0, i32 0)
62+
store target("spirv.VulkanBuffer", [0 x i32], 12, 1) %1, ptr @_ZL3Out, align 8
63+
store target("spirv.VulkanBuffer", i32, 12, 1) %2, ptr getelementptr inbounds nuw (i8, ptr @_ZL3Out, i64 8), align 8
64+
%3 = tail call i32 @llvm.spv.thread.id.in.group.i32(i32 0)
65+
%4 = tail call i32 @llvm.spv.thread.id.in.group.i32(i32 1)
66+
%mul.i = shl nuw nsw i32 %3, 3
67+
%add.i = add nuw nsw i32 %mul.i, %4
68+
br label %for.cond.i
69+
70+
for.cond.i: ; preds = %cleanup.i, %entry
71+
%i.0.i = phi i32 [ 0, %entry ], [ %inc10.i, %cleanup.i ]
72+
%5 = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token %0) ]
73+
%cmp.i = icmp ult i32 %i.0.i, 8
74+
br i1 %cmp.i, label %for.cond1.i.preheader, label %_Z4nestedDv3_j.exit
75+
76+
; Preheader:
77+
for.cond1.i.preheader: ; preds = %for.cond.i
78+
%cmp5.i = icmp eq i32 %i.0.i, %3
79+
br label %for.cond1.i
80+
81+
; Loop:
82+
for.cond1.i: ; preds = %for.body4.i, %for.cond1.i.preheader
83+
%j.0.i = phi i32 [ %inc.i, %for.body4.i ], [ 0, %for.cond1.i.preheader ]
84+
%6 = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token %5) ]
85+
%cmp2.i = icmp ult i32 %j.0.i, 8
86+
br i1 %cmp2.i, label %for.body4.i, label %cleanup.i.loopexit
87+
88+
for.body4.i: ; preds = %for.cond1.i
89+
%cmp6.i = icmp eq i32 %j.0.i, %4
90+
%or.cond = select i1 %cmp5.i, i1 %cmp6.i, i1 false
91+
%inc.i = add nuw nsw i32 %j.0.i, 1
92+
br i1 %or.cond, label %if.then.i, label %for.cond1.i
93+
94+
; Exit blocks
95+
cleanup.i.loopexit: ; preds = %for.cond1.i
96+
br label %cleanup.i
97+
98+
if.then.i: ; preds = %for.body4.i
99+
%hlsl.wave.active.max7.i = call spir_func i32 @llvm.spv.wave.reduce.umax.i32(i32 %add.i) [ "convergencectrl"(token %6) ]
100+
%7 = tail call noundef align 4 dereferenceable(4) ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.VulkanBuffer_a0i32_12_1t(target("spirv.VulkanBuffer", [0 x i32], 12, 1) %1, i32 %add.i)
101+
store i32 %hlsl.wave.active.max7.i, ptr addrspace(11) %7, align 4
102+
br label %cleanup.i
103+
104+
cleanup.i: ; preds = %cleanup.i.loopexit, %if.then.i
105+
%inc10.i = add nuw nsw i32 %i.0.i, 1
106+
br label %for.cond.i
107+
108+
_Z4nestedDv3_j.exit: ; preds = %for.cond.i
109+
ret void
110+
}
111+
112+
; Function Attrs: mustprogress nofree nosync nounwind willreturn memory(none)
113+
declare i32 @llvm.spv.thread.id.in.group.i32(i32) #2
114+
115+
; Function Attrs: convergent mustprogress nocallback nofree nosync nounwind willreturn memory(none)
116+
declare token @llvm.experimental.convergence.loop() #0
117+
118+
; Function Attrs: convergent mustprogress nocallback nofree nosync nounwind willreturn memory(none)
119+
declare i32 @llvm.spv.wave.reduce.umax.i32(i32) #0
120+
121+
; Function Attrs: mustprogress nocallback nofree nosync nounwind willreturn memory(none)
122+
declare target("spirv.VulkanBuffer", [0 x i32], 12, 1) @llvm.spv.resource.handlefrombinding.tspirv.VulkanBuffer_a0i32_12_1t(i32, i32, i32, i32, ptr) #4
123+
124+
; Function Attrs: mustprogress nocallback nofree nosync nounwind willreturn memory(none)
125+
declare target("spirv.VulkanBuffer", i32, 12, 1) @llvm.spv.resource.counterhandlefromimplicitbinding.tspirv.VulkanBuffer_i32_12_1t.tspirv.VulkanBuffer_a0i32_12_1t(target("spirv.VulkanBuffer", [0 x i32], 12, 1), i32, i32) #4
126+
127+
; Function Attrs: mustprogress nocallback nofree nosync nounwind willreturn memory(none)
128+
declare ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.VulkanBuffer_a0i32_12_1t(target("spirv.VulkanBuffer", [0 x i32], 12, 1), i32) #4
129+
130+
; Function Attrs: nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: readwrite)
131+
declare void @llvm.experimental.noalias.scope.decl(metadata) #5
132+
133+
attributes #0 = { convergent mustprogress nocallback nofree nosync nounwind willreturn memory(none) }
134+
attributes #1 = { convergent noinline norecurse "frame-pointer"="all" "hlsl.numthreads"="8,8,1" "hlsl.shader"="compute" "no-infs-fp-math"="true" "no-nans-fp-math"="true" "no-signed-zeros-fp-math"="true" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
135+
attributes #2 = { mustprogress nofree nosync nounwind willreturn memory(none) }
136+
attributes #3 = { mustprogress nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
137+
attributes #4 = { mustprogress nocallback nofree nosync nounwind willreturn memory(none) }
138+
attributes #5 = { nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: readwrite) }
139+
attributes #6 = { nounwind }

0 commit comments

Comments
 (0)