spirv-opt: Clone decorations when unrolling loops (#6373)

When unrolling a loop, the instructions in the body of the loop are
cloned.
If an instruction has a decoration, the decoration must be cloned and
applied to the new instruction.
This change ensures that the decorations are cloned.

Fixes #5902
diff --git a/source/opt/loop_unroller.cpp b/source/opt/loop_unroller.cpp
index 468b2ae..8765b16 100644
--- a/source/opt/loop_unroller.cpp
+++ b/source/opt/loop_unroller.cpp
@@ -938,6 +938,11 @@
     inst.SetResultId(new_id);
     def_use_mgr->AnalyzeInstDef(&inst);
 
+    // All decorations that can apply to an instruction in a function body
+    // modify the behaviour of the instruction, and should be on the
+    // new instruction to keep the same results.
+    context_->get_decoration_mgr()->CloneDecorations(old_id, new_id);
+
     // Save the mapping of old_id -> new_id.
     state_.new_inst[old_id] = inst.result_id();
     // Check if this instruction is the induction variable.
diff --git a/test/opt/loop_optimizations/unroll_simple.cpp b/test/opt/loop_optimizations/unroll_simple.cpp
index 6214902..256b487 100644
--- a/test/opt/loop_optimizations/unroll_simple.cpp
+++ b/test/opt/loop_optimizations/unroll_simple.cpp
@@ -3864,6 +3864,81 @@
   SinglePassRunAndCheck<LoopUnroller>(text, text, false);
 }
 
+TEST_F(PassClassTest, ApplyDecorationsToClonedInstructions) {
+  const std::string text = R"(
+  ; CHECK: OpDecorate [[ld1:%\w+]] RelaxedPrecision
+  ; CHECK: OpDecorate [[mul1:%\w+]] RelaxedPrecision
+  ; CHECK: OpDecorate [[add1:%\w+]] RelaxedPrecision
+  ; CHECK: OpDecorate [[ld2:%\w+]] RelaxedPrecision
+  ; CHECK: OpDecorate [[mul2:%\w+]] RelaxedPrecision
+  ; CHECK: OpDecorate [[add2:%\w+]] RelaxedPrecision
+  ; CHECK: OpDecorate [[ld3:%\w+]] RelaxedPrecision
+  ; CHECK: OpDecorate [[mul3:%\w+]] RelaxedPrecision
+  ; CHECK: OpDecorate [[add3:%\w+]] RelaxedPrecision
+  
+; CHECK: [[ld1]] = OpLoad %float
+; CHECK: [[mul1]] = OpFMul %float
+; CHECK: [[add1]] = OpFAdd %float
+; CHECK: [[ld2]] = OpLoad %float
+; CHECK: [[mul2]] = OpFMul %float
+; CHECK: [[add2]] = OpFAdd %float
+; CHECK: [[ld3]] = OpLoad %float
+; CHECK: [[mul3]] = OpFMul %float
+; CHECK: [[add3]] = OpFAdd %float
+
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %1 "main" %2
+               OpExecutionMode %1 LocalSize 1 1 1
+               OpDecorate %2 DescriptorSet 0
+               OpDecorate %2 Binding 0
+               OpDecorate %_runtimearr_float ArrayStride 4
+               OpMemberDecorate %_struct_4 0 Offset 0
+               OpDecorate %_struct_4 Block
+               OpDecorate %5 RelaxedPrecision
+               OpDecorate %6 RelaxedPrecision
+               OpDecorate %7 RelaxedPrecision
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+      %int_3 = OpConstant %int 3
+      %int_1 = OpConstant %int 1
+      %float = OpTypeFloat 32
+%_runtimearr_float = OpTypeRuntimeArray %float
+  %_struct_4 = OpTypeStruct %_runtimearr_float
+%_ptr_StorageBuffer__struct_4 = OpTypePointer StorageBuffer %_struct_4
+       %uint = OpTypeInt 32 0
+       %void = OpTypeVoid
+         %16 = OpTypeFunction %void
+       %bool = OpTypeBool
+%_ptr_StorageBuffer_float = OpTypePointer StorageBuffer %float
+          %2 = OpVariable %_ptr_StorageBuffer__struct_4 StorageBuffer
+          %1 = OpFunction %void None %16
+         %19 = OpLabel
+               OpBranch %20
+         %20 = OpLabel
+         %21 = OpPhi %int %int_0 %19 %22 %23
+         %24 = OpSLessThan %bool %21 %int_3
+               OpLoopMerge %25 %23 Unroll
+               OpBranchConditional %24 %26 %25
+         %26 = OpLabel
+         %27 = OpBitcast %uint %21
+         %28 = OpAccessChain %_ptr_StorageBuffer_float %2 %int_0 %27
+          %5 = OpLoad %float %28
+          %6 = OpFMul %float %5 %5
+          %7 = OpFAdd %float %5 %6
+               OpStore %28 %7
+               OpBranch %23
+         %23 = OpLabel
+         %22 = OpIAdd %int %21 %int_1
+               OpBranch %20
+         %25 = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+  SetTargetEnv(SPV_ENV_UNIVERSAL_1_6);
+  SinglePassRunAndMatch<LoopUnroller>(text, true);
+}
+
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools