Skip to content

Commit 9654570

Browse files
authored
spirv-opt: Avoid duplicate decorations on unrolled loops. (KhronosGroup#6690)
Fixes issue: KhronosGroup#6689 Also adds a test to reproduce and validate the result. *This PR is sponsored by [Ludicon](https://ludicon.com)*
1 parent 252d7d5 commit 9654570

2 files changed

Lines changed: 102 additions & 1 deletion

File tree

source/opt/loop_unroller.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -577,8 +577,13 @@ void LoopUnrollerUtilsImpl::ReplaceInductionUseWithFinalValue(Loop* loop) {
577577

578578
for (size_t index = 0; index < inductions.size(); ++index) {
579579
// We don't want the decorations that applied to the induction variable
580-
// to be applied to the value that replace it.
580+
// to be applied to the value that replaces it. ReplaceAllUsesWith below
581+
// retargets the original induction phi's decorations onto its
582+
// replacement, which already carries decorations cloned from the loop
583+
// body Drop the decorations of both the final-iteration phi copy and the
584+
// original induction phi before the replacement happens.
581585
context_->KillNamesAndDecorates(state_.previous_phis_[index]);
586+
context_->KillNamesAndDecorates(inductions[index]);
582587

583588
uint32_t trip_step_id = GetPhiDefID(state_.previous_phis_[index],
584589
state_.previous_latch_block_->id());

test/opt/loop_optimizations/unroll_simple.cpp

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3939,6 +3939,102 @@ TEST_F(PassClassTest, ApplyDecorationsToClonedInstructions) {
39393939
SinglePassRunAndMatch<LoopUnroller>(text, true);
39403940
}
39413941

3942+
// When the loop-carried induction phi is itself decorated, unrolling used to
3943+
// transfer that decoration (via ReplaceAllUsesWith) onto the value that
3944+
// replaces the phi, the final iteration's body value which already
3945+
// carries its own cloned decoration. The result was an id decorated
3946+
// RelaxedPrecision twice, which fails validation. The induction phi's
3947+
// decorations must be dropped before the replacement. Running with
3948+
// validation enabled is what catches the regression.
3949+
//
3950+
// The SPIR-V below is the following GLSL compiled with glslang, then run
3951+
// through `--eliminate-local-multi-store --ssa-rewrite`, that is what
3952+
// `-O` hands to the loop unroller:
3953+
//
3954+
// #version 450
3955+
// #extension GL_EXT_control_flow_attributes : require
3956+
// layout(local_size_x = 1) in;
3957+
// layout(std430, binding = 0) buffer Buf { float data[]; } buf;
3958+
// void main() {
3959+
// mediump float acc = 0.0;
3960+
// [[unroll]] for (uint i = 0u; i < 4u; ++i) {
3961+
// mediump float x = buf.data[i]; // mediump local
3962+
// acc += x; // mediump + mediump -> mediump
3963+
// }
3964+
// buf.data[0] = acc;
3965+
// }
3966+
//
3967+
// The essential pattern: an unrolled loop whose mediump/min16 accumulator is
3968+
// loop-carried, with an update expression that is also mediump, so both the
3969+
// accumulator phi and the loop-body result end up RelaxedPrecision-decorated.
3970+
TEST_F(PassClassTest, DoNotDuplicateDecorationsOnLoopCarriedValue) {
3971+
const std::string text = R"(
3972+
; The loop has one RelaxedPrecision-decorated body value and is unrolled
3973+
; four times, so the result must carry exactly four such decorations. A
3974+
; fifth -- the induction phi's decoration leaking onto its replacement --
3975+
; is the regression, and also fails the enabled validation.
3976+
; CHECK: OpDecorate {{%\w+}} RelaxedPrecision
3977+
; CHECK-NEXT: OpDecorate {{%\w+}} RelaxedPrecision
3978+
; CHECK-NEXT: OpDecorate {{%\w+}} RelaxedPrecision
3979+
; CHECK-NEXT: OpDecorate {{%\w+}} RelaxedPrecision
3980+
; CHECK-NOT: RelaxedPrecision
3981+
OpCapability Shader
3982+
%1 = OpExtInstImport "GLSL.std.450"
3983+
OpMemoryModel Logical GLSL450
3984+
OpEntryPoint GLCompute %main "main"
3985+
OpExecutionMode %main LocalSize 1 1 1
3986+
OpDecorate %rt_arr ArrayStride 4
3987+
OpDecorate %buf_struct Block
3988+
OpMemberDecorate %buf_struct 0 Offset 0
3989+
OpDecorate %buf Binding 0
3990+
OpDecorate %buf DescriptorSet 0
3991+
OpDecorate %add RelaxedPrecision
3992+
OpDecorate %acc_phi RelaxedPrecision
3993+
%void = OpTypeVoid
3994+
%fn_type = OpTypeFunction %void
3995+
%float = OpTypeFloat 32
3996+
%float_0 = OpConstant %float 0
3997+
%uint = OpTypeInt 32 0
3998+
%uint_0 = OpConstant %uint 0
3999+
%uint_4 = OpConstant %uint 4
4000+
%bool = OpTypeBool
4001+
%rt_arr = OpTypeRuntimeArray %float
4002+
%buf_struct = OpTypeStruct %rt_arr
4003+
%buf_ptr = OpTypePointer StorageBuffer %buf_struct
4004+
%buf = OpVariable %buf_ptr StorageBuffer
4005+
%int = OpTypeInt 32 1
4006+
%int_0 = OpConstant %int 0
4007+
%float_ptr = OpTypePointer StorageBuffer %float
4008+
%int_1 = OpConstant %int 1
4009+
%main = OpFunction %void None %fn_type
4010+
%entry = OpLabel
4011+
OpBranch %header
4012+
%header = OpLabel
4013+
%acc_phi = OpPhi %float %float_0 %entry %add %latch
4014+
%i_phi = OpPhi %uint %uint_0 %entry %i_next %latch
4015+
OpLoopMerge %merge %latch Unroll
4016+
OpBranch %cond_blk
4017+
%cond_blk = OpLabel
4018+
%cond = OpULessThan %bool %i_phi %uint_4
4019+
OpBranchConditional %cond %body %merge
4020+
%body = OpLabel
4021+
%elem_p = OpAccessChain %float_ptr %buf %int_0 %i_phi
4022+
%elem = OpLoad %float %elem_p
4023+
%add = OpFAdd %float %acc_phi %elem
4024+
OpBranch %latch
4025+
%latch = OpLabel
4026+
%i_next = OpIAdd %uint %i_phi %int_1
4027+
OpBranch %header
4028+
%merge = OpLabel
4029+
%store_p = OpAccessChain %float_ptr %buf %int_0 %int_0
4030+
OpStore %store_p %acc_phi
4031+
OpReturn
4032+
OpFunctionEnd
4033+
)";
4034+
SetTargetEnv(SPV_ENV_UNIVERSAL_1_3);
4035+
SinglePassRunAndMatch<LoopUnroller>(text, true);
4036+
}
4037+
39424038
} // namespace
39434039
} // namespace opt
39444040
} // namespace spvtools

0 commit comments

Comments
 (0)