292 changes: 292 additions & 0 deletions llvm/test/CodeGen/SPIRV/structurizer/cf.if.for.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,292 @@
; RUN: llc -mtriple=spirv-unknown-vulkan-compute -O0 %s -o - | FileCheck %s
; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-unknown-vulkan-compute %s -o - -filetype=obj | spirv-val %}
; RUN: llc -mtriple=spirv-unknown-vulkan-compute -O0 %s -o - | spirv-sim --function=_Z7processv --wave=1 --expects=6

;
; int process() {
; int color = 0;
;
; int val = 0;
;
; if (color < 0) {
; val = 1;
; }
;
; // for-stmt following if-stmt
; for (int i = 0; i < 10; ++i) {
; if (color < 0) { // if-stmt nested in for-stmt
; val = val + 1;
; for (int j = 0; j < 15; ++j) { // for-stmt deeply nested in if-then
; val = val * 2;
; } // end for (int j
; val = val + 3;
; }
;
; if (color < 1) { // if-stmt following if-stmt
; val = val * 4;
; } else {
; for (int k = 0; k < 20; ++k) { // for-stmt deeply nested in if-else
; val = val - 5;
; if (val < 0) { // deeply nested if-stmt
; val = val + 100;
; }
; } // end for (int k
; } // end elsek
; } // end for (int i
;
; // if-stmt following for-stmt
; if (color < 2) {
; val = val + 6;
; }
;
; return val;
; }
;
; [numthreads(1, 1, 1)]
; void main() {
; process();
; }

; CHECK: %[[#func_18:]] = OpFunction %[[#uint:]] DontInline %[[#]]
; CHECK: %[[#bb65:]] = OpLabel
; CHECK: OpSelectionMerge %[[#bb66:]] None
; CHECK: OpBranchConditional %[[#]] %[[#bb67:]] %[[#bb66:]]
; CHECK: %[[#bb67:]] = OpLabel
; CHECK: OpBranch %[[#bb66:]]
; CHECK: %[[#bb66:]] = OpLabel
; CHECK: OpBranch %[[#bb68:]]
; CHECK: %[[#bb68:]] = OpLabel
; CHECK: OpLoopMerge %[[#bb69:]] %[[#bb70:]] None
; CHECK: OpBranchConditional %[[#]] %[[#bb71:]] %[[#bb69:]]
; CHECK: %[[#bb71:]] = OpLabel
; CHECK: OpSelectionMerge %[[#bb72:]] None
; CHECK: OpBranchConditional %[[#]] %[[#bb73:]] %[[#bb72:]]
; CHECK: %[[#bb73:]] = OpLabel
; CHECK: OpBranch %[[#bb74:]]
; CHECK: %[[#bb74:]] = OpLabel
; CHECK: OpLoopMerge %[[#bb75:]] %[[#bb76:]] None
; CHECK: OpBranchConditional %[[#]] %[[#bb77:]] %[[#bb75:]]
; CHECK: %[[#bb77:]] = OpLabel
; CHECK: OpBranch %[[#bb76:]]
; CHECK: %[[#bb76:]] = OpLabel
; CHECK: OpBranch %[[#bb74:]]
; CHECK: %[[#bb75:]] = OpLabel
; CHECK: OpBranch %[[#bb72:]]
; CHECK: %[[#bb72:]] = OpLabel
; CHECK: OpSelectionMerge %[[#bb78:]] None
; CHECK: OpBranchConditional %[[#]] %[[#bb79:]] %[[#bb80:]]
; CHECK: %[[#bb79:]] = OpLabel
; CHECK: OpBranch %[[#bb78:]]
; CHECK: %[[#bb80:]] = OpLabel
; CHECK: OpBranch %[[#bb81:]]
; CHECK: %[[#bb81:]] = OpLabel
; CHECK: OpLoopMerge %[[#bb82:]] %[[#bb83:]] None
; CHECK: OpBranchConditional %[[#]] %[[#bb84:]] %[[#bb82:]]
; CHECK: %[[#bb84:]] = OpLabel
; CHECK: OpSelectionMerge %[[#bb85:]] None
; CHECK: OpBranchConditional %[[#]] %[[#bb86:]] %[[#bb85:]]
; CHECK: %[[#bb86:]] = OpLabel
; CHECK: OpBranch %[[#bb85:]]
; CHECK: %[[#bb85:]] = OpLabel
; CHECK: OpBranch %[[#bb83:]]
; CHECK: %[[#bb83:]] = OpLabel
; CHECK: OpBranch %[[#bb81:]]
; CHECK: %[[#bb82:]] = OpLabel
; CHECK: OpBranch %[[#bb78:]]
; CHECK: %[[#bb78:]] = OpLabel
; CHECK: OpBranch %[[#bb70:]]
; CHECK: %[[#bb70:]] = OpLabel
; CHECK: OpBranch %[[#bb68:]]
; CHECK: %[[#bb69:]] = OpLabel
; CHECK: OpSelectionMerge %[[#bb87:]] None
; CHECK: OpBranchConditional %[[#]] %[[#bb88:]] %[[#bb87:]]
; CHECK: %[[#bb88:]] = OpLabel
; CHECK: OpBranch %[[#bb87:]]
; CHECK: %[[#bb87:]] = OpLabel
; CHECK: OpReturnValue %[[#]]
; CHECK: OpFunctionEnd
; CHECK: %[[#func_61:]] = OpFunction %[[#void:]] DontInline %[[#]]
; CHECK: %[[#bb89:]] = OpLabel
; CHECK: OpReturn
; CHECK: OpFunctionEnd
; CHECK: %[[#func_63:]] = OpFunction %[[#void:]] None %[[#]]
; CHECK: %[[#bb90:]] = OpLabel
; CHECK: OpReturn
; CHECK: OpFunctionEnd



target datalayout = "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-G1"
target triple = "spirv-unknown-vulkan1.3-compute"

; Function Attrs: convergent noinline norecurse nounwind optnone
define spir_func noundef i32 @_Z7processv() #0 {
entry:
%0 = call token @llvm.experimental.convergence.entry()
%color = alloca i32, align 4
%val = alloca i32, align 4
%i = alloca i32, align 4
%j = alloca i32, align 4
%k = alloca i32, align 4
store i32 0, ptr %color, align 4
store i32 0, ptr %val, align 4
%1 = load i32, ptr %color, align 4
%cmp = icmp slt i32 %1, 0
br i1 %cmp, label %if.then, label %if.end

if.then: ; preds = %entry
store i32 1, ptr %val, align 4
br label %if.end

if.end: ; preds = %if.then, %entry
store i32 0, ptr %i, align 4
br label %for.cond

for.cond: ; preds = %for.inc23, %if.end
%2 = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token %0) ]
%3 = load i32, ptr %i, align 4
%cmp1 = icmp slt i32 %3, 10
br i1 %cmp1, label %for.body, label %for.end25

for.body: ; preds = %for.cond
%4 = load i32, ptr %color, align 4
%cmp2 = icmp slt i32 %4, 0
br i1 %cmp2, label %if.then3, label %if.end8

if.then3: ; preds = %for.body
%5 = load i32, ptr %val, align 4
%add = add nsw i32 %5, 1
store i32 %add, ptr %val, align 4
store i32 0, ptr %j, align 4
br label %for.cond4

for.cond4: ; preds = %for.inc, %if.then3
%6 = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token %2) ]
%7 = load i32, ptr %j, align 4
%cmp5 = icmp slt i32 %7, 15
br i1 %cmp5, label %for.body6, label %for.end

for.body6: ; preds = %for.cond4
%8 = load i32, ptr %val, align 4
%mul = mul nsw i32 %8, 2
store i32 %mul, ptr %val, align 4
br label %for.inc

for.inc: ; preds = %for.body6
%9 = load i32, ptr %j, align 4
%inc = add nsw i32 %9, 1
store i32 %inc, ptr %j, align 4
br label %for.cond4

for.end: ; preds = %for.cond4
%10 = load i32, ptr %val, align 4
%add7 = add nsw i32 %10, 3
store i32 %add7, ptr %val, align 4
br label %if.end8

if.end8: ; preds = %for.end, %for.body
%11 = load i32, ptr %color, align 4
%cmp9 = icmp slt i32 %11, 1
br i1 %cmp9, label %if.then10, label %if.else

if.then10: ; preds = %if.end8
%12 = load i32, ptr %val, align 4
%mul11 = mul nsw i32 %12, 4
store i32 %mul11, ptr %val, align 4
br label %if.end22

if.else: ; preds = %if.end8
store i32 0, ptr %k, align 4
br label %for.cond12

for.cond12: ; preds = %for.inc19, %if.else
%13 = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token %2) ]
%14 = load i32, ptr %k, align 4
%cmp13 = icmp slt i32 %14, 20
br i1 %cmp13, label %for.body14, label %for.end21

for.body14: ; preds = %for.cond12
%15 = load i32, ptr %val, align 4
%sub = sub nsw i32 %15, 5
store i32 %sub, ptr %val, align 4
%16 = load i32, ptr %val, align 4
%cmp15 = icmp slt i32 %16, 0
br i1 %cmp15, label %if.then16, label %if.end18

if.then16: ; preds = %for.body14
%17 = load i32, ptr %val, align 4
%add17 = add nsw i32 %17, 100
store i32 %add17, ptr %val, align 4
br label %if.end18

if.end18: ; preds = %if.then16, %for.body14
br label %for.inc19

for.inc19: ; preds = %if.end18
%18 = load i32, ptr %k, align 4
%inc20 = add nsw i32 %18, 1
store i32 %inc20, ptr %k, align 4
br label %for.cond12

for.end21: ; preds = %for.cond12
br label %if.end22

if.end22: ; preds = %for.end21, %if.then10
br label %for.inc23

for.inc23: ; preds = %if.end22
%19 = load i32, ptr %i, align 4
%inc24 = add nsw i32 %19, 1
store i32 %inc24, ptr %i, align 4
br label %for.cond

for.end25: ; preds = %for.cond
%20 = load i32, ptr %color, align 4
%cmp26 = icmp slt i32 %20, 2
br i1 %cmp26, label %if.then27, label %if.end29

if.then27: ; preds = %for.end25
%21 = load i32, ptr %val, align 4
%add28 = add nsw i32 %21, 6
store i32 %add28, ptr %val, align 4
br label %if.end29

if.end29: ; preds = %if.then27, %for.end25
%22 = load i32, ptr %val, align 4
ret i32 %22
}

; Function Attrs: convergent nocallback nofree nosync nounwind willreturn memory(none)
declare token @llvm.experimental.convergence.entry() #1

; Function Attrs: convergent nocallback nofree nosync nounwind willreturn memory(none)
declare token @llvm.experimental.convergence.loop() #1

; Function Attrs: convergent noinline norecurse nounwind optnone
define internal spir_func void @main() #0 {
entry:
%0 = call token @llvm.experimental.convergence.entry()
%call1 = call spir_func noundef i32 @_Z7processv() #3 [ "convergencectrl"(token %0) ]
ret void
}

; Function Attrs: convergent norecurse
define void @main.1() #2 {
entry:
call void @main()
ret void
}

attributes #0 = { convergent noinline norecurse nounwind optnone "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
attributes #1 = { convergent nocallback nofree nosync nounwind willreturn memory(none) }
attributes #2 = { convergent norecurse "frame-pointer"="all" "hlsl.numthreads"="1,1,1" "hlsl.shader"="compute" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
attributes #3 = { convergent }

!llvm.module.flags = !{!0, !1, !2}


!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{i32 4, !"dx.disable_optimizations", i32 1}
!2 = !{i32 7, !"frame-pointer", i32 2}


163 changes: 163 additions & 0 deletions llvm/test/CodeGen/SPIRV/structurizer/cf.if.nested.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
; RUN: llc -mtriple=spirv-unknown-vulkan-compute -O0 %s -o - | FileCheck %s
; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-unknown-vulkan-compute %s -o - -filetype=obj | spirv-val %}
; RUN: llc -mtriple=spirv-unknown-vulkan-compute -O0 %s -o - | spirv-sim --function=_Z7processv --wave=1 --expects=3


;
; int process() {
; int c1 = 0;
; int c2 = 1;
; int c3 = 0;
; int c4 = 1;
; int val = 0;
;
; if (c1) {
; if (c2)
; val = 1;
; } else {
; if (c3) {
; val = 2;
; } else {
; if (c4) {
; val = 3;
; }
; }
; }
; return val;
; }
;
; [numthreads(1, 1, 1)]
; void main() {
; process();
; }

; CHECK: %[[#func_11:]] = OpFunction %[[#uint:]] DontInline %[[#]]
; CHECK: %[[#bb30:]] = OpLabel
; CHECK: OpSelectionMerge %[[#bb31:]] None
; CHECK: OpBranchConditional %[[#]] %[[#bb32:]] %[[#bb33:]]
; CHECK: %[[#bb32:]] = OpLabel
; CHECK: OpSelectionMerge %[[#bb34:]] None
; CHECK: OpBranchConditional %[[#]] %[[#bb35:]] %[[#bb34:]]
; CHECK: %[[#bb33:]] = OpLabel
; CHECK: OpSelectionMerge %[[#bb36:]] None
; CHECK: OpBranchConditional %[[#]] %[[#bb37:]] %[[#bb38:]]
; CHECK: %[[#bb35:]] = OpLabel
; CHECK: OpBranch %[[#bb34:]]
; CHECK: %[[#bb37:]] = OpLabel
; CHECK: OpBranch %[[#bb36:]]
; CHECK: %[[#bb38:]] = OpLabel
; CHECK: OpSelectionMerge %[[#bb39:]] None
; CHECK: OpBranchConditional %[[#]] %[[#bb40:]] %[[#bb39:]]
; CHECK: %[[#bb34:]] = OpLabel
; CHECK: OpBranch %[[#bb31:]]
; CHECK: %[[#bb40:]] = OpLabel
; CHECK: OpBranch %[[#bb39:]]
; CHECK: %[[#bb39:]] = OpLabel
; CHECK: OpBranch %[[#bb36:]]
; CHECK: %[[#bb36:]] = OpLabel
; CHECK: OpBranch %[[#bb31:]]
; CHECK: %[[#bb31:]] = OpLabel
; CHECK: OpReturnValue %[[#]]
; CHECK: OpFunctionEnd
; CHECK: %[[#func_26:]] = OpFunction %[[#void:]] DontInline %[[#]]
; CHECK: %[[#bb41:]] = OpLabel
; CHECK: OpReturn
; CHECK: OpFunctionEnd
; CHECK: %[[#func_28:]] = OpFunction %[[#void:]] None %[[#]]
; CHECK: %[[#bb42:]] = OpLabel
; CHECK: OpReturn
; CHECK: OpFunctionEnd

target datalayout = "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-G1"
target triple = "spirv-unknown-vulkan1.3-compute"

; Function Attrs: convergent noinline norecurse nounwind optnone
define spir_func noundef i32 @_Z7processv() #0 {
entry:
%0 = call token @llvm.experimental.convergence.entry()
%c1 = alloca i32, align 4
%c2 = alloca i32, align 4
%c3 = alloca i32, align 4
%c4 = alloca i32, align 4
%val = alloca i32, align 4
store i32 0, ptr %c1, align 4
store i32 1, ptr %c2, align 4
store i32 0, ptr %c3, align 4
store i32 1, ptr %c4, align 4
store i32 0, ptr %val, align 4
%1 = load i32, ptr %c1, align 4
%tobool = icmp ne i32 %1, 0
br i1 %tobool, label %if.then, label %if.else

if.then: ; preds = %entry
%2 = load i32, ptr %c2, align 4
%tobool1 = icmp ne i32 %2, 0
br i1 %tobool1, label %if.then2, label %if.end

if.then2: ; preds = %if.then
store i32 1, ptr %val, align 4
br label %if.end

if.end: ; preds = %if.then2, %if.then
br label %if.end10

if.else: ; preds = %entry
%3 = load i32, ptr %c3, align 4
%tobool3 = icmp ne i32 %3, 0
br i1 %tobool3, label %if.then4, label %if.else5

if.then4: ; preds = %if.else
store i32 2, ptr %val, align 4
br label %if.end9

if.else5: ; preds = %if.else
%4 = load i32, ptr %c4, align 4
%tobool6 = icmp ne i32 %4, 0
br i1 %tobool6, label %if.then7, label %if.end8

if.then7: ; preds = %if.else5
store i32 3, ptr %val, align 4
br label %if.end8

if.end8: ; preds = %if.then7, %if.else5
br label %if.end9

if.end9: ; preds = %if.end8, %if.then4
br label %if.end10

if.end10: ; preds = %if.end9, %if.end
%5 = load i32, ptr %val, align 4
ret i32 %5
}

; Function Attrs: convergent nocallback nofree nosync nounwind willreturn memory(none)
declare token @llvm.experimental.convergence.entry() #1

; Function Attrs: convergent noinline norecurse nounwind optnone
define internal spir_func void @main() #0 {
entry:
%0 = call token @llvm.experimental.convergence.entry()
%call1 = call spir_func noundef i32 @_Z7processv() #3 [ "convergencectrl"(token %0) ]
ret void
}

; Function Attrs: convergent norecurse
define void @main.1() #2 {
entry:
call void @main()
ret void
}

attributes #0 = { convergent noinline norecurse nounwind optnone "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
attributes #1 = { convergent nocallback nofree nosync nounwind willreturn memory(none) }
attributes #2 = { convergent norecurse "frame-pointer"="all" "hlsl.numthreads"="1,1,1" "hlsl.shader"="compute" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
attributes #3 = { convergent }

!llvm.module.flags = !{!0, !1, !2}


!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{i32 4, !"dx.disable_optimizations", i32 1}
!2 = !{i32 7, !"frame-pointer", i32 2}


183 changes: 183 additions & 0 deletions llvm/test/CodeGen/SPIRV/structurizer/cf.if.plain.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
; RUN: llc -mtriple=spirv-unknown-vulkan-compute -O0 %s -o - | FileCheck %s
; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-unknown-vulkan-compute %s -o - -filetype=obj | spirv-val %}
; RUN: llc -mtriple=spirv-unknown-vulkan-compute -O0 %s -o - | spirv-sim --function=_Z7processv --wave=1 --expects=2

;
; int process() {
; int c = 0;
; int val = 0;
;
; // Both then and else
; if (c) {
; val = val + 1;
; } else {
; val = val + 2;
; }
;
; // No else
; if (c)
; val = 1;
;
; // Empty then
; if (c) {
; } else {
; val = 2;
; }
;
; // Null body
; if (c)
; ;
;
; if (int d = val) {
; c = true;
; }
;
; return val;
; }
;
; [numthreads(1, 1, 1)]
; void main() {
; process();
; }

; CHECK: %[[#func_10:]] = OpFunction %[[#uint:]] DontInline %[[#]]
; CHECK: %[[#bb34:]] = OpLabel
; CHECK: OpSelectionMerge %[[#bb35:]] None
; CHECK: OpBranchConditional %[[#]] %[[#bb36:]] %[[#bb37:]]
; CHECK: %[[#bb36:]] = OpLabel
; CHECK: OpBranch %[[#bb35:]]
; CHECK: %[[#bb37:]] = OpLabel
; CHECK: OpBranch %[[#bb35:]]
; CHECK: %[[#bb35:]] = OpLabel
; CHECK: OpSelectionMerge %[[#bb38:]] None
; CHECK: OpBranchConditional %[[#]] %[[#bb39:]] %[[#bb38:]]
; CHECK: %[[#bb39:]] = OpLabel
; CHECK: OpBranch %[[#bb38:]]
; CHECK: %[[#bb38:]] = OpLabel
; CHECK: OpSelectionMerge %[[#bb40:]] None
; CHECK: OpBranchConditional %[[#]] %[[#bb40:]] %[[#bb41:]]
; CHECK: %[[#bb41:]] = OpLabel
; CHECK: OpBranch %[[#bb40:]]
; CHECK: %[[#bb40:]] = OpLabel
; CHECK: OpBranch %[[#bb42:]]
; CHECK: %[[#bb42:]] = OpLabel
; CHECK: OpSelectionMerge %[[#bb43:]] None
; CHECK: OpBranchConditional %[[#]] %[[#bb44:]] %[[#bb43:]]
; CHECK: %[[#bb44:]] = OpLabel
; CHECK: OpBranch %[[#bb43:]]
; CHECK: %[[#bb43:]] = OpLabel
; CHECK: OpReturnValue %[[#]]
; CHECK: OpFunctionEnd
; CHECK: %[[#func_30:]] = OpFunction %[[#void:]] DontInline %[[#]]
; CHECK: %[[#bb45:]] = OpLabel
; CHECK: OpReturn
; CHECK: OpFunctionEnd
; CHECK: %[[#func_32:]] = OpFunction %[[#void:]] None %[[#]]
; CHECK: %[[#bb46:]] = OpLabel
; CHECK: OpReturn
; CHECK: OpFunctionEnd

target datalayout = "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-G1"
target triple = "spirv-unknown-vulkan1.3-compute"

; Function Attrs: convergent noinline norecurse nounwind optnone
define spir_func noundef i32 @_Z7processv() #0 {
entry:
%0 = call token @llvm.experimental.convergence.entry()
%c = alloca i32, align 4
%val = alloca i32, align 4
%d = alloca i32, align 4
store i32 0, ptr %c, align 4
store i32 0, ptr %val, align 4
%1 = load i32, ptr %c, align 4
%tobool = icmp ne i32 %1, 0
br i1 %tobool, label %if.then, label %if.else

if.then: ; preds = %entry
%2 = load i32, ptr %val, align 4
%add = add nsw i32 %2, 1
store i32 %add, ptr %val, align 4
br label %if.end

if.else: ; preds = %entry
%3 = load i32, ptr %val, align 4
%add1 = add nsw i32 %3, 2
store i32 %add1, ptr %val, align 4
br label %if.end

if.end: ; preds = %if.else, %if.then
%4 = load i32, ptr %c, align 4
%tobool2 = icmp ne i32 %4, 0
br i1 %tobool2, label %if.then3, label %if.end4

if.then3: ; preds = %if.end
store i32 1, ptr %val, align 4
br label %if.end4

if.end4: ; preds = %if.then3, %if.end
%5 = load i32, ptr %c, align 4
%tobool5 = icmp ne i32 %5, 0
br i1 %tobool5, label %if.then6, label %if.else7

if.then6: ; preds = %if.end4
br label %if.end8

if.else7: ; preds = %if.end4
store i32 2, ptr %val, align 4
br label %if.end8

if.end8: ; preds = %if.else7, %if.then6
%6 = load i32, ptr %c, align 4
%tobool9 = icmp ne i32 %6, 0
br i1 %tobool9, label %if.then10, label %if.end11

if.then10: ; preds = %if.end8
br label %if.end11

if.end11: ; preds = %if.then10, %if.end8
%7 = load i32, ptr %val, align 4
store i32 %7, ptr %d, align 4
%8 = load i32, ptr %d, align 4
%tobool12 = icmp ne i32 %8, 0
br i1 %tobool12, label %if.then13, label %if.end14

if.then13: ; preds = %if.end11
store i32 1, ptr %c, align 4
br label %if.end14

if.end14: ; preds = %if.then13, %if.end11
%9 = load i32, ptr %val, align 4
ret i32 %9
}

; Function Attrs: convergent nocallback nofree nosync nounwind willreturn memory(none)
declare token @llvm.experimental.convergence.entry() #1

; Function Attrs: convergent noinline norecurse nounwind optnone
define internal spir_func void @main() #0 {
entry:
%0 = call token @llvm.experimental.convergence.entry()
%call1 = call spir_func noundef i32 @_Z7processv() #3 [ "convergencectrl"(token %0) ]
ret void
}

; Function Attrs: convergent norecurse
define void @main.1() #2 {
entry:
call void @main()
ret void
}

attributes #0 = { convergent noinline norecurse nounwind optnone "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
attributes #1 = { convergent nocallback nofree nosync nounwind willreturn memory(none) }
attributes #2 = { convergent norecurse "frame-pointer"="all" "hlsl.numthreads"="1,1,1" "hlsl.shader"="compute" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
attributes #3 = { convergent }

!llvm.module.flags = !{!0, !1, !2}


!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{i32 4, !"dx.disable_optimizations", i32 1}
!2 = !{i32 7, !"frame-pointer", i32 2}


194 changes: 194 additions & 0 deletions llvm/test/CodeGen/SPIRV/structurizer/cf.logical-and.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
; RUN: llc -mtriple=spirv-unknown-vulkan-compute -O0 %s -o - | FileCheck %s
; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-unknown-vulkan-compute %s -o - -filetype=obj | spirv-val %}
; RUN: llc -mtriple=spirv-unknown-vulkan-compute -O0 %s -o - | spirv-sim --function=_Z7processv --wave=1 --expects=1

;
; int fn() { return true; }
;
; int process() {
; // Use in control flow
; int a = 0;
; int b = 0;
; int val = 0;
; if (a && b) val++;
;
; // Operand with side effects
; if (fn() && fn()) val++;
;
; if (a && fn())
; val++;
;
; if (fn() && b)
; val++;
; return val;
; }
;
; [numthreads(1, 1, 1)]
; void main() {
; process();
; }

; CHECK: %[[#func_9:]] = OpFunction %[[#uint:]] DontInline %[[#]]
; CHECK: %[[#bb43:]] = OpLabel
; CHECK: OpReturnValue %[[#]]
; CHECK: OpFunctionEnd
; CHECK: %[[#func_10:]] = OpFunction %[[#uint:]] DontInline %[[#]]
; CHECK: %[[#bb44:]] = OpLabel
; CHECK: OpSelectionMerge %[[#bb45:]] None
; CHECK: OpBranchConditional %[[#]] %[[#bb46:]] %[[#bb45:]]
; CHECK: %[[#bb46:]] = OpLabel
; CHECK: OpBranchConditional %[[#]] %[[#bb47:]] %[[#bb45:]]
; CHECK: %[[#bb47:]] = OpLabel
; CHECK: OpBranch %[[#bb45:]]
; CHECK: %[[#bb45:]] = OpLabel
; CHECK: OpSelectionMerge %[[#bb48:]] None
; CHECK: OpBranchConditional %[[#]] %[[#bb49:]] %[[#bb48:]]
; CHECK: %[[#bb49:]] = OpLabel
; CHECK: OpBranchConditional %[[#]] %[[#bb50:]] %[[#bb48:]]
; CHECK: %[[#bb50:]] = OpLabel
; CHECK: OpBranch %[[#bb48:]]
; CHECK: %[[#bb48:]] = OpLabel
; CHECK: OpSelectionMerge %[[#bb51:]] None
; CHECK: OpBranchConditional %[[#]] %[[#bb52:]] %[[#bb51:]]
; CHECK: %[[#bb52:]] = OpLabel
; CHECK: OpBranchConditional %[[#]] %[[#bb53:]] %[[#bb51:]]
; CHECK: %[[#bb53:]] = OpLabel
; CHECK: OpBranch %[[#bb51:]]
; CHECK: %[[#bb51:]] = OpLabel
; CHECK: OpSelectionMerge %[[#bb54:]] None
; CHECK: OpBranchConditional %[[#]] %[[#bb55:]] %[[#bb54:]]
; CHECK: %[[#bb55:]] = OpLabel
; CHECK: OpBranchConditional %[[#]] %[[#bb56:]] %[[#bb54:]]
; CHECK: %[[#bb56:]] = OpLabel
; CHECK: OpBranch %[[#bb54:]]
; CHECK: %[[#bb54:]] = OpLabel
; CHECK: OpReturnValue %[[#]]
; CHECK: OpFunctionEnd
; CHECK: %[[#func_39:]] = OpFunction %[[#void:]] DontInline %[[#]]
; CHECK: %[[#bb57:]] = OpLabel
; CHECK: OpReturn
; CHECK: OpFunctionEnd
; CHECK: %[[#func_41:]] = OpFunction %[[#void:]] None %[[#]]
; CHECK: %[[#bb58:]] = OpLabel
; CHECK: OpReturn
; CHECK: OpFunctionEnd

target datalayout = "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-G1"
target triple = "spirv-unknown-vulkan1.3-compute"

; Function Attrs: convergent noinline norecurse nounwind optnone
define spir_func noundef i32 @_Z2fnv() #0 {
entry:
%0 = call token @llvm.experimental.convergence.entry()
ret i32 1
}

; Function Attrs: convergent nocallback nofree nosync nounwind willreturn memory(none)
declare token @llvm.experimental.convergence.entry() #1

; Function Attrs: convergent noinline norecurse nounwind optnone
define spir_func noundef i32 @_Z7processv() #0 {
entry:
%0 = call token @llvm.experimental.convergence.entry()
%a = alloca i32, align 4
%b = alloca i32, align 4
%val = alloca i32, align 4
store i32 0, ptr %a, align 4
store i32 0, ptr %b, align 4
store i32 0, ptr %val, align 4
%1 = load i32, ptr %a, align 4
%tobool = icmp ne i32 %1, 0
br i1 %tobool, label %land.lhs.true, label %if.end

land.lhs.true: ; preds = %entry
%2 = load i32, ptr %b, align 4
%tobool1 = icmp ne i32 %2, 0
br i1 %tobool1, label %if.then, label %if.end

if.then: ; preds = %land.lhs.true
%3 = load i32, ptr %val, align 4
%inc = add nsw i32 %3, 1
store i32 %inc, ptr %val, align 4
br label %if.end

if.end: ; preds = %if.then, %land.lhs.true, %entry
%call2 = call spir_func noundef i32 @_Z2fnv() #3 [ "convergencectrl"(token %0) ]
%tobool3 = icmp ne i32 %call2, 0
br i1 %tobool3, label %land.lhs.true4, label %if.end9

land.lhs.true4: ; preds = %if.end
%call5 = call spir_func noundef i32 @_Z2fnv() #3 [ "convergencectrl"(token %0) ]
%tobool6 = icmp ne i32 %call5, 0
br i1 %tobool6, label %if.then7, label %if.end9

if.then7: ; preds = %land.lhs.true4
%4 = load i32, ptr %val, align 4
%inc8 = add nsw i32 %4, 1
store i32 %inc8, ptr %val, align 4
br label %if.end9

if.end9: ; preds = %if.then7, %land.lhs.true4, %if.end
%5 = load i32, ptr %a, align 4
%tobool10 = icmp ne i32 %5, 0
br i1 %tobool10, label %land.lhs.true11, label %if.end16

land.lhs.true11: ; preds = %if.end9
%call12 = call spir_func noundef i32 @_Z2fnv() #3 [ "convergencectrl"(token %0) ]
%tobool13 = icmp ne i32 %call12, 0
br i1 %tobool13, label %if.then14, label %if.end16

if.then14: ; preds = %land.lhs.true11
%6 = load i32, ptr %val, align 4
%inc15 = add nsw i32 %6, 1
store i32 %inc15, ptr %val, align 4
br label %if.end16

if.end16: ; preds = %if.then14, %land.lhs.true11, %if.end9
%call17 = call spir_func noundef i32 @_Z2fnv() #3 [ "convergencectrl"(token %0) ]
%tobool18 = icmp ne i32 %call17, 0
br i1 %tobool18, label %land.lhs.true19, label %if.end23

land.lhs.true19: ; preds = %if.end16
%7 = load i32, ptr %b, align 4
%tobool20 = icmp ne i32 %7, 0
br i1 %tobool20, label %if.then21, label %if.end23

if.then21: ; preds = %land.lhs.true19
%8 = load i32, ptr %val, align 4
%inc22 = add nsw i32 %8, 1
store i32 %inc22, ptr %val, align 4
br label %if.end23

if.end23: ; preds = %if.then21, %land.lhs.true19, %if.end16
%9 = load i32, ptr %val, align 4
ret i32 %9
}

; Function Attrs: convergent noinline norecurse nounwind optnone
define internal spir_func void @main() #0 {
entry:
%0 = call token @llvm.experimental.convergence.entry()
%call1 = call spir_func noundef i32 @_Z7processv() #3 [ "convergencectrl"(token %0) ]
ret void
}

; Function Attrs: convergent norecurse
define void @main.1() #2 {
entry:
call void @main()
ret void
}

attributes #0 = { convergent noinline norecurse nounwind optnone "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
attributes #1 = { convergent nocallback nofree nosync nounwind willreturn memory(none) }
attributes #2 = { convergent norecurse "frame-pointer"="all" "hlsl.numthreads"="1,1,1" "hlsl.shader"="compute" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
attributes #3 = { convergent }

!llvm.module.flags = !{!0, !1, !2}


!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{i32 4, !"dx.disable_optimizations", i32 1}
!2 = !{i32 7, !"frame-pointer", i32 2}


223 changes: 223 additions & 0 deletions llvm/test/CodeGen/SPIRV/structurizer/cf.logical-or.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
; RUN: llc -mtriple=spirv-unknown-vulkan-compute -O0 %s -o - | FileCheck %s
; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-unknown-vulkan-compute %s -o - -filetype=obj | spirv-val %}
; RUN: llc -mtriple=spirv-unknown-vulkan-compute -O0 %s -o - | spirv-sim --function=_Z7processv --wave=1 --expects=3

;
; int fn() { return true; }
;
; int process() {
; int a = 0;
; int b = 0;
; int val = 0;
;
; // Use in control flow
; if (a || b) val++;
;
; // Operand with side effects
; if (fn() || fn()) val++;
; if (a || fn()) val++;
; if (fn() || b) val++;
; return val;
; }
;
; [numthreads(1, 1, 1)]
; void main() {
; process();
; }

; CHECK: %[[#func_10:]] = OpFunction %[[#uint:]] DontInline %[[#]]
; CHECK: %[[#bb52:]] = OpLabel
; CHECK: OpReturnValue %[[#]]
; CHECK: OpFunctionEnd
; CHECK: %[[#func_11:]] = OpFunction %[[#uint:]] DontInline %[[#]]
; CHECK: %[[#bb53:]] = OpLabel
; CHECK: OpSelectionMerge %[[#bb54:]] None
; CHECK: OpBranchConditional %[[#]] %[[#bb55:]] %[[#bb56:]]
; CHECK: %[[#bb55:]] = OpLabel
; CHECK: OpSelectionMerge %[[#bb57:]] None
; CHECK: OpBranchConditional %[[#]] %[[#bb57:]] %[[#bb58:]]
; CHECK: %[[#bb56:]] = OpLabel
; CHECK: %[[#bb58:]] = OpLabel
; CHECK: OpBranchConditional %[[#]] %[[#bb57:]] %[[#bb59:]]
; CHECK: %[[#bb59:]] = OpLabel
; CHECK: OpBranch %[[#bb57:]]
; CHECK: %[[#bb57:]] = OpLabel
; CHECK: OpBranchConditional %[[#]] %[[#bb60:]] %[[#bb54:]]
; CHECK: %[[#bb60:]] = OpLabel
; CHECK: OpBranch %[[#bb54:]]
; CHECK: %[[#bb54:]] = OpLabel
; CHECK: OpSelectionMerge %[[#bb61:]] None
; CHECK: OpBranchConditional %[[#]] %[[#bb62:]] %[[#bb63:]]
; CHECK: %[[#bb62:]] = OpLabel
; CHECK: OpSelectionMerge %[[#bb64:]] None
; CHECK: OpBranchConditional %[[#]] %[[#bb64:]] %[[#bb65:]]
; CHECK: %[[#bb63:]] = OpLabel
; CHECK: %[[#bb65:]] = OpLabel
; CHECK: OpBranchConditional %[[#]] %[[#bb64:]] %[[#bb66:]]
; CHECK: %[[#bb66:]] = OpLabel
; CHECK: OpBranch %[[#bb64:]]
; CHECK: %[[#bb64:]] = OpLabel
; CHECK: OpBranchConditional %[[#]] %[[#bb67:]] %[[#bb61:]]
; CHECK: %[[#bb67:]] = OpLabel
; CHECK: OpBranch %[[#bb61:]]
; CHECK: %[[#bb61:]] = OpLabel
; CHECK: OpSelectionMerge %[[#bb68:]] None
; CHECK: OpBranchConditional %[[#]] %[[#bb69:]] %[[#bb70:]]
; CHECK: %[[#bb69:]] = OpLabel
; CHECK: OpSelectionMerge %[[#bb71:]] None
; CHECK: OpBranchConditional %[[#]] %[[#bb71:]] %[[#bb72:]]
; CHECK: %[[#bb70:]] = OpLabel
; CHECK: %[[#bb72:]] = OpLabel
; CHECK: OpBranchConditional %[[#]] %[[#bb71:]] %[[#bb73:]]
; CHECK: %[[#bb73:]] = OpLabel
; CHECK: OpBranch %[[#bb71:]]
; CHECK: %[[#bb71:]] = OpLabel
; CHECK: OpBranchConditional %[[#]] %[[#bb74:]] %[[#bb68:]]
; CHECK: %[[#bb74:]] = OpLabel
; CHECK: OpBranch %[[#bb68:]]
; CHECK: %[[#bb68:]] = OpLabel
; CHECK: OpSelectionMerge %[[#bb75:]] None
; CHECK: OpBranchConditional %[[#]] %[[#bb76:]] %[[#bb77:]]
; CHECK: %[[#bb76:]] = OpLabel
; CHECK: OpSelectionMerge %[[#bb78:]] None
; CHECK: OpBranchConditional %[[#]] %[[#bb78:]] %[[#bb79:]]
; CHECK: %[[#bb77:]] = OpLabel
; CHECK: %[[#bb79:]] = OpLabel
; CHECK: OpBranchConditional %[[#]] %[[#bb78:]] %[[#bb80:]]
; CHECK: %[[#bb80:]] = OpLabel
; CHECK: OpBranch %[[#bb78:]]
; CHECK: %[[#bb78:]] = OpLabel
; CHECK: OpBranchConditional %[[#]] %[[#bb81:]] %[[#bb75:]]
; CHECK: %[[#bb81:]] = OpLabel
; CHECK: OpBranch %[[#bb75:]]
; CHECK: %[[#bb75:]] = OpLabel
; CHECK: OpReturnValue %[[#]]
; CHECK: OpFunctionEnd
; CHECK: %[[#func_48:]] = OpFunction %[[#void:]] DontInline %[[#]]
; CHECK: %[[#bb82:]] = OpLabel
; CHECK: OpReturn
; CHECK: OpFunctionEnd
; CHECK: %[[#func_50:]] = OpFunction %[[#void:]] None %[[#]]
; CHECK: %[[#bb83:]] = OpLabel
; CHECK: OpReturn
; CHECK: OpFunctionEnd

target datalayout = "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-G1"
target triple = "spirv-unknown-vulkan1.3-compute"

; Function Attrs: convergent noinline norecurse nounwind optnone
define spir_func noundef i32 @_Z2fnv() #0 {
entry:
%0 = call token @llvm.experimental.convergence.entry()
ret i32 1
}

; Function Attrs: convergent nocallback nofree nosync nounwind willreturn memory(none)
declare token @llvm.experimental.convergence.entry() #1

; Function Attrs: convergent noinline norecurse nounwind optnone
define spir_func noundef i32 @_Z7processv() #0 {
entry:
%0 = call token @llvm.experimental.convergence.entry()
%a = alloca i32, align 4
%b = alloca i32, align 4
%val = alloca i32, align 4
store i32 0, ptr %a, align 4
store i32 0, ptr %b, align 4
store i32 0, ptr %val, align 4
%1 = load i32, ptr %a, align 4
%tobool = icmp ne i32 %1, 0
br i1 %tobool, label %if.then, label %lor.lhs.false

lor.lhs.false: ; preds = %entry
%2 = load i32, ptr %b, align 4
%tobool1 = icmp ne i32 %2, 0
br i1 %tobool1, label %if.then, label %if.end

if.then: ; preds = %lor.lhs.false, %entry
%3 = load i32, ptr %val, align 4
%inc = add nsw i32 %3, 1
store i32 %inc, ptr %val, align 4
br label %if.end

if.end: ; preds = %if.then, %lor.lhs.false
%call2 = call spir_func noundef i32 @_Z2fnv() #3 [ "convergencectrl"(token %0) ]
%tobool3 = icmp ne i32 %call2, 0
br i1 %tobool3, label %if.then7, label %lor.lhs.false4

lor.lhs.false4: ; preds = %if.end
%call5 = call spir_func noundef i32 @_Z2fnv() #3 [ "convergencectrl"(token %0) ]
%tobool6 = icmp ne i32 %call5, 0
br i1 %tobool6, label %if.then7, label %if.end9

if.then7: ; preds = %lor.lhs.false4, %if.end
%4 = load i32, ptr %val, align 4
%inc8 = add nsw i32 %4, 1
store i32 %inc8, ptr %val, align 4
br label %if.end9

if.end9: ; preds = %if.then7, %lor.lhs.false4
%5 = load i32, ptr %a, align 4
%tobool10 = icmp ne i32 %5, 0
br i1 %tobool10, label %if.then14, label %lor.lhs.false11

lor.lhs.false11: ; preds = %if.end9
%call12 = call spir_func noundef i32 @_Z2fnv() #3 [ "convergencectrl"(token %0) ]
%tobool13 = icmp ne i32 %call12, 0
br i1 %tobool13, label %if.then14, label %if.end16

if.then14: ; preds = %lor.lhs.false11, %if.end9
%6 = load i32, ptr %val, align 4
%inc15 = add nsw i32 %6, 1
store i32 %inc15, ptr %val, align 4
br label %if.end16

if.end16: ; preds = %if.then14, %lor.lhs.false11
%call17 = call spir_func noundef i32 @_Z2fnv() #3 [ "convergencectrl"(token %0) ]
%tobool18 = icmp ne i32 %call17, 0
br i1 %tobool18, label %if.then21, label %lor.lhs.false19

lor.lhs.false19: ; preds = %if.end16
%7 = load i32, ptr %b, align 4
%tobool20 = icmp ne i32 %7, 0
br i1 %tobool20, label %if.then21, label %if.end23

if.then21: ; preds = %lor.lhs.false19, %if.end16
%8 = load i32, ptr %val, align 4
%inc22 = add nsw i32 %8, 1
store i32 %inc22, ptr %val, align 4
br label %if.end23

if.end23: ; preds = %if.then21, %lor.lhs.false19
%9 = load i32, ptr %val, align 4
ret i32 %9
}

; Function Attrs: convergent noinline norecurse nounwind optnone
define internal spir_func void @main() #0 {
entry:
%0 = call token @llvm.experimental.convergence.entry()
%call1 = call spir_func noundef i32 @_Z7processv() #3 [ "convergencectrl"(token %0) ]
ret void
}

; Function Attrs: convergent norecurse
define void @main.1() #2 {
entry:
call void @main()
ret void
}

attributes #0 = { convergent noinline norecurse nounwind optnone "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
attributes #1 = { convergent nocallback nofree nosync nounwind willreturn memory(none) }
attributes #2 = { convergent norecurse "frame-pointer"="all" "hlsl.numthreads"="1,1,1" "hlsl.shader"="compute" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
attributes #3 = { convergent }

!llvm.module.flags = !{!0, !1, !2}


!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{i32 4, !"dx.disable_optimizations", i32 1}
!2 = !{i32 7, !"frame-pointer", i32 2}


237 changes: 237 additions & 0 deletions llvm/test/CodeGen/SPIRV/structurizer/cf.return.early.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
; RUN: llc -mtriple=spirv-unknown-vulkan-compute -O0 %s -o - | FileCheck %s
; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-unknown-vulkan-compute %s -o - -filetype=obj | spirv-val %}
; RUN: llc -mtriple=spirv-unknown-vulkan-compute -O0 %s -o - | spirv-sim --function=_Z7processv --wave=1 --expects=0

;
; int process() {
; int cond = 1;
; int value = 0;
;
; while(value < 10) {
; switch(value) {
; case 1:
; value = 1;
; return value;
; case 2: {
; value = 3;
; {return value;} // Return from function.
; value = 4; // No SPIR-V should be emitted for this statement.
; break; // No SPIR-V should be emitted for this statement.
; }
; case 5 : {
; value = 5;
; {{return value;}} // Return from function.
; value = 6; // No SPIR-V should be emitted for this statement.
; }
; default:
; for (int i=0; i<10; ++i) {
; if (cond) {
; return value; // Return from function.
; return value; // No SPIR-V should be emitted for this statement.
; continue; // No SPIR-V should be emitted for this statement.
; break; // No SPIR-V should be emitted for this statement.
; ++value; // No SPIR-V should be emitted for this statement.
; } else {
; return value; // Return from function
; continue; // No SPIR-V should be emitted for this statement.
; break; // No SPIR-V should be emitted for this statement.
; ++value; // No SPIR-V should be emitted for this statement.
; }
; }
;
; // Return from function.
; // Even though this statement will never be executed [because both "if" and "else" above have return statements],
; // SPIR-V code should be emitted for it as we do not analyze the logic.
; return value;
; }
;
; // Return from function.
; // Even though this statement will never be executed [because all "case" statements above contain a return statement],
; // SPIR-V code should be emitted for it as we do not analyze the logic.
; return value;
; }
;
; return value;
; }
;
; [numthreads(1, 1, 1)]
; void main() {
; process();
; }

; CHECK: %[[#func_13:]] = OpFunction %[[#uint:]] DontInline %[[#]]
; CHECK: %[[#bb43:]] = OpLabel
; CHECK: OpBranch %[[#bb44:]]
; CHECK: %[[#bb44:]] = OpLabel
; CHECK: OpSelectionMerge %[[#bb45:]] None
; CHECK: OpBranchConditional %[[#]] %[[#bb46:]] %[[#bb47:]]
; CHECK: %[[#bb46:]] = OpLabel
; CHECK: OpSelectionMerge %[[#bb48:]] None
; CHECK: OpSwitch %[[#]] %[[#bb48:]] 1 %[[#bb49:]] 2 %[[#bb50:]] 5 %[[#bb51:]]
; CHECK: %[[#bb47:]] = OpLabel
; CHECK: OpBranch %[[#bb45:]]
; CHECK: %[[#bb49:]] = OpLabel
; CHECK: OpBranch %[[#bb48:]]
; CHECK: %[[#bb50:]] = OpLabel
; CHECK: OpBranch %[[#bb48:]]
; CHECK: %[[#bb51:]] = OpLabel
; CHECK: OpBranch %[[#bb48:]]
; CHECK: %[[#bb48:]] = OpLabel
; CHECK: OpBranchConditional %[[#]] %[[#bb52:]] %[[#bb45:]]
; CHECK: %[[#bb52:]] = OpLabel
; CHECK: OpBranch %[[#bb53:]]
; CHECK: %[[#bb53:]] = OpLabel
; CHECK: OpSelectionMerge %[[#bb54:]] None
; CHECK: OpBranchConditional %[[#]] %[[#bb54:]] %[[#bb55:]]
; CHECK: %[[#bb55:]] = OpLabel
; CHECK: OpBranch %[[#bb54:]]
; CHECK: %[[#bb54:]] = OpLabel
; CHECK: OpBranchConditional %[[#]] %[[#bb56:]] %[[#bb45:]]
; CHECK: %[[#bb56:]] = OpLabel
; CHECK: OpSelectionMerge %[[#bb57:]] None
; CHECK: OpBranchConditional %[[#]] %[[#bb57:]] %[[#bb58:]]
; CHECK: %[[#bb58:]] = OpLabel
; CHECK: OpBranch %[[#bb57:]]
; CHECK: %[[#bb57:]] = OpLabel
; CHECK: OpBranchConditional %[[#]] %[[#bb59:]] %[[#bb45:]]
; CHECK: %[[#bb59:]] = OpLabel
; CHECK: OpBranch %[[#bb45:]]
; CHECK: %[[#bb45:]] = OpLabel
; CHECK: OpReturnValue %[[#]]
; CHECK: OpFunctionEnd
; CHECK: %[[#func_39:]] = OpFunction %[[#void:]] DontInline %[[#]]
; CHECK: %[[#bb60:]] = OpLabel
; CHECK: OpReturn
; CHECK: OpFunctionEnd
; CHECK: %[[#func_41:]] = OpFunction %[[#void:]] None %[[#]]
; CHECK: %[[#bb61:]] = OpLabel
; CHECK: OpReturn
; CHECK: OpFunctionEnd

target datalayout = "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-G1"
target triple = "spirv-unknown-vulkan1.3-compute"

; Function Attrs: convergent noinline norecurse nounwind optnone
define spir_func noundef i32 @_Z7processv() #0 {
entry:
%0 = call token @llvm.experimental.convergence.entry()
%retval = alloca i32, align 4
%cond = alloca i32, align 4
%value = alloca i32, align 4
%i = alloca i32, align 4
store i32 1, ptr %cond, align 4
store i32 0, ptr %value, align 4
br label %while.cond

while.cond: ; preds = %entry
%1 = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token %0) ]
%2 = load i32, ptr %value, align 4
%cmp = icmp slt i32 %2, 10
br i1 %cmp, label %while.body, label %while.end

while.body: ; preds = %while.cond
%3 = load i32, ptr %value, align 4
switch i32 %3, label %sw.default [
i32 1, label %sw.bb
i32 2, label %sw.bb1
i32 5, label %sw.bb2
]

sw.bb: ; preds = %while.body
store i32 1, ptr %value, align 4
%4 = load i32, ptr %value, align 4
store i32 %4, ptr %retval, align 4
br label %return

sw.bb1: ; preds = %while.body
store i32 3, ptr %value, align 4
%5 = load i32, ptr %value, align 4
store i32 %5, ptr %retval, align 4
br label %return

sw.bb2: ; preds = %while.body
store i32 5, ptr %value, align 4
%6 = load i32, ptr %value, align 4
store i32 %6, ptr %retval, align 4
br label %return

sw.default: ; preds = %while.body
store i32 0, ptr %i, align 4
br label %for.cond

for.cond: ; preds = %for.inc, %sw.default
%7 = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token %1) ]
%8 = load i32, ptr %i, align 4
%cmp3 = icmp slt i32 %8, 10
br i1 %cmp3, label %for.body, label %for.end

for.body: ; preds = %for.cond
%9 = load i32, ptr %cond, align 4
%tobool = icmp ne i32 %9, 0
br i1 %tobool, label %if.then, label %if.else

if.then: ; preds = %for.body
%10 = load i32, ptr %value, align 4
store i32 %10, ptr %retval, align 4
br label %return

if.else: ; preds = %for.body
%11 = load i32, ptr %value, align 4
store i32 %11, ptr %retval, align 4
br label %return

for.inc: ; No predecessors!
%12 = load i32, ptr %i, align 4
%inc = add nsw i32 %12, 1
store i32 %inc, ptr %i, align 4
br label %for.cond

for.end: ; preds = %for.cond
%13 = load i32, ptr %value, align 4
store i32 %13, ptr %retval, align 4
br label %return

while.end: ; preds = %while.cond
%14 = load i32, ptr %value, align 4
store i32 %14, ptr %retval, align 4
br label %return

return: ; preds = %while.end, %for.end, %if.else, %if.then, %sw.bb2, %sw.bb1, %sw.bb
%15 = load i32, ptr %retval, align 4
ret i32 %15
}

; Function Attrs: convergent nocallback nofree nosync nounwind willreturn memory(none)
declare token @llvm.experimental.convergence.entry() #1

; Function Attrs: convergent nocallback nofree nosync nounwind willreturn memory(none)
declare token @llvm.experimental.convergence.loop() #1

; Function Attrs: convergent noinline norecurse nounwind optnone
define internal spir_func void @main() #0 {
entry:
%0 = call token @llvm.experimental.convergence.entry()
%call1 = call spir_func noundef i32 @_Z7processv() #3 [ "convergencectrl"(token %0) ]
ret void
}

; Function Attrs: convergent norecurse
define void @main.1() #2 {
entry:
call void @main()
ret void
}

attributes #0 = { convergent noinline norecurse nounwind optnone "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
attributes #1 = { convergent nocallback nofree nosync nounwind willreturn memory(none) }
attributes #2 = { convergent norecurse "frame-pointer"="all" "hlsl.numthreads"="1,1,1" "hlsl.shader"="compute" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
attributes #3 = { convergent }

!llvm.module.flags = !{!0, !1, !2}


!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{i32 4, !"dx.disable_optimizations", i32 1}
!2 = !{i32 7, !"frame-pointer", i32 2}


128 changes: 128 additions & 0 deletions llvm/test/CodeGen/SPIRV/structurizer/cf.return.early.simple.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
; RUN: llc -mtriple=spirv-unknown-vulkan-compute -O0 %s -o - | FileCheck %s
; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-unknown-vulkan-compute %s -o - -filetype=obj | spirv-val %}

;
; [numthreads(1, 1, 1)]
; void main() {
; int a, b;
; int cond = 1;
;
; while(cond) {
; switch(b) {
; default:
; if (cond) {
; if (cond)
; return;
; else
; return;
; }
; }
; return;
; }
; }

; CHECK: %[[#func_8:]] = OpFunction %[[#void:]] DontInline %[[#]]
; CHECK: %[[#bb22:]] = OpLabel
; CHECK: OpBranch %[[#bb23:]]
; CHECK: %[[#bb23:]] = OpLabel
; CHECK: OpSelectionMerge %[[#bb24:]] None
; CHECK: OpBranchConditional %[[#]] %[[#bb25:]] %[[#bb24:]]
; CHECK: %[[#bb25:]] = OpLabel
; CHECK: OpBranch %[[#bb26:]]
; CHECK: %[[#bb26:]] = OpLabel
; CHECK: OpSelectionMerge %[[#bb27:]] None
; CHECK: OpBranchConditional %[[#]] %[[#bb27:]] %[[#bb28:]]
; CHECK: %[[#bb28:]] = OpLabel
; CHECK: OpBranch %[[#bb27:]]
; CHECK: %[[#bb27:]] = OpLabel
; CHECK: OpBranchConditional %[[#]] %[[#bb29:]] %[[#bb24:]]
; CHECK: %[[#bb29:]] = OpLabel
; CHECK: OpSelectionMerge %[[#bb30:]] None
; CHECK: OpBranchConditional %[[#]] %[[#bb30:]] %[[#bb31:]]
; CHECK: %[[#bb31:]] = OpLabel
; CHECK: OpBranch %[[#bb30:]]
; CHECK: %[[#bb30:]] = OpLabel
; CHECK: OpBranch %[[#bb24:]]
; CHECK: %[[#bb24:]] = OpLabel
; CHECK: OpReturn
; CHECK: OpFunctionEnd
; CHECK: %[[#func_20:]] = OpFunction %[[#void:]] None %[[#]]
; CHECK: %[[#bb32:]] = OpLabel
; CHECK: OpReturn
; CHECK: OpFunctionEnd

target datalayout = "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-G1"
target triple = "spirv-unknown-vulkan1.3-compute"

; Function Attrs: convergent noinline norecurse nounwind optnone
define internal spir_func void @main() #0 {
entry:
%0 = call token @llvm.experimental.convergence.entry()
%a = alloca i32, align 4
%b = alloca i32, align 4
%cond = alloca i32, align 4
store i32 1, ptr %cond, align 4
br label %while.cond

while.cond: ; preds = %entry
%1 = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token %0) ]
%2 = load i32, ptr %cond, align 4
%tobool = icmp ne i32 %2, 0
br i1 %tobool, label %while.body, label %while.end

while.body: ; preds = %while.cond
%3 = load i32, ptr %b, align 4
switch i32 %3, label %sw.default [
]

sw.default: ; preds = %while.body
%4 = load i32, ptr %cond, align 4
%tobool1 = icmp ne i32 %4, 0
br i1 %tobool1, label %if.then, label %if.end

if.then: ; preds = %sw.default
%5 = load i32, ptr %cond, align 4
%tobool2 = icmp ne i32 %5, 0
br i1 %tobool2, label %if.then3, label %if.else

if.then3: ; preds = %if.then
br label %while.end

if.else: ; preds = %if.then
br label %while.end

if.end: ; preds = %sw.default
br label %sw.epilog

sw.epilog: ; preds = %if.end
br label %while.end

while.end: ; preds = %if.then3, %if.else, %sw.epilog, %while.cond
ret void
}

; Function Attrs: convergent norecurse
define void @main.1() #1 {
entry:
call void @main()
ret void
}

; Function Attrs: convergent nocallback nofree nosync nounwind willreturn memory(none)
declare token @llvm.experimental.convergence.entry() #2

; Function Attrs: convergent nocallback nofree nosync nounwind willreturn memory(none)
declare token @llvm.experimental.convergence.loop() #2

attributes #0 = { convergent noinline norecurse nounwind optnone "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
attributes #1 = { convergent norecurse "frame-pointer"="all" "hlsl.numthreads"="1,1,1" "hlsl.shader"="compute" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
attributes #2 = { convergent nocallback nofree nosync nounwind willreturn memory(none) }

!llvm.module.flags = !{!0, !1, !2}


!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{i32 4, !"dx.disable_optimizations", i32 1}
!2 = !{i32 7, !"frame-pointer", i32 2}


68 changes: 68 additions & 0 deletions llvm/test/CodeGen/SPIRV/structurizer/cf.return.void.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
; RUN: llc -mtriple=spirv-unknown-vulkan-compute -O0 %s -o - | FileCheck %s
; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-unknown-vulkan-compute %s -o - -filetype=obj | spirv-val %}

;
; void A() {
; }
;
; [numthreads(1, 1, 1)]
; void main() {
; return A();
; }

; CHECK: %[[#func_3:]] = OpFunction %[[#void:]] DontInline %[[#]]
; CHECK: %[[#bb8:]] = OpLabel
; CHECK: OpReturn
; CHECK: OpFunctionEnd
; CHECK: %[[#func_4:]] = OpFunction %[[#void:]] DontInline %[[#]]
; CHECK: %[[#bb9:]] = OpLabel
; CHECK: OpReturn
; CHECK: OpFunctionEnd
; CHECK: %[[#func_6:]] = OpFunction %[[#void:]] None %[[#]]
; CHECK: %[[#bb10:]] = OpLabel
; CHECK: OpReturn
; CHECK: OpFunctionEnd



target datalayout = "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-G1"
target triple = "spirv-unknown-vulkan1.3-compute"

; Function Attrs: convergent noinline norecurse nounwind optnone
define spir_func void @_Z1Av() #0 {
entry:
%0 = call token @llvm.experimental.convergence.entry()
ret void
}

; Function Attrs: convergent nocallback nofree nosync nounwind willreturn memory(none)
declare token @llvm.experimental.convergence.entry() #1

; Function Attrs: convergent noinline norecurse nounwind optnone
define internal spir_func void @main() #0 {
entry:
%0 = call token @llvm.experimental.convergence.entry()
call spir_func void @_Z1Av() #3 [ "convergencectrl"(token %0) ]
ret void
}

; Function Attrs: convergent norecurse
define void @main.1() #2 {
entry:
call void @main()
ret void
}

attributes #0 = { convergent noinline norecurse nounwind optnone "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
attributes #1 = { convergent nocallback nofree nosync nounwind willreturn memory(none) }
attributes #2 = { convergent norecurse "frame-pointer"="all" "hlsl.numthreads"="1,1,1" "hlsl.shader"="compute" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
attributes #3 = { convergent }

!llvm.module.flags = !{!0, !1, !2}


!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{i32 4, !"dx.disable_optimizations", i32 1}
!2 = !{i32 7, !"frame-pointer", i32 2}


507 changes: 507 additions & 0 deletions llvm/test/CodeGen/SPIRV/structurizer/cf.switch.ifstmt.ll

Large diffs are not rendered by default.

127 changes: 127 additions & 0 deletions llvm/test/CodeGen/SPIRV/structurizer/cf.switch.ifstmt.simple.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
; RUN: llc -mtriple=spirv-unknown-vulkan-compute -O0 %s -o - | FileCheck %s
; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-unknown-vulkan-compute %s -o - -filetype=obj | spirv-val %}
; RUN: llc -mtriple=spirv-unknown-vulkan-compute -O0 %s -o - | spirv-sim --function=_Z7processv --wave=1 --expects=5

;
; int process() {
; int b = 0;
; const int t = 50;
;
; switch(int d = 5) {
; case t:
; b = t;
; case 4:
; case 5:
; b = 5;
; break;
; default:
; break;
; }
;
; return b;
; }
;
; [numthreads(1, 1, 1)]
; void main() {
; process();
; }

; CHECK: %[[#func_13:]] = OpFunction %[[#uint:]] DontInline %[[#]]
; CHECK: %[[#bb25:]] = OpLabel
; CHECK: OpSelectionMerge %[[#bb26:]] None
; CHECK: OpBranchConditional %[[#]] %[[#bb27:]] %[[#bb28:]]
; CHECK: %[[#bb27:]] = OpLabel
; CHECK: OpSelectionMerge %[[#bb29:]] None
; CHECK: OpSwitch %[[#]] %[[#bb30:]] 50 %[[#bb31:]] 4 %[[#bb29:]] 5 %[[#bb32:]]
; CHECK: %[[#bb28:]] = OpLabel
; CHECK: %[[#bb30:]] = OpLabel
; CHECK: OpBranch %[[#bb29:]]
; CHECK: %[[#bb31:]] = OpLabel
; CHECK: OpBranch %[[#bb29:]]
; CHECK: %[[#bb32:]] = OpLabel
; CHECK: OpBranch %[[#bb29:]]
; CHECK: %[[#bb29:]] = OpLabel
; CHECK: OpBranchConditional %[[#]] %[[#bb33:]] %[[#bb26:]]
; CHECK: %[[#bb33:]] = OpLabel
; CHECK: OpBranch %[[#bb26:]]
; CHECK: %[[#bb26:]] = OpLabel
; CHECK: OpReturnValue %[[#]]
; CHECK: OpFunctionEnd
; CHECK: %[[#func_21:]] = OpFunction %[[#void:]] DontInline %[[#]]
; CHECK: %[[#bb34:]] = OpLabel
; CHECK: OpReturn
; CHECK: OpFunctionEnd
; CHECK: %[[#func_23:]] = OpFunction %[[#void:]] None %[[#]]
; CHECK: %[[#bb35:]] = OpLabel
; CHECK: OpReturn
; CHECK: OpFunctionEnd



target datalayout = "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-G1"
target triple = "spirv-unknown-vulkan1.3-compute"

; Function Attrs: convergent noinline norecurse nounwind optnone
define spir_func noundef i32 @_Z7processv() #0 {
entry:
%0 = call token @llvm.experimental.convergence.entry()
%b = alloca i32, align 4
%t = alloca i32, align 4
%d = alloca i32, align 4
store i32 0, ptr %b, align 4
store i32 50, ptr %t, align 4
store i32 5, ptr %d, align 4
%1 = load i32, ptr %d, align 4
switch i32 %1, label %sw.default [
i32 50, label %sw.bb
i32 4, label %sw.bb1
i32 5, label %sw.bb1
]

sw.bb: ; preds = %entry
store i32 50, ptr %b, align 4
br label %sw.bb1

sw.bb1: ; preds = %entry, %entry, %sw.bb
store i32 5, ptr %b, align 4
br label %sw.epilog

sw.default: ; preds = %entry
br label %sw.epilog

sw.epilog: ; preds = %sw.default, %sw.bb1
%2 = load i32, ptr %b, align 4
ret i32 %2
}

; Function Attrs: convergent nocallback nofree nosync nounwind willreturn memory(none)
declare token @llvm.experimental.convergence.entry() #1

; Function Attrs: convergent noinline norecurse nounwind optnone
define internal spir_func void @main() #0 {
entry:
%0 = call token @llvm.experimental.convergence.entry()
%call1 = call spir_func noundef i32 @_Z7processv() #3 [ "convergencectrl"(token %0) ]
ret void
}

; Function Attrs: convergent norecurse
define void @main.1() #2 {
entry:
call void @main()
ret void
}

attributes #0 = { convergent noinline norecurse nounwind optnone "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
attributes #1 = { convergent nocallback nofree nosync nounwind willreturn memory(none) }
attributes #2 = { convergent norecurse "frame-pointer"="all" "hlsl.numthreads"="1,1,1" "hlsl.shader"="compute" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
attributes #3 = { convergent }

!llvm.module.flags = !{!0, !1, !2}


!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{i32 4, !"dx.disable_optimizations", i32 1}
!2 = !{i32 7, !"frame-pointer", i32 2}


243 changes: 243 additions & 0 deletions llvm/test/CodeGen/SPIRV/structurizer/cf.switch.ifstmt.simple2.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
; RUN: llc -mtriple=spirv-unknown-vulkan-compute -O0 %s -o - | FileCheck %s
; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-unknown-vulkan-compute %s -o - -filetype=obj | spirv-val %}
; RUN: llc -mtriple=spirv-unknown-vulkan-compute -O0 %s -o - | spirv-sim --function=_Z7processv --wave=1 --expects=5

;
; int foo() { return 200; }
;
; int process() {
; int a = 0;
; int b = 0;
; int c = 0;
; const int r = 20;
; const int s = 40;
; const int t = 3*r+2*s;
;
; switch(int d = 5) {
; case 1:
; b += 1;
; c += foo();
; case 2:
; b += 2;
; break;
; case 3:
; {
; b += 3;
; break;
; }
; case t:
; b += t;
; case 4:
; case 5:
; b += 5;
; break;
; case 6: {
; case 7:
; break;}
; default:
; break;
; }
;
; return a + b + c;
; }
;
; [numthreads(1, 1, 1)]
; void main() {
; process();
; }

; CHECK: %[[#func_18:]] = OpFunction %[[#uint:]] DontInline %[[#]]
; CHECK: %[[#bb52:]] = OpLabel
; CHECK: OpReturnValue %[[#]]
; CHECK: OpFunctionEnd
; CHECK: %[[#func_19:]] = OpFunction %[[#uint:]] DontInline %[[#]]
; CHECK: %[[#bb53:]] = OpLabel
; CHECK: OpSelectionMerge %[[#bb54:]] None
; CHECK: OpBranchConditional %[[#]] %[[#bb55:]] %[[#bb56:]]
; CHECK: %[[#bb55:]] = OpLabel
; CHECK: OpSelectionMerge %[[#bb57:]] None
; CHECK: OpBranchConditional %[[#]] %[[#bb58:]] %[[#bb59:]]
; CHECK: %[[#bb56:]] = OpLabel
; CHECK: %[[#bb58:]] = OpLabel
; CHECK: OpSelectionMerge %[[#bb60:]] None
; CHECK: OpBranchConditional %[[#]] %[[#bb61:]] %[[#bb62:]]
; CHECK: %[[#bb59:]] = OpLabel
; CHECK: %[[#bb61:]] = OpLabel
; CHECK: OpSelectionMerge %[[#bb63:]] None
; CHECK: OpSwitch %[[#]] %[[#bb64:]] 1 %[[#bb65:]] 2 %[[#bb63:]] 3 %[[#bb66:]] 140 %[[#bb67:]] 4 %[[#bb68:]] 5 %[[#bb69:]] 6 %[[#bb70:]] 7 %[[#bb71:]]
; CHECK: %[[#bb62:]] = OpLabel
; CHECK: %[[#bb64:]] = OpLabel
; CHECK: OpBranch %[[#bb63:]]
; CHECK: %[[#bb65:]] = OpLabel
; CHECK: OpBranch %[[#bb63:]]
; CHECK: %[[#bb66:]] = OpLabel
; CHECK: OpBranch %[[#bb63:]]
; CHECK: %[[#bb67:]] = OpLabel
; CHECK: OpBranch %[[#bb63:]]
; CHECK: %[[#bb68:]] = OpLabel
; CHECK: OpBranch %[[#bb63:]]
; CHECK: %[[#bb69:]] = OpLabel
; CHECK: OpBranch %[[#bb63:]]
; CHECK: %[[#bb70:]] = OpLabel
; CHECK: OpBranch %[[#bb63:]]
; CHECK: %[[#bb71:]] = OpLabel
; CHECK: OpBranch %[[#bb63:]]
; CHECK: %[[#bb63:]] = OpLabel
; CHECK: OpSelectionMerge %[[#bb72:]] None
; CHECK: OpSwitch %[[#]] %[[#bb73:]] 1 %[[#bb72:]] 2 %[[#bb74:]] 3 %[[#bb75:]]
; CHECK: %[[#bb73:]] = OpLabel
; CHECK: OpBranch %[[#bb72:]]
; CHECK: %[[#bb74:]] = OpLabel
; CHECK: OpBranch %[[#bb72:]]
; CHECK: %[[#bb75:]] = OpLabel
; CHECK: OpBranch %[[#bb72:]]
; CHECK: %[[#bb72:]] = OpLabel
; CHECK: OpBranch %[[#bb60:]]
; CHECK: %[[#bb60:]] = OpLabel
; CHECK: OpSelectionMerge %[[#bb76:]] None
; CHECK: OpSwitch %[[#]] %[[#bb77:]] 1 %[[#bb76:]] 2 %[[#bb78:]]
; CHECK: %[[#bb77:]] = OpLabel
; CHECK: OpBranch %[[#bb76:]]
; CHECK: %[[#bb78:]] = OpLabel
; CHECK: OpBranch %[[#bb76:]]
; CHECK: %[[#bb76:]] = OpLabel
; CHECK: OpBranch %[[#bb57:]]
; CHECK: %[[#bb57:]] = OpLabel
; CHECK: OpBranch %[[#bb54:]]
; CHECK: %[[#bb54:]] = OpLabel
; CHECK: OpReturnValue %[[#]]
; CHECK: OpFunctionEnd
; CHECK: %[[#func_48:]] = OpFunction %[[#void:]] DontInline %[[#]]
; CHECK: %[[#bb79:]] = OpLabel
; CHECK: OpReturn
; CHECK: OpFunctionEnd
; CHECK: %[[#func_50:]] = OpFunction %[[#void:]] None %[[#]]
; CHECK: %[[#bb80:]] = OpLabel
; CHECK: OpReturn
; CHECK: OpFunctionEnd



target datalayout = "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-G1"
target triple = "spirv-unknown-vulkan1.3-compute"

; Function Attrs: convergent noinline norecurse nounwind optnone
define spir_func noundef i32 @_Z3foov() #0 {
entry:
%0 = call token @llvm.experimental.convergence.entry()
ret i32 200
}

; Function Attrs: convergent nocallback nofree nosync nounwind willreturn memory(none)
declare token @llvm.experimental.convergence.entry() #1

; Function Attrs: convergent noinline norecurse nounwind optnone
define spir_func noundef i32 @_Z7processv() #0 {
entry:
%0 = call token @llvm.experimental.convergence.entry()
%a = alloca i32, align 4
%b = alloca i32, align 4
%c = alloca i32, align 4
%r = alloca i32, align 4
%s = alloca i32, align 4
%t = alloca i32, align 4
%d = alloca i32, align 4
store i32 0, ptr %a, align 4
store i32 0, ptr %b, align 4
store i32 0, ptr %c, align 4
store i32 20, ptr %r, align 4
store i32 40, ptr %s, align 4
store i32 140, ptr %t, align 4
store i32 5, ptr %d, align 4
%1 = load i32, ptr %d, align 4
switch i32 %1, label %sw.default [
i32 1, label %sw.bb
i32 2, label %sw.bb3
i32 3, label %sw.bb5
i32 140, label %sw.bb7
i32 4, label %sw.bb9
i32 5, label %sw.bb9
i32 6, label %sw.bb11
i32 7, label %sw.bb12
]

sw.bb: ; preds = %entry
%2 = load i32, ptr %b, align 4
%add = add nsw i32 %2, 1
store i32 %add, ptr %b, align 4
%call1 = call spir_func noundef i32 @_Z3foov() #3 [ "convergencectrl"(token %0) ]
%3 = load i32, ptr %c, align 4
%add2 = add nsw i32 %3, %call1
store i32 %add2, ptr %c, align 4
br label %sw.bb3

sw.bb3: ; preds = %entry, %sw.bb
%4 = load i32, ptr %b, align 4
%add4 = add nsw i32 %4, 2
store i32 %add4, ptr %b, align 4
br label %sw.epilog

sw.bb5: ; preds = %entry
%5 = load i32, ptr %b, align 4
%add6 = add nsw i32 %5, 3
store i32 %add6, ptr %b, align 4
br label %sw.epilog

sw.bb7: ; preds = %entry
%6 = load i32, ptr %b, align 4
%add8 = add nsw i32 %6, 140
store i32 %add8, ptr %b, align 4
br label %sw.bb9

sw.bb9: ; preds = %entry, %entry, %sw.bb7
%7 = load i32, ptr %b, align 4
%add10 = add nsw i32 %7, 5
store i32 %add10, ptr %b, align 4
br label %sw.epilog

sw.bb11: ; preds = %entry
br label %sw.bb12

sw.bb12: ; preds = %entry, %sw.bb11
br label %sw.epilog

sw.default: ; preds = %entry
br label %sw.epilog

sw.epilog: ; preds = %sw.default, %sw.bb12, %sw.bb9, %sw.bb5, %sw.bb3
%8 = load i32, ptr %a, align 4
%9 = load i32, ptr %b, align 4
%add13 = add nsw i32 %8, %9
%10 = load i32, ptr %c, align 4
%add14 = add nsw i32 %add13, %10
ret i32 %add14
}

; Function Attrs: convergent noinline norecurse nounwind optnone
define internal spir_func void @main() #0 {
entry:
%0 = call token @llvm.experimental.convergence.entry()
%call1 = call spir_func noundef i32 @_Z7processv() #3 [ "convergencectrl"(token %0) ]
ret void
}

; Function Attrs: convergent norecurse
define void @main.1() #2 {
entry:
call void @main()
ret void
}

attributes #0 = { convergent noinline norecurse nounwind optnone "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
attributes #1 = { convergent nocallback nofree nosync nounwind willreturn memory(none) }
attributes #2 = { convergent norecurse "frame-pointer"="all" "hlsl.numthreads"="1,1,1" "hlsl.shader"="compute" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
attributes #3 = { convergent }

!llvm.module.flags = !{!0, !1, !2}


!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{i32 4, !"dx.disable_optimizations", i32 1}
!2 = !{i32 7, !"frame-pointer", i32 2}


70 changes: 70 additions & 0 deletions llvm/test/CodeGen/SPIRV/structurizer/cf.switch.opswitch.literal.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
; RUN: llc -mtriple=spirv-unknown-vulkan-compute -O0 %s -o - | FileCheck %s
; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-unknown-vulkan-compute %s -o - -filetype=obj | spirv-val %}

;
; [numthreads(1, 1, 1)]
; void main() {
; ///////////////////////////////
; // 32-bit int literal switch //
; ///////////////////////////////
; switch (0) {
; case 0:
; return;
; default:
; return;
; }
;
; ///////////////////////////////
; // 64-bit int literal switch //
; ///////////////////////////////
; switch (12345678910) {
; case 12345678910:
; return;
; }
;
; return;
; }

; CHECK: %[[#func_3:]] = OpFunction %[[#void:]] DontInline %[[#]]
; CHECK: %[[#bb6:]] = OpLabel
; CHECK: OpReturn
; CHECK: OpFunctionEnd
; CHECK: %[[#func_4:]] = OpFunction %[[#void:]] None %[[#]]
; CHECK: %[[#bb7:]] = OpLabel
; CHECK: OpReturn
; CHECK: OpFunctionEnd



target datalayout = "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-G1"
target triple = "spirv-unknown-vulkan1.3-compute"

; Function Attrs: convergent noinline norecurse nounwind optnone
define internal spir_func void @main() #0 {
entry:
%0 = call token @llvm.experimental.convergence.entry()
ret void
}

; Function Attrs: convergent norecurse
define void @main.1() #1 {
entry:
call void @main()
ret void
}

; Function Attrs: convergent nocallback nofree nosync nounwind willreturn memory(none)
declare token @llvm.experimental.convergence.entry() #2

attributes #0 = { convergent noinline norecurse nounwind optnone "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
attributes #1 = { convergent norecurse "frame-pointer"="all" "hlsl.numthreads"="1,1,1" "hlsl.shader"="compute" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
attributes #2 = { convergent nocallback nofree nosync nounwind willreturn memory(none) }

!llvm.module.flags = !{!0, !1, !2}


!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{i32 4, !"dx.disable_optimizations", i32 1}
!2 = !{i32 7, !"frame-pointer", i32 2}


717 changes: 717 additions & 0 deletions llvm/test/CodeGen/SPIRV/structurizer/cf.switch.opswitch.ll

Large diffs are not rendered by default.

165 changes: 165 additions & 0 deletions llvm/test/CodeGen/SPIRV/structurizer/cf.switch.opswitch.simple.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
; RUN: llc -mtriple=spirv-unknown-vulkan-compute -O0 %s -o - | FileCheck %s
; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-unknown-vulkan-compute %s -o - -filetype=obj | spirv-val %}

;
; int foo() { return 200; }
;
; [numthreads(1, 1, 1)]
; void main() {
; int result;
;
; int a = 0;
; switch(a) {
; case -3:
; result = -300;
; break;
; case 0:
; result = 0;
; break;
; case 1:
; result = 100;
; break;
; case 2:
; result = foo();
; break;
; default:
; result = 777;
; break;
; }
;
; switch(int c = a) {
; case -4:
; result = -400;
; break;
; case 4:
; result = 400;
; break;
; }
; }

; CHECK: %[[#func_14:]] = OpFunction %[[#uint:]] DontInline %[[#]]
; CHECK: %[[#bb25:]] = OpLabel
; CHECK: OpReturnValue %[[#]]
; CHECK: OpFunctionEnd
; CHECK: %[[#func_15:]] = OpFunction %[[#void:]] DontInline %[[#]]
; CHECK: %[[#bb26:]] = OpLabel
; CHECK: OpSelectionMerge %[[#bb27:]] None
; CHECK: OpSwitch %[[#]] %[[#bb28:]] 4294967293 %[[#bb29:]] 0 %[[#bb30:]] 1 %[[#bb31:]] 2 %[[#bb32:]]
; CHECK: %[[#bb28:]] = OpLabel
; CHECK: OpBranch %[[#bb27:]]
; CHECK: %[[#bb29:]] = OpLabel
; CHECK: OpBranch %[[#bb27:]]
; CHECK: %[[#bb30:]] = OpLabel
; CHECK: OpBranch %[[#bb27:]]
; CHECK: %[[#bb31:]] = OpLabel
; CHECK: OpBranch %[[#bb27:]]
; CHECK: %[[#bb32:]] = OpLabel
; CHECK: OpBranch %[[#bb27:]]
; CHECK: %[[#bb27:]] = OpLabel
; CHECK: OpSelectionMerge %[[#bb33:]] None
; CHECK: OpSwitch %[[#]] %[[#bb33:]] 4294967292 %[[#bb34:]] 4 %[[#bb35:]]
; CHECK: %[[#bb34:]] = OpLabel
; CHECK: OpBranch %[[#bb33:]]
; CHECK: %[[#bb35:]] = OpLabel
; CHECK: OpBranch %[[#bb33:]]
; CHECK: %[[#bb33:]] = OpLabel
; CHECK: OpReturn
; CHECK: OpFunctionEnd
; CHECK: %[[#func_23:]] = OpFunction %[[#void:]] None %[[#]]
; CHECK: %[[#bb36:]] = OpLabel
; CHECK: OpReturn
; CHECK: OpFunctionEnd



target datalayout = "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-G1"
target triple = "spirv-unknown-vulkan1.3-compute"

; Function Attrs: convergent noinline norecurse nounwind optnone
define spir_func noundef i32 @_Z3foov() #0 {
entry:
%0 = call token @llvm.experimental.convergence.entry()
ret i32 200
}

; Function Attrs: convergent nocallback nofree nosync nounwind willreturn memory(none)
declare token @llvm.experimental.convergence.entry() #1

; Function Attrs: convergent noinline norecurse nounwind optnone
define internal spir_func void @main() #0 {
entry:
%0 = call token @llvm.experimental.convergence.entry()
%result = alloca i32, align 4
%a = alloca i32, align 4
%c = alloca i32, align 4
store i32 0, ptr %a, align 4
%1 = load i32, ptr %a, align 4
switch i32 %1, label %sw.default [
i32 -3, label %sw.bb
i32 0, label %sw.bb1
i32 1, label %sw.bb2
i32 2, label %sw.bb3
]

sw.bb: ; preds = %entry
store i32 -300, ptr %result, align 4
br label %sw.epilog

sw.bb1: ; preds = %entry
store i32 0, ptr %result, align 4
br label %sw.epilog

sw.bb2: ; preds = %entry
store i32 100, ptr %result, align 4
br label %sw.epilog

sw.bb3: ; preds = %entry
%call4 = call spir_func noundef i32 @_Z3foov() #3 [ "convergencectrl"(token %0) ]
store i32 %call4, ptr %result, align 4
br label %sw.epilog

sw.default: ; preds = %entry
store i32 777, ptr %result, align 4
br label %sw.epilog

sw.epilog: ; preds = %sw.default, %sw.bb3, %sw.bb2, %sw.bb1, %sw.bb
%2 = load i32, ptr %a, align 4
store i32 %2, ptr %c, align 4
%3 = load i32, ptr %c, align 4
switch i32 %3, label %sw.epilog7 [
i32 -4, label %sw.bb5
i32 4, label %sw.bb6
]

sw.bb5: ; preds = %sw.epilog
store i32 -400, ptr %result, align 4
br label %sw.epilog7

sw.bb6: ; preds = %sw.epilog
store i32 400, ptr %result, align 4
br label %sw.epilog7

sw.epilog7: ; preds = %sw.epilog, %sw.bb6, %sw.bb5
ret void
}

; Function Attrs: convergent norecurse
define void @main.1() #2 {
entry:
call void @main()
ret void
}

attributes #0 = { convergent noinline norecurse nounwind optnone "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
attributes #1 = { convergent nocallback nofree nosync nounwind willreturn memory(none) }
attributes #2 = { convergent norecurse "frame-pointer"="all" "hlsl.numthreads"="1,1,1" "hlsl.shader"="compute" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
attributes #3 = { convergent }

!llvm.module.flags = !{!0, !1, !2}


!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{i32 4, !"dx.disable_optimizations", i32 1}
!2 = !{i32 7, !"frame-pointer", i32 2}


190 changes: 190 additions & 0 deletions llvm/test/CodeGen/SPIRV/structurizer/cf.while.break.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
; RUN: llc -mtriple=spirv-unknown-vulkan-compute -O0 %s -o - | FileCheck %s
; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-unknown-vulkan-compute %s -o - -filetype=obj | spirv-val %}

;
; int foo() { return true; }
;
; [numthreads(1, 1, 1)]
; void main() {
; int val = 0;
; int i = 0;
;
; while (i < 10) {
; val = i;
; if (val > 5) {
; break;
; }
;
; if (val > 6) {
; break;
; break; // No SPIR-V should be emitted for this statement.
; val++; // No SPIR-V should be emitted for this statement.
; while(true); // No SPIR-V should be emitted for this statement.
; --i; // No SPIR-V should be emitted for this statement.
; }
; }
;
; ////////////////////////////////////////////////////////////////////////////////
; // Nested while loops with break statements //
; // Each break statement should branch to the corresponding loop's break block //
; ////////////////////////////////////////////////////////////////////////////////
;
; while (true) {
; i++;
; while(i<20) {
; val = i;
; {{break;}}
; }
; --i;
; break;
; }
; }

; CHECK: %[[#func_16:]] = OpFunction %[[#uint:]] DontInline %[[#]]
; CHECK: %[[#bb37:]] = OpLabel
; CHECK: OpReturnValue %[[#]]
; CHECK: OpFunctionEnd
; CHECK: %[[#func_17:]] = OpFunction %[[#void:]] DontInline %[[#]]
; CHECK: %[[#bb38:]] = OpLabel
; CHECK: OpBranch %[[#bb39:]]
; CHECK: %[[#bb39:]] = OpLabel
; CHECK: OpLoopMerge %[[#bb40:]] %[[#bb41:]] None
; CHECK: OpBranchConditional %[[#]] %[[#bb42:]] %[[#bb40:]]
; CHECK: %[[#bb42:]] = OpLabel
; CHECK: OpBranchConditional %[[#]] %[[#bb40:]] %[[#bb43:]]
; CHECK: %[[#bb43:]] = OpLabel
; CHECK: OpBranchConditional %[[#]] %[[#bb40:]] %[[#bb41:]]
; CHECK: %[[#bb40:]] = OpLabel
; CHECK: OpSelectionMerge %[[#bb44:]] None
; CHECK: OpSwitch %[[#]] %[[#bb44:]] 1 %[[#bb44:]] 2 %[[#bb44:]]
; CHECK: %[[#bb41:]] = OpLabel
; CHECK: OpBranch %[[#bb39:]]
; CHECK: %[[#bb44:]] = OpLabel
; CHECK: OpBranch %[[#bb45:]]
; CHECK: %[[#bb45:]] = OpLabel
; CHECK: OpBranch %[[#bb46:]]
; CHECK: %[[#bb46:]] = OpLabel
; CHECK: OpBranch %[[#bb47:]]
; CHECK: %[[#bb47:]] = OpLabel
; CHECK: OpSelectionMerge %[[#bb48:]] None
; CHECK: OpBranchConditional %[[#]] %[[#bb49:]] %[[#bb48:]]
; CHECK: %[[#bb49:]] = OpLabel
; CHECK: OpBranch %[[#bb48:]]
; CHECK: %[[#bb48:]] = OpLabel
; CHECK: OpBranch %[[#bb50:]]
; CHECK: %[[#bb50:]] = OpLabel
; CHECK: OpReturn
; CHECK: OpFunctionEnd
; CHECK: %[[#func_35:]] = OpFunction %[[#void:]] None %[[#]]
; CHECK: %[[#bb51:]] = OpLabel
; CHECK: OpReturn
; CHECK: OpFunctionEnd



target datalayout = "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-G1"
target triple = "spirv-unknown-vulkan1.3-compute"

; Function Attrs: convergent noinline norecurse nounwind optnone
define spir_func noundef i32 @_Z3foov() #0 {
entry:
%0 = call token @llvm.experimental.convergence.entry()
ret i32 1
}

; Function Attrs: convergent nocallback nofree nosync nounwind willreturn memory(none)
declare token @llvm.experimental.convergence.entry() #1

; Function Attrs: convergent noinline norecurse nounwind optnone
define internal spir_func void @main() #0 {
entry:
%0 = call token @llvm.experimental.convergence.entry()
%val = alloca i32, align 4
%i = alloca i32, align 4
store i32 0, ptr %val, align 4
store i32 0, ptr %i, align 4
br label %while.cond

while.cond: ; preds = %if.end4, %entry
%1 = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token %0) ]
%2 = load i32, ptr %i, align 4
%cmp = icmp slt i32 %2, 10
br i1 %cmp, label %while.body, label %while.end

while.body: ; preds = %while.cond
%3 = load i32, ptr %i, align 4
store i32 %3, ptr %val, align 4
%4 = load i32, ptr %val, align 4
%cmp1 = icmp sgt i32 %4, 5
br i1 %cmp1, label %if.then, label %if.end

if.then: ; preds = %while.body
br label %while.end

if.end: ; preds = %while.body
%5 = load i32, ptr %val, align 4
%cmp2 = icmp sgt i32 %5, 6
br i1 %cmp2, label %if.then3, label %if.end4

if.then3: ; preds = %if.end
br label %while.end

if.end4: ; preds = %if.end
br label %while.cond

while.end: ; preds = %if.then3, %if.then, %while.cond
br label %while.cond5

while.cond5: ; preds = %while.end
%6 = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token %0) ]
br label %while.body6

while.body6: ; preds = %while.cond5
%7 = load i32, ptr %i, align 4
%inc = add nsw i32 %7, 1
store i32 %inc, ptr %i, align 4
br label %while.cond7

while.cond7: ; preds = %while.body6
%8 = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token %6) ]
%9 = load i32, ptr %i, align 4
%cmp8 = icmp slt i32 %9, 20
br i1 %cmp8, label %while.body9, label %while.end10

while.body9: ; preds = %while.cond7
%10 = load i32, ptr %i, align 4
store i32 %10, ptr %val, align 4
br label %while.end10

while.end10: ; preds = %while.body9, %while.cond7
%11 = load i32, ptr %i, align 4
%dec = add nsw i32 %11, -1
store i32 %dec, ptr %i, align 4
br label %while.end11

while.end11: ; preds = %while.end10
ret void
}

; Function Attrs: convergent norecurse
define void @main.1() #2 {
entry:
call void @main()
ret void
}

; Function Attrs: convergent nocallback nofree nosync nounwind willreturn memory(none)
declare token @llvm.experimental.convergence.loop() #1

attributes #0 = { convergent noinline norecurse nounwind optnone "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
attributes #1 = { convergent nocallback nofree nosync nounwind willreturn memory(none) }
attributes #2 = { convergent norecurse "frame-pointer"="all" "hlsl.numthreads"="1,1,1" "hlsl.shader"="compute" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }

!llvm.module.flags = !{!0, !1, !2}


!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{i32 4, !"dx.disable_optimizations", i32 1}
!2 = !{i32 7, !"frame-pointer", i32 2}


198 changes: 198 additions & 0 deletions llvm/test/CodeGen/SPIRV/structurizer/cf.while.continue.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
; RUN: llc -mtriple=spirv-unknown-vulkan-compute -O0 %s -o - | FileCheck %s
; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-unknown-vulkan-compute %s -o - -filetype=obj | spirv-val %}

;
; int foo() { return true; }
;
; [numthreads(1, 1, 1)]
; void main() {
; int val = 0;
; int i = 0;
;
; while (i < 10) {
; val = i;
; if (val > 5) {
; continue;
; }
;
; if (val > 6) {
; {{continue;}}
; val++; // No SPIR-V should be emitted for this statement.
; continue; // No SPIR-V should be emitted for this statement.
; while(true); // No SPIR-V should be emitted for this statement.
; --i; // No SPIR-V should be emitted for this statement.
; }
;
; }
;
; //////////////////////////////////////////////////////////////////////////////////////
; // Nested while loops with continue statements //
; // Each continue statement should branch to the corresponding loop's continue block //
; //////////////////////////////////////////////////////////////////////////////////////
;
; while (true) {
; i++;
;
; while(i<20) {
; val = i;
; continue;
; }
; --i;
; continue;
; continue; // No SPIR-V should be emitted for this statement.
;
; }
; }

; CHECK: %[[#func_15:]] = OpFunction %[[#uint:]] DontInline %[[#]]
; CHECK: %[[#bb35:]] = OpLabel
; CHECK: OpReturnValue %[[#]]
; CHECK: OpFunctionEnd
; CHECK: %[[#func_16:]] = OpFunction %[[#void:]] DontInline %[[#]]
; CHECK: %[[#bb36:]] = OpLabel
; CHECK: OpBranch %[[#bb37:]]
; CHECK: %[[#bb37:]] = OpLabel
; CHECK: OpLoopMerge %[[#bb38:]] %[[#bb39:]] None
; CHECK: OpBranchConditional %[[#]] %[[#bb40:]] %[[#bb38:]]
; CHECK: %[[#bb40:]] = OpLabel
; CHECK: OpSelectionMerge %[[#bb41:]] None
; CHECK: OpBranchConditional %[[#]] %[[#bb41:]] %[[#bb42:]]
; CHECK: %[[#bb42:]] = OpLabel
; CHECK: OpSelectionMerge %[[#bb43:]] None
; CHECK: OpBranchConditional %[[#]] %[[#bb43:]] %[[#bb44:]]
; CHECK: %[[#bb44:]] = OpLabel
; CHECK: OpBranch %[[#bb43:]]
; CHECK: %[[#bb43:]] = OpLabel
; CHECK: OpBranch %[[#bb41:]]
; CHECK: %[[#bb41:]] = OpLabel
; CHECK: OpBranch %[[#bb39:]]
; CHECK: %[[#bb39:]] = OpLabel
; CHECK: OpBranch %[[#bb37:]]
; CHECK: %[[#bb38:]] = OpLabel
; CHECK: OpBranch %[[#bb45:]]
; CHECK: %[[#bb45:]] = OpLabel
; CHECK: OpLoopMerge %[[#bb46:]] %[[#bb47:]] None
; CHECK: OpBranchConditional %[[#]] %[[#bb46:]] %[[#bb48:]]
; CHECK: %[[#bb48:]] = OpLabel
; CHECK: OpBranch %[[#bb49:]]
; CHECK: %[[#bb49:]] = OpLabel
; CHECK: OpLoopMerge %[[#bb50:]] %[[#bb51:]] None
; CHECK: OpBranchConditional %[[#]] %[[#bb51:]] %[[#bb50:]]
; CHECK: %[[#bb51:]] = OpLabel
; CHECK: OpBranch %[[#bb49:]]
; CHECK: %[[#bb50:]] = OpLabel
; CHECK: OpBranch %[[#bb47:]]
; CHECK: %[[#bb47:]] = OpLabel
; CHECK: OpBranch %[[#bb45:]]
; CHECK: %[[#bb46:]] = OpLabel
; CHECK: OpFunctionEnd
; CHECK: %[[#func_33:]] = OpFunction %[[#void:]] None %[[#]]
; CHECK: %[[#bb52:]] = OpLabel
; CHECK: OpReturn
; CHECK: OpFunctionEnd



target datalayout = "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-G1"
target triple = "spirv-unknown-vulkan1.3-compute"

; Function Attrs: convergent noinline norecurse nounwind optnone
define spir_func noundef i32 @_Z3foov() #0 {
entry:
%0 = call token @llvm.experimental.convergence.entry()
ret i32 1
}

; Function Attrs: convergent nocallback nofree nosync nounwind willreturn memory(none)
declare token @llvm.experimental.convergence.entry() #1

; Function Attrs: convergent noinline norecurse nounwind optnone
define internal spir_func void @main() #0 {
entry:
%0 = call token @llvm.experimental.convergence.entry()
%val = alloca i32, align 4
%i = alloca i32, align 4
store i32 0, ptr %val, align 4
store i32 0, ptr %i, align 4
br label %while.cond

while.cond: ; preds = %if.end4, %if.then3, %if.then, %entry
%1 = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token %0) ]
%2 = load i32, ptr %i, align 4
%cmp = icmp slt i32 %2, 10
br i1 %cmp, label %while.body, label %while.end

while.body: ; preds = %while.cond
%3 = load i32, ptr %i, align 4
store i32 %3, ptr %val, align 4
%4 = load i32, ptr %val, align 4
%cmp1 = icmp sgt i32 %4, 5
br i1 %cmp1, label %if.then, label %if.end

if.then: ; preds = %while.body
br label %while.cond

if.end: ; preds = %while.body
%5 = load i32, ptr %val, align 4
%cmp2 = icmp sgt i32 %5, 6
br i1 %cmp2, label %if.then3, label %if.end4

if.then3: ; preds = %if.end
br label %while.cond

if.end4: ; preds = %if.end
br label %while.cond

while.end: ; preds = %while.cond
br label %while.cond5

while.cond5: ; preds = %while.end10, %while.end
%6 = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token %0) ]
br label %while.body6

while.body6: ; preds = %while.cond5
%7 = load i32, ptr %i, align 4
%inc = add nsw i32 %7, 1
store i32 %inc, ptr %i, align 4
br label %while.cond7

while.cond7: ; preds = %while.body9, %while.body6
%8 = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token %6) ]
%9 = load i32, ptr %i, align 4
%cmp8 = icmp slt i32 %9, 20
br i1 %cmp8, label %while.body9, label %while.end10

while.body9: ; preds = %while.cond7
%10 = load i32, ptr %i, align 4
store i32 %10, ptr %val, align 4
br label %while.cond7

while.end10: ; preds = %while.cond7
%11 = load i32, ptr %i, align 4
%dec = add nsw i32 %11, -1
store i32 %dec, ptr %i, align 4
br label %while.cond5
}

; Function Attrs: convergent norecurse
define void @main.1() #2 {
entry:
call void @main()
ret void
}

; Function Attrs: convergent nocallback nofree nosync nounwind willreturn memory(none)
declare token @llvm.experimental.convergence.loop() #1

attributes #0 = { convergent noinline norecurse nounwind optnone "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
attributes #1 = { convergent nocallback nofree nosync nounwind willreturn memory(none) }
attributes #2 = { convergent norecurse "frame-pointer"="all" "hlsl.numthreads"="1,1,1" "hlsl.shader"="compute" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }

!llvm.module.flags = !{!0, !1, !2}


!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{i32 4, !"dx.disable_optimizations", i32 1}
!2 = !{i32 7, !"frame-pointer", i32 2}


157 changes: 157 additions & 0 deletions llvm/test/CodeGen/SPIRV/structurizer/cf.while.nested.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
; RUN: llc -mtriple=spirv-unknown-vulkan-compute -O0 %s -o - | FileCheck %s
; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-unknown-vulkan-compute %s -o - -filetype=obj | spirv-val %}

;
; [numthreads(1, 1, 1)]
; void main() {
; int val=0, i=0, j=0, k=0;
;
; while (i < 10) {
; val = val + i;
; while (j < 20) {
; while (k < 30) {
; val = val + k;
; ++k;
; }
;
; val = val * 2;
; ++j;
; }
;
; ++i;
; }
; }

; CHECK: %[[#func_12:]] = OpFunction %[[#void:]] DontInline %[[#]]
; CHECK: %[[#bb39:]] = OpLabel
; CHECK: OpBranch %[[#bb40:]]
; CHECK: %[[#bb40:]] = OpLabel
; CHECK: OpLoopMerge %[[#bb41:]] %[[#bb42:]] None
; CHECK: OpBranchConditional %[[#]] %[[#bb43:]] %[[#bb41:]]
; CHECK: %[[#bb43:]] = OpLabel
; CHECK: OpBranch %[[#bb44:]]
; CHECK: %[[#bb44:]] = OpLabel
; CHECK: OpLoopMerge %[[#bb45:]] %[[#bb46:]] None
; CHECK: OpBranchConditional %[[#]] %[[#bb47:]] %[[#bb45:]]
; CHECK: %[[#bb47:]] = OpLabel
; CHECK: OpLoopMerge %[[#bb48:]] %[[#bb49:]] None
; CHECK: OpBranchConditional %[[#]] %[[#bb49:]] %[[#bb48:]]
; CHECK: %[[#bb49:]] = OpLabel
; CHECK: OpBranch %[[#bb47:]]
; CHECK: %[[#bb48:]] = OpLabel
; CHECK: OpBranch %[[#bb46:]]
; CHECK: %[[#bb46:]] = OpLabel
; CHECK: OpBranch %[[#bb44:]]
; CHECK: %[[#bb45:]] = OpLabel
; CHECK: OpBranch %[[#bb42:]]
; CHECK: %[[#bb42:]] = OpLabel
; CHECK: OpBranch %[[#bb40:]]
; CHECK: %[[#bb41:]] = OpLabel
; CHECK: OpReturn
; CHECK: OpFunctionEnd
; CHECK: %[[#func_37:]] = OpFunction %[[#void:]] None %[[#]]
; CHECK: %[[#bb50:]] = OpLabel
; CHECK: OpReturn
; CHECK: OpFunctionEnd



target datalayout = "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-G1"
target triple = "spirv-unknown-vulkan1.3-compute"

; Function Attrs: convergent noinline norecurse nounwind optnone
define internal spir_func void @main() #0 {
entry:
%0 = call token @llvm.experimental.convergence.entry()
%val = alloca i32, align 4
%i = alloca i32, align 4
%j = alloca i32, align 4
%k = alloca i32, align 4
store i32 0, ptr %val, align 4
store i32 0, ptr %i, align 4
store i32 0, ptr %j, align 4
store i32 0, ptr %k, align 4
br label %while.cond

while.cond: ; preds = %while.end9, %entry
%1 = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token %0) ]
%2 = load i32, ptr %i, align 4
%cmp = icmp slt i32 %2, 10
br i1 %cmp, label %while.body, label %while.end11

while.body: ; preds = %while.cond
%3 = load i32, ptr %val, align 4
%4 = load i32, ptr %i, align 4
%add = add nsw i32 %3, %4
store i32 %add, ptr %val, align 4
br label %while.cond1

while.cond1: ; preds = %while.end, %while.body
%5 = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token %1) ]
%6 = load i32, ptr %j, align 4
%cmp2 = icmp slt i32 %6, 20
br i1 %cmp2, label %while.body3, label %while.end9

while.body3: ; preds = %while.cond1
br label %while.cond4

while.cond4: ; preds = %while.body6, %while.body3
%7 = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token %5) ]
%8 = load i32, ptr %k, align 4
%cmp5 = icmp slt i32 %8, 30
br i1 %cmp5, label %while.body6, label %while.end

while.body6: ; preds = %while.cond4
%9 = load i32, ptr %val, align 4
%10 = load i32, ptr %k, align 4
%add7 = add nsw i32 %9, %10
store i32 %add7, ptr %val, align 4
%11 = load i32, ptr %k, align 4
%inc = add nsw i32 %11, 1
store i32 %inc, ptr %k, align 4
br label %while.cond4

while.end: ; preds = %while.cond4
%12 = load i32, ptr %val, align 4
%mul = mul nsw i32 %12, 2
store i32 %mul, ptr %val, align 4
%13 = load i32, ptr %j, align 4
%inc8 = add nsw i32 %13, 1
store i32 %inc8, ptr %j, align 4
br label %while.cond1

while.end9: ; preds = %while.cond1
%14 = load i32, ptr %i, align 4
%inc10 = add nsw i32 %14, 1
store i32 %inc10, ptr %i, align 4
br label %while.cond

while.end11: ; preds = %while.cond
ret void
}

; Function Attrs: convergent norecurse
define void @main.1() #1 {
entry:
call void @main()
ret void
}

; Function Attrs: convergent nocallback nofree nosync nounwind willreturn memory(none)
declare token @llvm.experimental.convergence.entry() #2

; Function Attrs: convergent nocallback nofree nosync nounwind willreturn memory(none)
declare token @llvm.experimental.convergence.loop() #2

attributes #0 = { convergent noinline norecurse nounwind optnone "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
attributes #1 = { convergent norecurse "frame-pointer"="all" "hlsl.numthreads"="1,1,1" "hlsl.shader"="compute" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
attributes #2 = { convergent nocallback nofree nosync nounwind willreturn memory(none) }

!llvm.module.flags = !{!0, !1, !2}


!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{i32 4, !"dx.disable_optimizations", i32 1}
!2 = !{i32 7, !"frame-pointer", i32 2}


137 changes: 137 additions & 0 deletions llvm/test/CodeGen/SPIRV/structurizer/cf.while.plain.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
; RUN: llc -mtriple=spirv-unknown-vulkan-compute -O0 %s -o - | FileCheck %s
; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-unknown-vulkan-compute %s -o - -filetype=obj | spirv-val %}

;
; int foo() { return true; }
;
; [numthreads(1, 1, 1)]
; void main() {
; int val = 0;
; int i = 0;
;
; //////////////////////////
; //// Basic while loop ////
; //////////////////////////
; while (i < 10) {
; val = i;
; }
;
; //////////////////////////
; //// infinite loop ////
; //////////////////////////
; while (true) {
; val = 0;
; }
;
; //////////////////////////
; //// Null Body ////
; //////////////////////////
; while (val < 20)
; ;
;
; ////////////////////////////////////////////////////////////////
; //// Condition variable has VarDecl ////
; //// foo() returns an integer which must be cast to boolean ////
; ////////////////////////////////////////////////////////////////
; while (int a = foo()) {
; val = a;
; }
;
; }

; CHECK: %[[#func_11:]] = OpFunction %[[#uint:]] DontInline %[[#]]
; CHECK: %[[#bb20:]] = OpLabel
; CHECK: OpReturnValue %[[#]]
; CHECK: OpFunctionEnd
; CHECK: %[[#func_12:]] = OpFunction %[[#void:]] DontInline %[[#]]
; CHECK: %[[#bb21:]] = OpLabel
; CHECK: OpBranch %[[#bb22:]]
; CHECK: %[[#bb22:]] = OpLabel
; CHECK: OpLoopMerge %[[#bb23:]] %[[#bb24:]] None
; CHECK: OpBranchConditional %[[#]] %[[#bb24:]] %[[#bb23:]]
; CHECK: %[[#bb24:]] = OpLabel
; CHECK: OpBranch %[[#bb22:]]
; CHECK: %[[#bb23:]] = OpLabel
; CHECK: OpBranch %[[#bb25:]]
; CHECK: %[[#bb25:]] = OpLabel
; CHECK: OpLoopMerge %[[#bb26:]] %[[#bb27:]] None
; CHECK: OpBranchConditional %[[#]] %[[#bb26:]] %[[#bb27:]]
; CHECK: %[[#bb27:]] = OpLabel
; CHECK: OpBranch %[[#bb25:]]
; CHECK: %[[#bb26:]] = OpLabel
; CHECK: OpFunctionEnd
; CHECK: %[[#func_18:]] = OpFunction %[[#void:]] None %[[#]]
; CHECK: %[[#bb28:]] = OpLabel
; CHECK: OpReturn
; CHECK: OpFunctionEnd



target datalayout = "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-G1"
target triple = "spirv-unknown-vulkan1.3-compute"

; Function Attrs: convergent noinline norecurse nounwind optnone
define spir_func noundef i32 @_Z3foov() #0 {
entry:
%0 = call token @llvm.experimental.convergence.entry()
ret i32 1
}

; Function Attrs: convergent nocallback nofree nosync nounwind willreturn memory(none)
declare token @llvm.experimental.convergence.entry() #1

; Function Attrs: convergent noinline norecurse nounwind optnone
define internal spir_func void @main() #0 {
entry:
%0 = call token @llvm.experimental.convergence.entry()
%val = alloca i32, align 4
%i = alloca i32, align 4
store i32 0, ptr %val, align 4
store i32 0, ptr %i, align 4
br label %while.cond

while.cond: ; preds = %while.body, %entry
%1 = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token %0) ]
%2 = load i32, ptr %i, align 4
%cmp = icmp slt i32 %2, 10
br i1 %cmp, label %while.body, label %while.end

while.body: ; preds = %while.cond
%3 = load i32, ptr %i, align 4
store i32 %3, ptr %val, align 4
br label %while.cond

while.end: ; preds = %while.cond
br label %while.cond1

while.cond1: ; preds = %while.body2, %while.end
%4 = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token %0) ]
br label %while.body2

while.body2: ; preds = %while.cond1
store i32 0, ptr %val, align 4
br label %while.cond1
}

; Function Attrs: convergent norecurse
define void @main.1() #2 {
entry:
call void @main()
ret void
}

; Function Attrs: convergent nocallback nofree nosync nounwind willreturn memory(none)
declare token @llvm.experimental.convergence.loop() #1

attributes #0 = { convergent noinline norecurse nounwind optnone "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
attributes #1 = { convergent nocallback nofree nosync nounwind willreturn memory(none) }
attributes #2 = { convergent norecurse "frame-pointer"="all" "hlsl.numthreads"="1,1,1" "hlsl.shader"="compute" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }

!llvm.module.flags = !{!0, !1, !2}


!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{i32 4, !"dx.disable_optimizations", i32 1}
!2 = !{i32 7, !"frame-pointer", i32 2}


Loading