@@ -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