KhronosGroup / KhronosGroup/SPIRV-Tools
optimizer pulls loop header into first block of function
- Dominant language
- C++
- Stars
- 1.4k
- Forks
- 709
- Avg merge
- 1d 22h
- Merged PRs (30d)
- 28
Description
SPIR-V requires that the first block of a function must not be the target of a branch.
But this GLSL code optimizes such that the first block of the function is a loop header.
This may have the same root cause as #4630, where backedges are not identified when they should be.
GLSL:
```
#version 450
layout(set=0,binding=0) buffer A {uint a; } buf;
int foo() {
while(true) {
buf.a++;
}
return 1;
}
void main()
{
for ( ; ; foo() ) {
}
}
```
$ glslc a.comp -o a.spv
Produces:
```
; SPIR-V
; Version: 1.0
; Generator: Google Shaderc over Glslang; 10
; Bound: 34
; Schema: 0
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionMode %main LocalSize 1 1 1
OpSource GLSL 450
OpSourceExtension "GL_GOOGLE_cpp_style_line_directive"
OpSourceExtension "GL_GOOGLE_include_directive"
OpName %main "main"
OpName %foo_ "foo("
OpName %A "A"
OpMemberName %A 0 "a"
OpName %buf "buf"
OpMemberDecorate %A 0 Offset 0
OpDecorate %A BufferBlock
OpDecorate %buf DescriptorSet 0
OpDecorate %buf Binding 0
%void = OpTypeVoid
%3 = OpTypeFunction %void
%int = OpTypeInt 32 1
%7 = OpTypeFunction %int
%bool = OpTypeBool
%true = OpConstantTrue %bool
%uint = OpTypeInt 32 0
%A = OpTypeStruct %uint
%_ptr_Uniform_A = OpTypePointer Uniform %A
%buf = OpVariable %_ptr_Uniform_A Uniform
%int_0 = OpConstant %int 0
%_ptr_Uniform_uint = OpTypePointer Uniform %uint
%int_1 = OpConstant %int 1
%main = OpFunction %void None %3
%5 = OpLabel
OpBranch %29
%29 = OpLabel
OpLoopMerge %31 %32 None
OpBranch %30
%30 = OpLabel
OpBranch %32
%32 = OpLabel
%33 = OpFunctionCall %int %foo_
OpBranch %29
%31 = OpLabel
OpUnreachable
OpFunctionEnd
%foo_ = OpFunction %int None %7
%9 = OpLabel
OpBranch %10
%10 = OpLabel
OpLoopMerge %12 %13 None
OpBranch %14
%14 = OpLabel
OpBranchConditional %true %11 %12
%11 = OpLabel
%23 = OpAccessChain %_ptr_Uniform_uint %buf %int_0
%24 = OpLoad %uint %23
%26 = OpIAdd %uint %24 %int_1
OpStore %23 %26
OpBranch %13
%13 = OpLabel
OpBranch %10
%12 = OpLabel
OpReturnValue %int_1
OpFunctionEnd
```
But then optimizing:
$ spirv-opt -O a.spv -o b.spv
produces:
```
; SPIR-V
; Version: 1.0
; Generator: Google Shaderc over Glslang; 10
; Bound: 45
; Schema: 0
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionMode %main LocalSize 1 1 1
OpSource GLSL 450
OpSourceExtension "GL_GOOGLE_cpp_style_line_directive"
OpSourceExtension "GL_GOOGLE_include_directive"
OpName %main "main"
OpName %A "A"
OpMemberName %A 0 "a"
OpName %buf "buf"
OpMemberDecorate %A 0 Offset 0
OpDecorate %A BufferBlock
OpDecorate %buf DescriptorSet 0
OpDecorate %buf Binding 0
%void = OpTypeVoid
%3 = OpTypeFunction %void
%int = OpTypeInt 32 1
%uint = OpTypeInt 32 0
%A = OpTypeStruct %uint
%_ptr_Uniform_A = OpTypePointer Uniform %A
%buf = OpVariable %_ptr_Uniform_A Uniform
%int_0 = OpConstant %int 0
%_ptr_Uniform_uint = OpTypePointer Uniform %uint
%int_1 = OpConstant %int 1
%main = OpFunction %void None %3
%5 = OpLabel
OpLoopMerge %31 %5 None
OpBranch %37
%37 = OpLabel
%40 = OpAccessChain %_ptr_Uniform_uint %buf %int_0
%41 = OpLoad %uint %40
%42 = OpIAdd %uint %41 %int_1
OpStore %40 %42
OpLoopMerge %44 %37 None
OpBranch %37
%44 = OpLabel
OpUnreachable
%31 = OpLabel
OpUnreachable
OpFunctionEnd
```
with this CFG:

Note that block %5 is the first block of the function and is also a loop header: it has an OpLoopMerge instruction in it.
I'd wait until #4630 is resolved before tackling this.
Contributor guide
Assessment
This issue has not been assessed yet.