[ir][spirv-writer] Add index to IO output names

Avoids a bug in Qualcomm drivers that cannot handle duplicate names.

Change-Id: I8075098df45922d31e4c14d619eaf22786cecace
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/153460
Kokoro: Kokoro <noreply+kokoro@google.com>
Auto-Submit: James Price <jrprice@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
diff --git a/src/tint/lang/core/ir/disassembler.cc b/src/tint/lang/core/ir/disassembler.cc
index 6625466..08648f3 100644
--- a/src/tint/lang/core/ir/disassembler.cc
+++ b/src/tint/lang/core/ir/disassembler.cc
@@ -487,6 +487,9 @@
             if (v->Attributes().location.has_value()) {
                 out_ << " @location(" << v->Attributes().location.value() << ")";
             }
+            if (v->Attributes().index.has_value()) {
+                out_ << " @index(" << v->Attributes().index.value() << ")";
+            }
             if (v->Attributes().interpolation.has_value()) {
                 auto& interp = v->Attributes().interpolation.value();
                 out_ << " @interpolate(" << interp.type;
diff --git a/src/tint/lang/spirv/writer/function_test.cc b/src/tint/lang/spirv/writer/function_test.cc
index dcbb23c..cb84965 100644
--- a/src/tint/lang/spirv/writer/function_test.cc
+++ b/src/tint/lang/spirv/writer/function_test.cc
@@ -346,25 +346,26 @@
     });
 
     ASSERT_TRUE(Generate()) << Error() << output_;
-    EXPECT_INST(R"(OpEntryPoint Fragment %main "main" %main_loc0_Output %main_loc0_Output_0)");
+    EXPECT_INST(
+        R"(OpEntryPoint Fragment %main "main" %main_loc0_idx0_Output %main_loc0_idx1_Output)");
     EXPECT_INST(R"(
-               OpDecorate %main_loc0_Output Location 0
-               OpDecorate %main_loc0_Output Index 0
-               OpDecorate %main_loc0_Output_0 Location 0
-               OpDecorate %main_loc0_Output_0 Index 1
+               OpDecorate %main_loc0_idx0_Output Location 0
+               OpDecorate %main_loc0_idx0_Output Index 0
+               OpDecorate %main_loc0_idx1_Output Location 0
+               OpDecorate %main_loc0_idx1_Output Index 1
     )");
     EXPECT_INST(R"(
-%main_loc0_Output = OpVariable %_ptr_Output_float Output
-%main_loc0_Output_0 = OpVariable %_ptr_Output_float Output
+%main_loc0_idx0_Output = OpVariable %_ptr_Output_float Output
+%main_loc0_idx1_Output = OpVariable %_ptr_Output_float Output
     )");
     EXPECT_INST(R"(
        %main = OpFunction %void None %14
          %15 = OpLabel
          %16 = OpFunctionCall %Outputs %main_inner
          %17 = OpCompositeExtract %float %16 0
-               OpStore %main_loc0_Output %17
+               OpStore %main_loc0_idx0_Output %17
          %18 = OpCompositeExtract %float %16 1
-               OpStore %main_loc0_Output_0 %18
+               OpStore %main_loc0_idx1_Output %18
                OpReturn
                OpFunctionEnd
 )");
diff --git a/src/tint/lang/spirv/writer/raise/shader_io.cc b/src/tint/lang/spirv/writer/raise/shader_io.cc
index f807c31..2aa195e 100644
--- a/src/tint/lang/spirv/writer/raise/shader_io.cc
+++ b/src/tint/lang/spirv/writer/raise/shader_io.cc
@@ -83,6 +83,9 @@
                 }
             } else {
                 name << "_loc" << io.attributes.location.value();
+                if (io.attributes.index.has_value()) {
+                    name << "_idx" << io.attributes.index.value();
+                }
             }
             name << name_suffix;
 
diff --git a/src/tint/lang/spirv/writer/raise/shader_io_test.cc b/src/tint/lang/spirv/writer/raise/shader_io_test.cc
index 343f778..c37f9fb 100644
--- a/src/tint/lang/spirv/writer/raise/shader_io_test.cc
+++ b/src/tint/lang/spirv/writer/raise/shader_io_test.cc
@@ -547,6 +547,78 @@
     EXPECT_EQ(expect, str());
 }
 
+TEST_F(SpirvWriter_ShaderIOTest, ReturnValue_DualSourceBlending) {
+    auto* str_ty = ty.Struct(mod.symbols.New("Output"), {
+                                                            {
+                                                                mod.symbols.New("color1"),
+                                                                ty.f32(),
+                                                                {0u, 0u, {}, {}, false},
+                                                            },
+                                                            {
+                                                                mod.symbols.New("color2"),
+                                                                ty.f32(),
+                                                                {0u, 1u, {}, {}, false},
+                                                            },
+                                                        });
+
+    auto* ep = b.Function("foo", str_ty);
+    ep->SetStage(core::ir::Function::PipelineStage::kFragment);
+
+    b.Append(ep->Block(), [&] {  //
+        b.Return(ep, b.Construct(str_ty, 0.25_f, 0.75_f));
+    });
+
+    auto* src = R"(
+Output = struct @align(4) {
+  color1:f32 @offset(0), @location(0)
+  color2:f32 @offset(4), @location(0)
+}
+
+%foo = @fragment func():Output -> %b1 {
+  %b1 = block {
+    %2:Output = construct 0.25f, 0.75f
+    ret %2
+  }
+}
+)";
+    EXPECT_EQ(src, str());
+
+    auto* expect = R"(
+Output = struct @align(4) {
+  color1:f32 @offset(0)
+  color2:f32 @offset(4)
+}
+
+%b1 = block {  # root
+  %foo_loc0_idx0_Output:ptr<__out, f32, write> = var @location(0) @index(0)
+  %foo_loc0_idx1_Output:ptr<__out, f32, write> = var @location(0) @index(1)
+}
+
+%foo_inner = func():Output -> %b2 {
+  %b2 = block {
+    %4:Output = construct 0.25f, 0.75f
+    ret %4
+  }
+}
+%foo = @fragment func():void -> %b3 {
+  %b3 = block {
+    %6:Output = call %foo_inner
+    %7:f32 = access %6, 0u
+    store %foo_loc0_idx0_Output, %7
+    %8:f32 = access %6, 1u
+    store %foo_loc0_idx1_Output, %8
+    ret
+  }
+}
+)";
+
+    ShaderIOConfig config;
+    config.clamp_frag_depth = false;
+    Run(ShaderIO, config);
+
+    EXPECT_EQ(expect, str());
+}
+
 TEST_F(SpirvWriter_ShaderIOTest, Struct_SharedByVertexAndFragment) {
     auto* vec4f = ty.vec4<f32>();
     auto* str_ty = ty.Struct(mod.symbols.New("Interface"),