tint/writer: Replace scope_stack_, fix type ctor scoping.

ScopeStack is not needed here - the resolver already provides variable scoping with the sem::Variables.

Re-purpose scope_stack_ for a stack of Scope, which now holds the type constructor -> SPIR-V ID map.
This map needs to be per-scope, to fix issues like crbug.com/tint/1520

Fixed: tint:1520
Change-Id: Ifa7749338abf63652a1369e76cf5400be1c37298
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/88301
Reviewed-by: Dan Sinclair <dsinclair@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
diff --git a/src/tint/writer/spirv/builder.cc b/src/tint/writer/spirv/builder.cc
index cf98b5d..5bc2686 100644
--- a/src/tint/writer/spirv/builder.cc
+++ b/src/tint/writer/spirv/builder.cc
@@ -245,7 +245,7 @@
 
 Builder::Builder(const Program* program, bool zero_initialize_workgroup_memory)
     : builder_(ProgramBuilder::Wrap(program)),
-      scope_stack_({}),
+      scope_stack_{Scope{}},
       zero_initialize_workgroup_memory_(zero_initialize_workgroup_memory) {}
 
 Builder::~Builder() = default;
@@ -279,6 +279,30 @@
   return true;
 }
 
+void Builder::RegisterVariable(const sem::Variable* var, uint32_t id) {
+  var_to_id_.emplace(var, id);
+  id_to_var_.emplace(id, var);
+}
+
+uint32_t Builder::LookupVariableID(const sem::Variable* var) {
+  auto it = var_to_id_.find(var);
+  if (it == var_to_id_.end()) {
+    error_ = "unable to find ID for variable: " +
+             builder_.Symbols().NameFor(var->Declaration()->symbol);
+    return 0;
+  }
+  return it->second;
+}
+
+void Builder::PushScope() {
+  // Push a new scope, by copying the top-most stack
+  scope_stack_.push_back(scope_stack_.back());
+}
+
+void Builder::PopScope() {
+  scope_stack_.pop_back();
+}
+
 Operand Builder::result_op() {
   return Operand::Int(next_id());
 }
@@ -441,7 +465,7 @@
       continue;
     }
 
-    uint32_t var_id = scope_stack_.Get(var->Declaration()->symbol);
+    uint32_t var_id = LookupVariableID(var);
     if (var_id == 0) {
       error_ = "unable to find ID for global variable: " +
                builder_.Symbols().NameFor(var->Declaration()->symbol);
@@ -588,8 +612,8 @@
     return false;
   }
 
-  scope_stack_.Push();
-  TINT_DEFER(scope_stack_.Pop());
+  PushScope();
+  TINT_DEFER(PopScope());
 
   auto definition_inst = Instruction{
       spv::Op::OpFunction,
@@ -612,7 +636,7 @@
     params.push_back(Instruction{spv::Op::OpFunctionParameter,
                                  {Operand::Int(param_type_id), param_op}});
 
-    scope_stack_.Set(param->Declaration()->symbol, param_id);
+    RegisterVariable(param, param_id);
   }
 
   push_function(Function{definition_inst, result_op(), std::move(params)});
@@ -680,20 +704,21 @@
     }
   }
 
+  auto* sem = builder_.Sem().Get(var);
+
   if (var->is_const) {
     if (!var->constructor) {
       error_ = "missing constructor for constant";
       return false;
     }
-    scope_stack_.Set(var->symbol, init_id);
-    spirv_id_to_variable_[init_id] = var;
+    RegisterVariable(sem, init_id);
     return true;
   }
 
   auto result = result_op();
   auto var_id = result.to_i();
   auto sc = ast::StorageClass::kFunction;
-  auto* type = builder_.Sem().Get(var)->Type();
+  auto* type = sem->Type();
   auto type_id = GenerateTypeIfNeeded(type);
   if (type_id == 0) {
     return false;
@@ -719,8 +744,7 @@
     }
   }
 
-  scope_stack_.Set(var->symbol, var_id);
-  spirv_id_to_variable_[var_id] = var;
+  RegisterVariable(sem, var_id);
 
   return true;
 }
@@ -775,8 +799,7 @@
                {Operand::Int(init_id),
                 Operand::String(builder_.Symbols().NameFor(var->symbol))});
 
-    scope_stack_.Set(var->symbol, init_id);
-    spirv_id_to_variable_[init_id] = var;
+    RegisterVariable(sem, init_id);
     return true;
   }
 
@@ -898,8 +921,7 @@
     }
   }
 
-  scope_stack_.Set(var->symbol, var_id);
-  spirv_id_to_variable_[var_id] = var;
+  RegisterVariable(sem, var_id);
   return true;
 }
 
@@ -1174,12 +1196,13 @@
 
 uint32_t Builder::GenerateIdentifierExpression(
     const ast::IdentifierExpression* expr) {
-  uint32_t val = scope_stack_.Get(expr->symbol);
-  if (val == 0) {
-    error_ = "unable to find variable with identifier: " +
-             builder_.Symbols().NameFor(expr->symbol);
+  auto* sem = builder_.Sem().Get(expr);
+  if (auto* user = sem->As<sem::VariableUser>()) {
+    return LookupVariableID(user->Variable());
   }
-  return val;
+  error_ = "identifier '" + builder_.Symbols().NameFor(expr->symbol) +
+           "' does not resolve to a variable";
+  return 0;
 }
 
 uint32_t Builder::GenerateExpressionWithLoadIfNeeded(
@@ -1479,8 +1502,12 @@
     }
   }
 
+  auto& stack = (result_is_spec_composite || result_is_constant_composite)
+                    ? scope_stack_[0]       // Global scope
+                    : scope_stack_.back();  // Lexical scope
+
   return utils::GetOrCreate(
-      type_constructor_to_id_, OperandListKey{ops}, [&]() -> uint32_t {
+      stack.type_ctor_to_id_, OperandListKey{ops}, [&]() -> uint32_t {
         auto result = result_op();
         ops[kOpsResultIdx] = result;
 
@@ -2193,8 +2220,8 @@
 }
 
 bool Builder::GenerateBlockStatement(const ast::BlockStatement* stmt) {
-  scope_stack_.Push();
-  TINT_DEFER(scope_stack_.Pop());
+  PushScope();
+  TINT_DEFER(PopScope());
   return GenerateBlockStatementWithoutScoping(stmt);
 }
 
@@ -3653,8 +3680,8 @@
   // We need variables from the body to be visible in the continuing block, so
   // manage scope outside of GenerateBlockStatement.
   {
-    scope_stack_.Push();
-    TINT_DEFER(scope_stack_.Pop());
+    PushScope();
+    TINT_DEFER(PopScope());
 
     if (!GenerateBlockStatementWithoutScoping(stmt->body)) {
       return false;
diff --git a/src/tint/writer/spirv/builder.h b/src/tint/writer/spirv/builder.h
index cf07e27..cb3ba30 100644
--- a/src/tint/writer/spirv/builder.h
+++ b/src/tint/writer/spirv/builder.h
@@ -584,6 +584,22 @@
   uint32_t GenerateConstantVectorSplatIfNeeded(const sem::Vector* type,
                                                uint32_t value_id);
 
+  /// Registers the semantic variable to the given SPIR-V ID
+  /// @param var the semantic variable
+  /// @param id the generated SPIR-V identifier for the variable
+  void RegisterVariable(const sem::Variable* var, uint32_t id);
+
+  /// Looks up the SPIR-V ID for the variable, which must have been registered
+  /// with a call to RegisterVariable()
+  /// @returns the SPIR-V ID, or 0 if the variable was not found
+  uint32_t LookupVariableID(const sem::Variable* var);
+
+  /// Pushes a new scope
+  void PushScope();
+
+  /// Pops the top-most scope
+  void PopScope();
+
   ProgramBuilder builder_;
   std::string error_;
   uint32_t next_id_ = 1;
@@ -599,18 +615,23 @@
   InstructionList annotations_;
   std::vector<Function> functions_;
 
+  // Scope holds per-block information
+  struct Scope {
+    std::unordered_map<OperandListKey, uint32_t> type_ctor_to_id_;
+  };
+
+  std::unordered_map<const sem::Variable*, uint32_t> var_to_id_;
+  std::unordered_map<uint32_t, const sem::Variable*> id_to_var_;
   std::unordered_map<std::string, uint32_t> import_name_to_id_;
   std::unordered_map<Symbol, uint32_t> func_symbol_to_id_;
   std::unordered_map<sem::CallTargetSignature, uint32_t> func_sig_to_id_;
   std::unordered_map<const sem::Type*, uint32_t> type_to_id_;
   std::unordered_map<ScalarConstant, uint32_t> const_to_id_;
-  std::unordered_map<OperandListKey, uint32_t> type_constructor_to_id_;
   std::unordered_map<const sem::Type*, uint32_t> const_null_to_id_;
   std::unordered_map<uint64_t, uint32_t> const_splat_to_id_;
   std::unordered_map<const sem::Type*, uint32_t>
       texture_type_to_sampled_image_type_id_;
-  ScopeStack<uint32_t> scope_stack_;
-  std::unordered_map<uint32_t, const ast::Variable*> spirv_id_to_variable_;
+  std::vector<Scope> scope_stack_;
   std::vector<uint32_t> merge_stack_;
   std::vector<uint32_t> continue_stack_;
   std::unordered_set<uint32_t> capability_set_;
diff --git a/src/tint/writer/spirv/builder_constructor_expression_test.cc b/src/tint/writer/spirv/builder_constructor_expression_test.cc
index 495f1c4..ae56e5d 100644
--- a/src/tint/writer/spirv/builder_constructor_expression_test.cc
+++ b/src/tint/writer/spirv/builder_constructor_expression_test.cc
@@ -1850,5 +1850,99 @@
   EXPECT_FALSE(b.has_error());
 }
 
+TEST_F(SpvBuilderConstructorTest, ConstantCompositeScoping) {
+  // if (true) {
+  //    let x = vec3<f32>(1.0, 2.0, 3.0);
+  // }
+  // let y = vec3<f32>(1.0, 2.0, 3.0); // Reuses the ID 'x'
+
+  WrapInFunction(
+      If(true, Block(Decl(Let("x", nullptr, vec3<f32>(1.f, 2.f, 3.f))))),
+      Decl(Let("y", nullptr, vec3<f32>(1.f, 2.f, 3.f))));
+
+  spirv::Builder& b = SanitizeAndBuild();
+  ASSERT_TRUE(b.Build());
+
+  EXPECT_EQ(DumpBuilder(b), R"(OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %3 "test_function"
+OpExecutionMode %3 LocalSize 1 1 1
+OpName %3 "test_function"
+%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%5 = OpTypeBool
+%6 = OpConstantTrue %5
+%10 = OpTypeFloat 32
+%9 = OpTypeVector %10 3
+%11 = OpConstant %10 1
+%12 = OpConstant %10 2
+%13 = OpConstant %10 3
+%14 = OpConstantComposite %9 %11 %12 %13
+%3 = OpFunction %2 None %1
+%4 = OpLabel
+OpSelectionMerge %7 None
+OpBranchConditional %6 %8 %7
+%8 = OpLabel
+OpBranch %7
+%7 = OpLabel
+OpReturn
+OpFunctionEnd
+)");
+  Validate(b);
+}
+
+// TODO(crbug.com/tint/1155) Implement when overrides are fully implemented.
+// TEST_F(SpvBuilderConstructorTest, SpecConstantCompositeScoping)
+
+TEST_F(SpvBuilderConstructorTest, CompositeConstructScoping) {
+  // var one = 1.0;
+  // if (true) {
+  //    let x = vec3<f32>(one, 2.0, 3.0);
+  // }
+  // let y = vec3<f32>(one, 2.0, 3.0); // Mustn't reuse the ID 'x'
+
+  WrapInFunction(
+      Decl(Var("one", nullptr, Expr(1.f))),
+      If(true, Block(Decl(Let("x", nullptr, vec3<f32>("one", 2.f, 3.f))))),
+      Decl(Let("y", nullptr, vec3<f32>("one", 2.f, 3.f))));
+
+  spirv::Builder& b = SanitizeAndBuild();
+  ASSERT_TRUE(b.Build());
+
+  EXPECT_EQ(DumpBuilder(b), R"(OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %3 "test_function"
+OpExecutionMode %3 LocalSize 1 1 1
+OpName %3 "test_function"
+OpName %7 "one"
+%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%5 = OpTypeFloat 32
+%6 = OpConstant %5 1
+%8 = OpTypePointer Function %5
+%9 = OpConstantNull %5
+%10 = OpTypeBool
+%11 = OpConstantTrue %10
+%14 = OpTypeVector %5 3
+%16 = OpConstant %5 2
+%17 = OpConstant %5 3
+%3 = OpFunction %2 None %1
+%4 = OpLabel
+%7 = OpVariable %8 Function %9
+OpStore %7 %6
+OpSelectionMerge %12 None
+OpBranchConditional %11 %13 %12
+%13 = OpLabel
+%15 = OpLoad %5 %7
+%18 = OpCompositeConstruct %14 %15 %16 %17
+OpBranch %12
+%12 = OpLabel
+%19 = OpLoad %5 %7
+%20 = OpCompositeConstruct %14 %19 %16 %17
+OpReturn
+OpFunctionEnd
+)");
+  Validate(b);
+}
 }  // namespace
 }  // namespace tint::writer::spirv
diff --git a/test/tint/bug/tint/1520.spvasm b/test/tint/bug/tint/1520.spvasm
new file mode 100644
index 0000000..a9071c2
--- /dev/null
+++ b/test/tint/bug/tint/1520.spvasm
@@ -0,0 +1,248 @@
+OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %sk_FragColor %sk_Clockwise %vcolor_S0
+OpExecutionMode %main OriginUpperLeft
+OpName %UniformBuffer "UniformBuffer"
+OpMemberName %UniformBuffer 0 "unknownInput_S1_c0"
+OpMemberName %UniformBuffer 1 "ucolorRed_S1_c0"
+OpMemberName %UniformBuffer 2 "ucolorGreen_S1_c0"
+OpMemberName %UniformBuffer 3 "umatrix_S1"
+OpName %sk_FragColor "sk_FragColor"
+OpName %sk_Clockwise "sk_Clockwise"
+OpName %vcolor_S0 "vcolor_S0"
+OpName %test_int_S1_c0_b "test_int_S1_c0_b"
+OpName %unknown "unknown"
+OpName %ok "ok"
+OpName %val "val"
+OpName %main "main"
+OpName %outputColor_S0 "outputColor_S0"
+OpName %output_S1 "output_S1"
+OpName %_8_unknown "_8_unknown"
+OpName %_9_ok "_9_ok"
+OpName %_10_val "_10_val"
+OpMemberDecorate %UniformBuffer 0 Offset 16
+OpMemberDecorate %UniformBuffer 0 RelaxedPrecision
+OpMemberDecorate %UniformBuffer 1 Offset 32
+OpMemberDecorate %UniformBuffer 1 RelaxedPrecision
+OpMemberDecorate %UniformBuffer 2 Offset 48
+OpMemberDecorate %UniformBuffer 2 RelaxedPrecision
+OpMemberDecorate %UniformBuffer 3 Offset 64
+OpMemberDecorate %UniformBuffer 3 ColMajor
+OpMemberDecorate %UniformBuffer 3 MatrixStride 16
+OpDecorate %UniformBuffer Block
+OpDecorate %4 Binding 0
+OpDecorate %4 DescriptorSet 0
+OpDecorate %sk_FragColor RelaxedPrecision
+OpDecorate %sk_FragColor Location 0
+OpDecorate %sk_FragColor Index 0
+OpDecorate %sk_Clockwise BuiltIn FrontFacing
+OpDecorate %vcolor_S0 RelaxedPrecision
+OpDecorate %vcolor_S0 Location 0
+OpDecorate %26 RelaxedPrecision
+OpDecorate %outputColor_S0 RelaxedPrecision
+OpDecorate %72 RelaxedPrecision
+OpDecorate %output_S1 RelaxedPrecision
+OpDecorate %_8_unknown RelaxedPrecision
+OpDecorate %77 RelaxedPrecision
+OpDecorate %83 RelaxedPrecision
+OpDecorate %84 RelaxedPrecision
+OpDecorate %_10_val RelaxedPrecision
+OpDecorate %89 RelaxedPrecision
+OpDecorate %92 RelaxedPrecision
+OpDecorate %93 RelaxedPrecision
+OpDecorate %94 RelaxedPrecision
+OpDecorate %95 RelaxedPrecision
+OpDecorate %103 RelaxedPrecision
+OpDecorate %104 RelaxedPrecision
+OpDecorate %105 RelaxedPrecision
+OpDecorate %106 RelaxedPrecision
+OpDecorate %122 RelaxedPrecision
+OpDecorate %124 RelaxedPrecision
+OpDecorate %125 RelaxedPrecision
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%v3float = OpTypeVector %float 3
+%mat3v3float = OpTypeMatrix %v3float 3
+%UniformBuffer = OpTypeStruct %float %v4float %v4float %mat3v3float
+%_ptr_Uniform_UniformBuffer = OpTypePointer Uniform %UniformBuffer
+%4 = OpVariable %_ptr_Uniform_UniformBuffer Uniform
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%sk_FragColor = OpVariable %_ptr_Output_v4float Output
+%bool = OpTypeBool
+%_ptr_Input_bool = OpTypePointer Input %bool
+%sk_Clockwise = OpVariable %_ptr_Input_bool Input
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%vcolor_S0 = OpVariable %_ptr_Input_v4float Input
+%18 = OpTypeFunction %bool
+%int = OpTypeInt 32 1
+%_ptr_Function_int = OpTypePointer Function %int
+%int_0 = OpConstant %int 0
+%_ptr_Uniform_float = OpTypePointer Uniform %float
+%_ptr_Function_bool = OpTypePointer Function %bool
+%true = OpConstantTrue %bool
+%false = OpConstantFalse %bool
+%v4int = OpTypeVector %int 4
+%35 = OpConstantComposite %v4int %int_0 %int_0 %int_0 %int_0
+%v4bool = OpTypeVector %bool 4
+%_ptr_Function_v4int = OpTypePointer Function %v4int
+%int_1 = OpConstant %int 1
+%46 = OpConstantComposite %v4int %int_1 %int_1 %int_1 %int_1
+%int_2 = OpConstant %int 2
+%57 = OpConstantComposite %v4int %int_2 %int_2 %int_2 %int_2
+%void = OpTypeVoid
+%68 = OpTypeFunction %void
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%_ptr_Function_float = OpTypePointer Function %float
+%float_0 = OpConstant %float 0
+%82 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
+%float_1 = OpConstant %float 1
+%91 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1
+%float_2 = OpConstant %float 2
+%102 = OpConstantComposite %v4float %float_2 %float_2 %float_2 %float_2
+%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float
+%test_int_S1_c0_b = OpFunction %bool None %18
+%19 = OpLabel
+%unknown = OpVariable %_ptr_Function_int Function
+%ok = OpVariable %_ptr_Function_bool Function
+%val = OpVariable %_ptr_Function_v4int Function
+%24 = OpAccessChain %_ptr_Uniform_float %4 %int_0
+%26 = OpLoad %float %24
+%27 = OpConvertFToS %int %26
+OpStore %unknown %27
+OpStore %ok %true
+OpSelectionMerge %33 None
+OpBranchConditional %true %32 %33
+%32 = OpLabel
+%36 = OpCompositeConstruct %v4int %27 %27 %27 %27
+%37 = OpSDiv %v4int %35 %36
+%38 = OpIEqual %v4bool %37 %35
+%40 = OpAll %bool %38
+OpBranch %33
+%33 = OpLabel
+%41 = OpPhi %bool %false %19 %40 %32
+OpStore %ok %41
+%44 = OpCompositeConstruct %v4int %27 %27 %27 %27
+OpStore %val %44
+%47 = OpIAdd %v4int %44 %46
+OpStore %val %47
+%48 = OpISub %v4int %47 %46
+OpStore %val %48
+%49 = OpIAdd %v4int %48 %46
+OpStore %val %49
+%50 = OpISub %v4int %49 %46
+OpStore %val %50
+OpSelectionMerge %52 None
+OpBranchConditional %41 %51 %52
+%51 = OpLabel
+%53 = OpIEqual %v4bool %50 %44
+%54 = OpAll %bool %53
+OpBranch %52
+%52 = OpLabel
+%55 = OpPhi %bool %false %33 %54 %51
+OpStore %ok %55
+%58 = OpIMul %v4int %50 %57
+OpStore %val %58
+%59 = OpSDiv %v4int %58 %57
+OpStore %val %59
+%60 = OpIMul %v4int %59 %57
+OpStore %val %60
+%61 = OpSDiv %v4int %60 %57
+OpStore %val %61
+OpSelectionMerge %63 None
+OpBranchConditional %55 %62 %63
+%62 = OpLabel
+%64 = OpIEqual %v4bool %61 %44
+%65 = OpAll %bool %64
+OpBranch %63
+%63 = OpLabel
+%66 = OpPhi %bool %false %52 %65 %62
+OpStore %ok %66
+OpReturnValue %66
+OpFunctionEnd
+%main = OpFunction %void None %68
+%69 = OpLabel
+%outputColor_S0 = OpVariable %_ptr_Function_v4float Function
+%output_S1 = OpVariable %_ptr_Function_v4float Function
+%_8_unknown = OpVariable %_ptr_Function_float Function
+%_9_ok = OpVariable %_ptr_Function_bool Function
+%_10_val = OpVariable %_ptr_Function_v4float Function
+%116 = OpVariable %_ptr_Function_v4float Function
+%72 = OpLoad %v4float %vcolor_S0
+OpStore %outputColor_S0 %72
+%76 = OpAccessChain %_ptr_Uniform_float %4 %int_0
+%77 = OpLoad %float %76
+OpStore %_8_unknown %77
+OpStore %_9_ok %true
+OpSelectionMerge %80 None
+OpBranchConditional %true %79 %80
+%79 = OpLabel
+%83 = OpCompositeConstruct %v4float %77 %77 %77 %77
+%84 = OpFDiv %v4float %82 %83
+%85 = OpFOrdEqual %v4bool %84 %82
+%86 = OpAll %bool %85
+OpBranch %80
+%80 = OpLabel
+%87 = OpPhi %bool %false %69 %86 %79
+OpStore %_9_ok %87
+%89 = OpCompositeConstruct %v4float %77 %77 %77 %77
+OpStore %_10_val %89
+%92 = OpFAdd %v4float %89 %91
+OpStore %_10_val %92
+%93 = OpFSub %v4float %92 %91
+OpStore %_10_val %93
+%94 = OpFAdd %v4float %93 %91
+OpStore %_10_val %94
+%95 = OpFSub %v4float %94 %91
+OpStore %_10_val %95
+OpSelectionMerge %97 None
+OpBranchConditional %87 %96 %97
+%96 = OpLabel
+%98 = OpFOrdEqual %v4bool %95 %89
+%99 = OpAll %bool %98
+OpBranch %97
+%97 = OpLabel
+%100 = OpPhi %bool %false %80 %99 %96
+OpStore %_9_ok %100
+%103 = OpFMul %v4float %95 %102
+OpStore %_10_val %103
+%104 = OpFDiv %v4float %103 %102
+OpStore %_10_val %104
+%105 = OpFMul %v4float %104 %102
+OpStore %_10_val %105
+%106 = OpFDiv %v4float %105 %102
+OpStore %_10_val %106
+OpSelectionMerge %108 None
+OpBranchConditional %100 %107 %108
+%107 = OpLabel
+%109 = OpFOrdEqual %v4bool %106 %89
+%110 = OpAll %bool %109
+OpBranch %108
+%108 = OpLabel
+%111 = OpPhi %bool %false %97 %110 %107
+OpStore %_9_ok %111
+OpSelectionMerge %113 None
+OpBranchConditional %111 %112 %113
+%112 = OpLabel
+%114 = OpFunctionCall %bool %test_int_S1_c0_b
+OpBranch %113
+%113 = OpLabel
+%115 = OpPhi %bool %false %108 %114 %112
+OpSelectionMerge %119 None
+OpBranchConditional %115 %117 %118
+%117 = OpLabel
+%120 = OpAccessChain %_ptr_Uniform_v4float %4 %int_2
+%122 = OpLoad %v4float %120
+OpStore %116 %122
+OpBranch %119
+%118 = OpLabel
+%123 = OpAccessChain %_ptr_Uniform_v4float %4 %int_1
+%124 = OpLoad %v4float %123
+OpStore %116 %124
+OpBranch %119
+%119 = OpLabel
+%125 = OpLoad %v4float %116
+OpStore %output_S1 %125
+OpStore %sk_FragColor %125
+OpReturn
+OpFunctionEnd
diff --git a/test/tint/bug/tint/1520.spvasm.expected.glsl b/test/tint/bug/tint/1520.spvasm.expected.glsl
new file mode 100644
index 0000000..358bd77
--- /dev/null
+++ b/test/tint/bug/tint/1520.spvasm.expected.glsl
@@ -0,0 +1,171 @@
+#version 310 es
+precision mediump float;
+
+layout(location = 0) in vec4 vcolor_S0_param_1;
+layout(location = 0) out vec4 sk_FragColor_1_1;
+struct UniformBuffer {
+  float unknownInput_S1_c0;
+  vec4 ucolorRed_S1_c0;
+  vec4 ucolorGreen_S1_c0;
+  mat3 umatrix_S1;
+};
+
+layout(binding = 0) uniform UniformBuffer_1 {
+  float unknownInput_S1_c0;
+  vec4 ucolorRed_S1_c0;
+  vec4 ucolorGreen_S1_c0;
+  mat3 umatrix_S1;
+} x_4;
+
+vec4 sk_FragColor = vec4(0.0f, 0.0f, 0.0f, 0.0f);
+bool sk_Clockwise = false;
+vec4 vcolor_S0 = vec4(0.0f, 0.0f, 0.0f, 0.0f);
+bool test_int_S1_c0_b() {
+  int unknown = 0;
+  bool ok = false;
+  ivec4 val = ivec4(0, 0, 0, 0);
+  bool x_40 = false;
+  bool x_54 = false;
+  bool x_65 = false;
+  bool x_41_phi = false;
+  bool x_55_phi = false;
+  bool x_66_phi = false;
+  float x_26 = x_4.unknownInput_S1_c0;
+  int x_27 = int(x_26);
+  unknown = x_27;
+  ok = true;
+  x_41_phi = false;
+  if (true) {
+    x_40 = all(equal((ivec4(0, 0, 0, 0) / ivec4(x_27, x_27, x_27, x_27)), ivec4(0, 0, 0, 0)));
+    x_41_phi = x_40;
+  }
+  bool x_41 = x_41_phi;
+  ok = x_41;
+  ivec4 x_44 = ivec4(x_27, x_27, x_27, x_27);
+  val = x_44;
+  ivec4 x_47 = (x_44 + ivec4(1, 1, 1, 1));
+  val = x_47;
+  ivec4 x_48 = (x_47 - ivec4(1, 1, 1, 1));
+  val = x_48;
+  ivec4 x_49 = (x_48 + ivec4(1, 1, 1, 1));
+  val = x_49;
+  ivec4 x_50 = (x_49 - ivec4(1, 1, 1, 1));
+  val = x_50;
+  x_55_phi = false;
+  if (x_41) {
+    x_54 = all(equal(x_50, x_44));
+    x_55_phi = x_54;
+  }
+  bool x_55 = x_55_phi;
+  ok = x_55;
+  ivec4 x_58 = (x_50 * ivec4(2, 2, 2, 2));
+  val = x_58;
+  ivec4 x_59 = (x_58 / ivec4(2, 2, 2, 2));
+  val = x_59;
+  ivec4 x_60 = (x_59 * ivec4(2, 2, 2, 2));
+  val = x_60;
+  ivec4 x_61 = (x_60 / ivec4(2, 2, 2, 2));
+  val = x_61;
+  x_66_phi = false;
+  if (x_55) {
+    x_65 = all(equal(x_61, x_44));
+    x_66_phi = x_65;
+  }
+  bool x_66 = x_66_phi;
+  ok = x_66;
+  return x_66;
+}
+
+void main_1() {
+  vec4 outputColor_S0 = vec4(0.0f, 0.0f, 0.0f, 0.0f);
+  vec4 output_S1 = vec4(0.0f, 0.0f, 0.0f, 0.0f);
+  float x_8_unknown = 0.0f;
+  bool x_9_ok = false;
+  vec4 x_10_val = vec4(0.0f, 0.0f, 0.0f, 0.0f);
+  vec4 x_116 = vec4(0.0f, 0.0f, 0.0f, 0.0f);
+  bool x_86 = false;
+  bool x_99 = false;
+  bool x_110 = false;
+  bool x_114 = false;
+  bool x_87_phi = false;
+  bool x_100_phi = false;
+  bool x_111_phi = false;
+  bool x_115_phi = false;
+  outputColor_S0 = vcolor_S0;
+  float x_77 = x_4.unknownInput_S1_c0;
+  x_8_unknown = x_77;
+  x_9_ok = true;
+  x_87_phi = false;
+  if (true) {
+    x_86 = all(equal((vec4(0.0f, 0.0f, 0.0f, 0.0f) / vec4(x_77, x_77, x_77, x_77)), vec4(0.0f, 0.0f, 0.0f, 0.0f)));
+    x_87_phi = x_86;
+  }
+  bool x_87 = x_87_phi;
+  x_9_ok = x_87;
+  vec4 x_89 = vec4(x_77, x_77, x_77, x_77);
+  x_10_val = x_89;
+  vec4 x_92 = (x_89 + vec4(1.0f, 1.0f, 1.0f, 1.0f));
+  x_10_val = x_92;
+  vec4 x_93 = (x_92 - vec4(1.0f, 1.0f, 1.0f, 1.0f));
+  x_10_val = x_93;
+  vec4 x_94 = (x_93 + vec4(1.0f, 1.0f, 1.0f, 1.0f));
+  x_10_val = x_94;
+  vec4 x_95 = (x_94 - vec4(1.0f, 1.0f, 1.0f, 1.0f));
+  x_10_val = x_95;
+  x_100_phi = false;
+  if (x_87) {
+    x_99 = all(equal(x_95, x_89));
+    x_100_phi = x_99;
+  }
+  bool x_100 = x_100_phi;
+  x_9_ok = x_100;
+  vec4 x_103 = (x_95 * vec4(2.0f, 2.0f, 2.0f, 2.0f));
+  x_10_val = x_103;
+  vec4 x_104 = (x_103 / vec4(2.0f, 2.0f, 2.0f, 2.0f));
+  x_10_val = x_104;
+  vec4 x_105 = (x_104 * vec4(2.0f, 2.0f, 2.0f, 2.0f));
+  x_10_val = x_105;
+  vec4 x_106 = (x_105 / vec4(2.0f, 2.0f, 2.0f, 2.0f));
+  x_10_val = x_106;
+  x_111_phi = false;
+  if (x_100) {
+    x_110 = all(equal(x_106, x_89));
+    x_111_phi = x_110;
+  }
+  bool x_111 = x_111_phi;
+  x_9_ok = x_111;
+  x_115_phi = false;
+  if (x_111) {
+    x_114 = test_int_S1_c0_b();
+    x_115_phi = x_114;
+  }
+  if (x_115_phi) {
+    vec4 x_122 = x_4.ucolorGreen_S1_c0;
+    x_116 = x_122;
+  } else {
+    vec4 x_124 = x_4.ucolorRed_S1_c0;
+    x_116 = x_124;
+  }
+  vec4 x_125 = x_116;
+  output_S1 = x_125;
+  sk_FragColor = x_125;
+  return;
+}
+
+struct main_out {
+  vec4 sk_FragColor_1;
+};
+
+main_out tint_symbol(bool sk_Clockwise_param, vec4 vcolor_S0_param) {
+  sk_Clockwise = sk_Clockwise_param;
+  vcolor_S0 = vcolor_S0_param;
+  main_1();
+  main_out tint_symbol_1 = main_out(sk_FragColor);
+  return tint_symbol_1;
+}
+
+void main() {
+  main_out inner_result = tint_symbol(gl_FrontFacing, vcolor_S0_param_1);
+  sk_FragColor_1_1 = inner_result.sk_FragColor_1;
+  return;
+}
diff --git a/test/tint/bug/tint/1520.spvasm.expected.hlsl b/test/tint/bug/tint/1520.spvasm.expected.hlsl
new file mode 100644
index 0000000..052cd99
--- /dev/null
+++ b/test/tint/bug/tint/1520.spvasm.expected.hlsl
@@ -0,0 +1,168 @@
+int4 value_or_one_if_zero_int4(int4 value) {
+  return value == int4(0, 0, 0, 0) ? int4(1, 1, 1, 1) : value;
+}
+
+cbuffer cbuffer_x_4 : register(b0, space0) {
+  uint4 x_4[7];
+};
+static float4 sk_FragColor = float4(0.0f, 0.0f, 0.0f, 0.0f);
+static bool sk_Clockwise = false;
+static float4 vcolor_S0 = float4(0.0f, 0.0f, 0.0f, 0.0f);
+
+bool test_int_S1_c0_b() {
+  int unknown = 0;
+  bool ok = false;
+  int4 val = int4(0, 0, 0, 0);
+  bool x_40 = false;
+  bool x_54 = false;
+  bool x_65 = false;
+  bool x_41_phi = false;
+  bool x_55_phi = false;
+  bool x_66_phi = false;
+  const float x_26 = asfloat(x_4[1].x);
+  const int x_27 = int(x_26);
+  unknown = x_27;
+  ok = true;
+  x_41_phi = false;
+  if (true) {
+    x_40 = all(((int4(0, 0, 0, 0) / value_or_one_if_zero_int4(int4(x_27, x_27, x_27, x_27))) == int4(0, 0, 0, 0)));
+    x_41_phi = x_40;
+  }
+  const bool x_41 = x_41_phi;
+  ok = x_41;
+  const int4 x_44 = int4(x_27, x_27, x_27, x_27);
+  val = x_44;
+  const int4 x_47 = (x_44 + int4(1, 1, 1, 1));
+  val = x_47;
+  const int4 x_48 = (x_47 - int4(1, 1, 1, 1));
+  val = x_48;
+  const int4 x_49 = (x_48 + int4(1, 1, 1, 1));
+  val = x_49;
+  const int4 x_50 = (x_49 - int4(1, 1, 1, 1));
+  val = x_50;
+  x_55_phi = false;
+  if (x_41) {
+    x_54 = all((x_50 == x_44));
+    x_55_phi = x_54;
+  }
+  const bool x_55 = x_55_phi;
+  ok = x_55;
+  const int4 x_58 = (x_50 * int4(2, 2, 2, 2));
+  val = x_58;
+  const int4 x_59 = (x_58 / int4(2, 2, 2, 2));
+  val = x_59;
+  const int4 x_60 = (x_59 * int4(2, 2, 2, 2));
+  val = x_60;
+  const int4 x_61 = (x_60 / int4(2, 2, 2, 2));
+  val = x_61;
+  x_66_phi = false;
+  if (x_55) {
+    x_65 = all((x_61 == x_44));
+    x_66_phi = x_65;
+  }
+  const bool x_66 = x_66_phi;
+  ok = x_66;
+  return x_66;
+}
+
+void main_1() {
+  float4 outputColor_S0 = float4(0.0f, 0.0f, 0.0f, 0.0f);
+  float4 output_S1 = float4(0.0f, 0.0f, 0.0f, 0.0f);
+  float x_8_unknown = 0.0f;
+  bool x_9_ok = false;
+  float4 x_10_val = float4(0.0f, 0.0f, 0.0f, 0.0f);
+  float4 x_116 = float4(0.0f, 0.0f, 0.0f, 0.0f);
+  bool x_86 = false;
+  bool x_99 = false;
+  bool x_110 = false;
+  bool x_114 = false;
+  bool x_87_phi = false;
+  bool x_100_phi = false;
+  bool x_111_phi = false;
+  bool x_115_phi = false;
+  outputColor_S0 = vcolor_S0;
+  const float x_77 = asfloat(x_4[1].x);
+  x_8_unknown = x_77;
+  x_9_ok = true;
+  x_87_phi = false;
+  if (true) {
+    x_86 = all(((float4(0.0f, 0.0f, 0.0f, 0.0f) / float4(x_77, x_77, x_77, x_77)) == float4(0.0f, 0.0f, 0.0f, 0.0f)));
+    x_87_phi = x_86;
+  }
+  const bool x_87 = x_87_phi;
+  x_9_ok = x_87;
+  const float4 x_89 = float4(x_77, x_77, x_77, x_77);
+  x_10_val = x_89;
+  const float4 x_92 = (x_89 + float4(1.0f, 1.0f, 1.0f, 1.0f));
+  x_10_val = x_92;
+  const float4 x_93 = (x_92 - float4(1.0f, 1.0f, 1.0f, 1.0f));
+  x_10_val = x_93;
+  const float4 x_94 = (x_93 + float4(1.0f, 1.0f, 1.0f, 1.0f));
+  x_10_val = x_94;
+  const float4 x_95 = (x_94 - float4(1.0f, 1.0f, 1.0f, 1.0f));
+  x_10_val = x_95;
+  x_100_phi = false;
+  if (x_87) {
+    x_99 = all((x_95 == x_89));
+    x_100_phi = x_99;
+  }
+  const bool x_100 = x_100_phi;
+  x_9_ok = x_100;
+  const float4 x_103 = (x_95 * float4(2.0f, 2.0f, 2.0f, 2.0f));
+  x_10_val = x_103;
+  const float4 x_104 = (x_103 / float4(2.0f, 2.0f, 2.0f, 2.0f));
+  x_10_val = x_104;
+  const float4 x_105 = (x_104 * float4(2.0f, 2.0f, 2.0f, 2.0f));
+  x_10_val = x_105;
+  const float4 x_106 = (x_105 / float4(2.0f, 2.0f, 2.0f, 2.0f));
+  x_10_val = x_106;
+  x_111_phi = false;
+  if (x_100) {
+    x_110 = all((x_106 == x_89));
+    x_111_phi = x_110;
+  }
+  const bool x_111 = x_111_phi;
+  x_9_ok = x_111;
+  x_115_phi = false;
+  if (x_111) {
+    x_114 = test_int_S1_c0_b();
+    x_115_phi = x_114;
+  }
+  if (x_115_phi) {
+    const float4 x_122 = asfloat(x_4[3]);
+    x_116 = x_122;
+  } else {
+    const float4 x_124 = asfloat(x_4[2]);
+    x_116 = x_124;
+  }
+  const float4 x_125 = x_116;
+  output_S1 = x_125;
+  sk_FragColor = x_125;
+  return;
+}
+
+struct main_out {
+  float4 sk_FragColor_1;
+};
+struct tint_symbol_1 {
+  float4 vcolor_S0_param : TEXCOORD0;
+  bool sk_Clockwise_param : SV_IsFrontFace;
+};
+struct tint_symbol_2 {
+  float4 sk_FragColor_1 : SV_Target0;
+};
+
+main_out main_inner(bool sk_Clockwise_param, float4 vcolor_S0_param) {
+  sk_Clockwise = sk_Clockwise_param;
+  vcolor_S0 = vcolor_S0_param;
+  main_1();
+  const main_out tint_symbol_5 = {sk_FragColor};
+  return tint_symbol_5;
+}
+
+tint_symbol_2 main(tint_symbol_1 tint_symbol) {
+  const main_out inner_result = main_inner(tint_symbol.sk_Clockwise_param, tint_symbol.vcolor_S0_param);
+  tint_symbol_2 wrapper_result = (tint_symbol_2)0;
+  wrapper_result.sk_FragColor_1 = inner_result.sk_FragColor_1;
+  return wrapper_result;
+}
diff --git a/test/tint/bug/tint/1520.spvasm.expected.msl b/test/tint/bug/tint/1520.spvasm.expected.msl
new file mode 100644
index 0000000..3e2b30a
--- /dev/null
+++ b/test/tint/bug/tint/1520.spvasm.expected.msl
@@ -0,0 +1,176 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct UniformBuffer {
+  /* 0x0000 */ int8_t tint_pad[16];
+  /* 0x0010 */ float unknownInput_S1_c0;
+  /* 0x0014 */ int8_t tint_pad_1[12];
+  /* 0x0020 */ float4 ucolorRed_S1_c0;
+  /* 0x0030 */ float4 ucolorGreen_S1_c0;
+  /* 0x0040 */ float3x3 umatrix_S1;
+};
+
+bool test_int_S1_c0_b(const constant UniformBuffer* const tint_symbol_5) {
+  int unknown = 0;
+  bool ok = false;
+  int4 val = 0;
+  bool x_40 = false;
+  bool x_54 = false;
+  bool x_65 = false;
+  bool x_41_phi = false;
+  bool x_55_phi = false;
+  bool x_66_phi = false;
+  float const x_26 = (*(tint_symbol_5)).unknownInput_S1_c0;
+  int const x_27 = int(x_26);
+  unknown = x_27;
+  ok = true;
+  x_41_phi = false;
+  if (true) {
+    x_40 = all(((int4(0, 0, 0, 0) / int4(x_27, x_27, x_27, x_27)) == int4(0, 0, 0, 0)));
+    x_41_phi = x_40;
+  }
+  bool const x_41 = x_41_phi;
+  ok = x_41;
+  int4 const x_44 = int4(x_27, x_27, x_27, x_27);
+  val = x_44;
+  int4 const x_47 = as_type<int4>((as_type<uint4>(x_44) + as_type<uint4>(int4(1, 1, 1, 1))));
+  val = x_47;
+  int4 const x_48 = as_type<int4>((as_type<uint4>(x_47) - as_type<uint4>(int4(1, 1, 1, 1))));
+  val = x_48;
+  int4 const x_49 = as_type<int4>((as_type<uint4>(x_48) + as_type<uint4>(int4(1, 1, 1, 1))));
+  val = x_49;
+  int4 const x_50 = as_type<int4>((as_type<uint4>(x_49) - as_type<uint4>(int4(1, 1, 1, 1))));
+  val = x_50;
+  x_55_phi = false;
+  if (x_41) {
+    x_54 = all((x_50 == x_44));
+    x_55_phi = x_54;
+  }
+  bool const x_55 = x_55_phi;
+  ok = x_55;
+  int4 const x_58 = as_type<int4>((as_type<uint4>(x_50) * as_type<uint4>(int4(2, 2, 2, 2))));
+  val = x_58;
+  int4 const x_59 = (x_58 / int4(2, 2, 2, 2));
+  val = x_59;
+  int4 const x_60 = as_type<int4>((as_type<uint4>(x_59) * as_type<uint4>(int4(2, 2, 2, 2))));
+  val = x_60;
+  int4 const x_61 = (x_60 / int4(2, 2, 2, 2));
+  val = x_61;
+  x_66_phi = false;
+  if (x_55) {
+    x_65 = all((x_61 == x_44));
+    x_66_phi = x_65;
+  }
+  bool const x_66 = x_66_phi;
+  ok = x_66;
+  return x_66;
+}
+
+void main_1(thread float4* const tint_symbol_6, const constant UniformBuffer* const tint_symbol_7, thread float4* const tint_symbol_8) {
+  float4 outputColor_S0 = 0.0f;
+  float4 output_S1 = 0.0f;
+  float x_8_unknown = 0.0f;
+  bool x_9_ok = false;
+  float4 x_10_val = 0.0f;
+  float4 x_116 = 0.0f;
+  bool x_86 = false;
+  bool x_99 = false;
+  bool x_110 = false;
+  bool x_114 = false;
+  bool x_87_phi = false;
+  bool x_100_phi = false;
+  bool x_111_phi = false;
+  bool x_115_phi = false;
+  float4 const x_72 = *(tint_symbol_6);
+  outputColor_S0 = x_72;
+  float const x_77 = (*(tint_symbol_7)).unknownInput_S1_c0;
+  x_8_unknown = x_77;
+  x_9_ok = true;
+  x_87_phi = false;
+  if (true) {
+    x_86 = all(((float4(0.0f, 0.0f, 0.0f, 0.0f) / float4(x_77, x_77, x_77, x_77)) == float4(0.0f, 0.0f, 0.0f, 0.0f)));
+    x_87_phi = x_86;
+  }
+  bool const x_87 = x_87_phi;
+  x_9_ok = x_87;
+  float4 const x_89 = float4(x_77, x_77, x_77, x_77);
+  x_10_val = x_89;
+  float4 const x_92 = (x_89 + float4(1.0f, 1.0f, 1.0f, 1.0f));
+  x_10_val = x_92;
+  float4 const x_93 = (x_92 - float4(1.0f, 1.0f, 1.0f, 1.0f));
+  x_10_val = x_93;
+  float4 const x_94 = (x_93 + float4(1.0f, 1.0f, 1.0f, 1.0f));
+  x_10_val = x_94;
+  float4 const x_95 = (x_94 - float4(1.0f, 1.0f, 1.0f, 1.0f));
+  x_10_val = x_95;
+  x_100_phi = false;
+  if (x_87) {
+    x_99 = all((x_95 == x_89));
+    x_100_phi = x_99;
+  }
+  bool const x_100 = x_100_phi;
+  x_9_ok = x_100;
+  float4 const x_103 = (x_95 * float4(2.0f, 2.0f, 2.0f, 2.0f));
+  x_10_val = x_103;
+  float4 const x_104 = (x_103 / float4(2.0f, 2.0f, 2.0f, 2.0f));
+  x_10_val = x_104;
+  float4 const x_105 = (x_104 * float4(2.0f, 2.0f, 2.0f, 2.0f));
+  x_10_val = x_105;
+  float4 const x_106 = (x_105 / float4(2.0f, 2.0f, 2.0f, 2.0f));
+  x_10_val = x_106;
+  x_111_phi = false;
+  if (x_100) {
+    x_110 = all((x_106 == x_89));
+    x_111_phi = x_110;
+  }
+  bool const x_111 = x_111_phi;
+  x_9_ok = x_111;
+  x_115_phi = false;
+  if (x_111) {
+    x_114 = test_int_S1_c0_b(tint_symbol_7);
+    x_115_phi = x_114;
+  }
+  bool const x_115 = x_115_phi;
+  if (x_115) {
+    float4 const x_122 = (*(tint_symbol_7)).ucolorGreen_S1_c0;
+    x_116 = x_122;
+  } else {
+    float4 const x_124 = (*(tint_symbol_7)).ucolorRed_S1_c0;
+    x_116 = x_124;
+  }
+  float4 const x_125 = x_116;
+  output_S1 = x_125;
+  *(tint_symbol_8) = x_125;
+  return;
+}
+
+struct main_out {
+  float4 sk_FragColor_1;
+};
+
+struct tint_symbol_2 {
+  float4 vcolor_S0_param [[user(locn0)]];
+};
+
+struct tint_symbol_3 {
+  float4 sk_FragColor_1 [[color(0)]];
+};
+
+main_out tint_symbol_inner(bool sk_Clockwise_param, float4 vcolor_S0_param, thread bool* const tint_symbol_9, thread float4* const tint_symbol_10, const constant UniformBuffer* const tint_symbol_11, thread float4* const tint_symbol_12) {
+  *(tint_symbol_9) = sk_Clockwise_param;
+  *(tint_symbol_10) = vcolor_S0_param;
+  main_1(tint_symbol_10, tint_symbol_11, tint_symbol_12);
+  main_out const tint_symbol_4 = {.sk_FragColor_1=*(tint_symbol_12)};
+  return tint_symbol_4;
+}
+
+fragment tint_symbol_3 tint_symbol(const constant UniformBuffer* tint_symbol_15 [[buffer(0)]], bool sk_Clockwise_param [[front_facing]], tint_symbol_2 tint_symbol_1 [[stage_in]]) {
+  thread bool tint_symbol_13 = false;
+  thread float4 tint_symbol_14 = 0.0f;
+  thread float4 tint_symbol_16 = 0.0f;
+  main_out const inner_result = tint_symbol_inner(sk_Clockwise_param, tint_symbol_1.vcolor_S0_param, &(tint_symbol_13), &(tint_symbol_14), tint_symbol_15, &(tint_symbol_16));
+  tint_symbol_3 wrapper_result = {};
+  wrapper_result.sk_FragColor_1 = inner_result.sk_FragColor_1;
+  return wrapper_result;
+}
+
diff --git a/test/tint/bug/tint/1520.spvasm.expected.spvasm b/test/tint/bug/tint/1520.spvasm.expected.spvasm
new file mode 100644
index 0000000..ad91329
--- /dev/null
+++ b/test/tint/bug/tint/1520.spvasm.expected.spvasm
@@ -0,0 +1,330 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 175
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main" %sk_Clockwise_param_1 %vcolor_S0_param_1 %sk_FragColor_1_1
+               OpExecutionMode %main OriginUpperLeft
+               OpName %sk_Clockwise_param_1 "sk_Clockwise_param_1"
+               OpName %vcolor_S0_param_1 "vcolor_S0_param_1"
+               OpName %sk_FragColor_1_1 "sk_FragColor_1_1"
+               OpName %UniformBuffer "UniformBuffer"
+               OpMemberName %UniformBuffer 0 "unknownInput_S1_c0"
+               OpMemberName %UniformBuffer 1 "ucolorRed_S1_c0"
+               OpMemberName %UniformBuffer 2 "ucolorGreen_S1_c0"
+               OpMemberName %UniformBuffer 3 "umatrix_S1"
+               OpName %x_4 "x_4"
+               OpName %sk_FragColor "sk_FragColor"
+               OpName %sk_Clockwise "sk_Clockwise"
+               OpName %vcolor_S0 "vcolor_S0"
+               OpName %test_int_S1_c0_b "test_int_S1_c0_b"
+               OpName %unknown "unknown"
+               OpName %ok "ok"
+               OpName %val "val"
+               OpName %x_40 "x_40"
+               OpName %x_54 "x_54"
+               OpName %x_65 "x_65"
+               OpName %x_41_phi "x_41_phi"
+               OpName %x_55_phi "x_55_phi"
+               OpName %x_66_phi "x_66_phi"
+               OpName %main_1 "main_1"
+               OpName %outputColor_S0 "outputColor_S0"
+               OpName %output_S1 "output_S1"
+               OpName %x_8_unknown "x_8_unknown"
+               OpName %x_9_ok "x_9_ok"
+               OpName %x_10_val "x_10_val"
+               OpName %x_116 "x_116"
+               OpName %x_86 "x_86"
+               OpName %x_99 "x_99"
+               OpName %x_110 "x_110"
+               OpName %x_114 "x_114"
+               OpName %x_87_phi "x_87_phi"
+               OpName %x_100_phi "x_100_phi"
+               OpName %x_111_phi "x_111_phi"
+               OpName %x_115_phi "x_115_phi"
+               OpName %main_out "main_out"
+               OpMemberName %main_out 0 "sk_FragColor_1"
+               OpName %main_inner "main_inner"
+               OpName %sk_Clockwise_param "sk_Clockwise_param"
+               OpName %vcolor_S0_param "vcolor_S0_param"
+               OpName %main "main"
+               OpDecorate %sk_Clockwise_param_1 BuiltIn FrontFacing
+               OpDecorate %vcolor_S0_param_1 Location 0
+               OpDecorate %sk_FragColor_1_1 Location 0
+               OpDecorate %UniformBuffer Block
+               OpMemberDecorate %UniformBuffer 0 Offset 16
+               OpMemberDecorate %UniformBuffer 1 Offset 32
+               OpMemberDecorate %UniformBuffer 2 Offset 48
+               OpMemberDecorate %UniformBuffer 3 Offset 64
+               OpMemberDecorate %UniformBuffer 3 ColMajor
+               OpMemberDecorate %UniformBuffer 3 MatrixStride 16
+               OpDecorate %x_4 NonWritable
+               OpDecorate %x_4 Binding 0
+               OpDecorate %x_4 DescriptorSet 0
+               OpMemberDecorate %main_out 0 Offset 0
+       %bool = OpTypeBool
+%_ptr_Input_bool = OpTypePointer Input %bool
+%sk_Clockwise_param_1 = OpVariable %_ptr_Input_bool Input
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%vcolor_S0_param_1 = OpVariable %_ptr_Input_v4float Input
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+         %10 = OpConstantNull %v4float
+%sk_FragColor_1_1 = OpVariable %_ptr_Output_v4float Output %10
+    %v3float = OpTypeVector %float 3
+%mat3v3float = OpTypeMatrix %v3float 3
+%UniformBuffer = OpTypeStruct %float %v4float %v4float %mat3v3float
+%_ptr_Uniform_UniformBuffer = OpTypePointer Uniform %UniformBuffer
+        %x_4 = OpVariable %_ptr_Uniform_UniformBuffer Uniform
+%_ptr_Private_v4float = OpTypePointer Private %v4float
+%sk_FragColor = OpVariable %_ptr_Private_v4float Private %10
+%_ptr_Private_bool = OpTypePointer Private %bool
+         %20 = OpConstantNull %bool
+%sk_Clockwise = OpVariable %_ptr_Private_bool Private %20
+  %vcolor_S0 = OpVariable %_ptr_Private_v4float Private %10
+         %22 = OpTypeFunction %bool
+        %int = OpTypeInt 32 1
+%_ptr_Function_int = OpTypePointer Function %int
+         %28 = OpConstantNull %int
+%_ptr_Function_bool = OpTypePointer Function %bool
+      %v4int = OpTypeVector %int 4
+%_ptr_Function_v4int = OpTypePointer Function %v4int
+         %34 = OpConstantNull %v4int
+       %uint = OpTypeInt 32 0
+     %uint_0 = OpConstant %uint 0
+%_ptr_Uniform_float = OpTypePointer Uniform %float
+       %true = OpConstantTrue %bool
+      %false = OpConstantFalse %bool
+      %int_0 = OpConstant %int 0
+         %53 = OpConstantComposite %v4int %int_0 %int_0 %int_0 %int_0
+     %v4bool = OpTypeVector %bool 4
+      %int_1 = OpConstant %int 1
+         %62 = OpConstantComposite %v4int %int_1 %int_1 %int_1 %int_1
+      %int_2 = OpConstant %int 2
+         %74 = OpConstantComposite %v4int %int_2 %int_2 %int_2 %int_2
+       %void = OpTypeVoid
+         %85 = OpTypeFunction %void
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%_ptr_Function_float = OpTypePointer Function %float
+         %94 = OpConstantNull %float
+    %float_0 = OpConstant %float 0
+        %113 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
+    %float_1 = OpConstant %float 1
+        %121 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1
+    %float_2 = OpConstant %float 2
+        %133 = OpConstantComposite %v4float %float_2 %float_2 %float_2 %float_2
+     %uint_2 = OpConstant %uint 2
+%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float
+     %uint_1 = OpConstant %uint 1
+   %main_out = OpTypeStruct %v4float
+        %160 = OpTypeFunction %main_out %bool %v4float
+%test_int_S1_c0_b = OpFunction %bool None %22
+         %24 = OpLabel
+    %unknown = OpVariable %_ptr_Function_int Function %28
+         %ok = OpVariable %_ptr_Function_bool Function %20
+        %val = OpVariable %_ptr_Function_v4int Function %34
+       %x_40 = OpVariable %_ptr_Function_bool Function %20
+       %x_54 = OpVariable %_ptr_Function_bool Function %20
+       %x_65 = OpVariable %_ptr_Function_bool Function %20
+   %x_41_phi = OpVariable %_ptr_Function_bool Function %20
+   %x_55_phi = OpVariable %_ptr_Function_bool Function %20
+   %x_66_phi = OpVariable %_ptr_Function_bool Function %20
+         %44 = OpAccessChain %_ptr_Uniform_float %x_4 %uint_0
+         %45 = OpLoad %float %44
+         %46 = OpConvertFToS %int %45
+               OpStore %unknown %46
+               OpStore %ok %true
+               OpStore %x_41_phi %false
+               OpSelectionMerge %49 None
+               OpBranchConditional %true %50 %49
+         %50 = OpLabel
+         %54 = OpCompositeConstruct %v4int %46 %46 %46 %46
+         %55 = OpSDiv %v4int %53 %54
+         %56 = OpIEqual %v4bool %55 %53
+         %51 = OpAll %bool %56
+               OpStore %x_40 %51
+         %58 = OpLoad %bool %x_40
+               OpStore %x_41_phi %58
+               OpBranch %49
+         %49 = OpLabel
+         %59 = OpLoad %bool %x_41_phi
+               OpStore %ok %59
+         %60 = OpCompositeConstruct %v4int %46 %46 %46 %46
+               OpStore %val %60
+         %63 = OpIAdd %v4int %60 %62
+               OpStore %val %63
+         %64 = OpISub %v4int %63 %62
+               OpStore %val %64
+         %65 = OpIAdd %v4int %64 %62
+               OpStore %val %65
+         %66 = OpISub %v4int %65 %62
+               OpStore %val %66
+               OpStore %x_55_phi %false
+               OpSelectionMerge %67 None
+               OpBranchConditional %59 %68 %67
+         %68 = OpLabel
+         %70 = OpIEqual %v4bool %66 %60
+         %69 = OpAll %bool %70
+               OpStore %x_54 %69
+         %71 = OpLoad %bool %x_54
+               OpStore %x_55_phi %71
+               OpBranch %67
+         %67 = OpLabel
+         %72 = OpLoad %bool %x_55_phi
+               OpStore %ok %72
+         %75 = OpIMul %v4int %66 %74
+               OpStore %val %75
+         %76 = OpSDiv %v4int %75 %74
+               OpStore %val %76
+         %77 = OpIMul %v4int %76 %74
+               OpStore %val %77
+         %78 = OpSDiv %v4int %77 %74
+               OpStore %val %78
+               OpStore %x_66_phi %false
+               OpSelectionMerge %79 None
+               OpBranchConditional %72 %80 %79
+         %80 = OpLabel
+         %82 = OpIEqual %v4bool %78 %60
+         %81 = OpAll %bool %82
+               OpStore %x_65 %81
+         %83 = OpLoad %bool %x_65
+               OpStore %x_66_phi %83
+               OpBranch %79
+         %79 = OpLabel
+         %84 = OpLoad %bool %x_66_phi
+               OpStore %ok %84
+               OpReturnValue %84
+               OpFunctionEnd
+     %main_1 = OpFunction %void None %85
+         %88 = OpLabel
+%outputColor_S0 = OpVariable %_ptr_Function_v4float Function %10
+  %output_S1 = OpVariable %_ptr_Function_v4float Function %10
+%x_8_unknown = OpVariable %_ptr_Function_float Function %94
+     %x_9_ok = OpVariable %_ptr_Function_bool Function %20
+   %x_10_val = OpVariable %_ptr_Function_v4float Function %10
+      %x_116 = OpVariable %_ptr_Function_v4float Function %10
+       %x_86 = OpVariable %_ptr_Function_bool Function %20
+       %x_99 = OpVariable %_ptr_Function_bool Function %20
+      %x_110 = OpVariable %_ptr_Function_bool Function %20
+      %x_114 = OpVariable %_ptr_Function_bool Function %20
+   %x_87_phi = OpVariable %_ptr_Function_bool Function %20
+  %x_100_phi = OpVariable %_ptr_Function_bool Function %20
+  %x_111_phi = OpVariable %_ptr_Function_bool Function %20
+  %x_115_phi = OpVariable %_ptr_Function_bool Function %20
+        %106 = OpLoad %v4float %vcolor_S0
+               OpStore %outputColor_S0 %106
+        %107 = OpAccessChain %_ptr_Uniform_float %x_4 %uint_0
+        %108 = OpLoad %float %107
+               OpStore %x_8_unknown %108
+               OpStore %x_9_ok %true
+               OpStore %x_87_phi %false
+               OpSelectionMerge %109 None
+               OpBranchConditional %true %110 %109
+        %110 = OpLabel
+        %114 = OpCompositeConstruct %v4float %108 %108 %108 %108
+        %115 = OpFDiv %v4float %113 %114
+        %116 = OpFOrdEqual %v4bool %115 %113
+        %111 = OpAll %bool %116
+               OpStore %x_86 %111
+        %117 = OpLoad %bool %x_86
+               OpStore %x_87_phi %117
+               OpBranch %109
+        %109 = OpLabel
+        %118 = OpLoad %bool %x_87_phi
+               OpStore %x_9_ok %118
+        %119 = OpCompositeConstruct %v4float %108 %108 %108 %108
+               OpStore %x_10_val %119
+        %122 = OpFAdd %v4float %119 %121
+               OpStore %x_10_val %122
+        %123 = OpFSub %v4float %122 %121
+               OpStore %x_10_val %123
+        %124 = OpFAdd %v4float %123 %121
+               OpStore %x_10_val %124
+        %125 = OpFSub %v4float %124 %121
+               OpStore %x_10_val %125
+               OpStore %x_100_phi %false
+               OpSelectionMerge %126 None
+               OpBranchConditional %118 %127 %126
+        %127 = OpLabel
+        %129 = OpFOrdEqual %v4bool %125 %119
+        %128 = OpAll %bool %129
+               OpStore %x_99 %128
+        %130 = OpLoad %bool %x_99
+               OpStore %x_100_phi %130
+               OpBranch %126
+        %126 = OpLabel
+        %131 = OpLoad %bool %x_100_phi
+               OpStore %x_9_ok %131
+        %134 = OpFMul %v4float %125 %133
+               OpStore %x_10_val %134
+        %135 = OpFDiv %v4float %134 %133
+               OpStore %x_10_val %135
+        %136 = OpFMul %v4float %135 %133
+               OpStore %x_10_val %136
+        %137 = OpFDiv %v4float %136 %133
+               OpStore %x_10_val %137
+               OpStore %x_111_phi %false
+               OpSelectionMerge %138 None
+               OpBranchConditional %131 %139 %138
+        %139 = OpLabel
+        %141 = OpFOrdEqual %v4bool %137 %119
+        %140 = OpAll %bool %141
+               OpStore %x_110 %140
+        %142 = OpLoad %bool %x_110
+               OpStore %x_111_phi %142
+               OpBranch %138
+        %138 = OpLabel
+        %143 = OpLoad %bool %x_111_phi
+               OpStore %x_9_ok %143
+               OpStore %x_115_phi %false
+               OpSelectionMerge %144 None
+               OpBranchConditional %143 %145 %144
+        %145 = OpLabel
+        %146 = OpFunctionCall %bool %test_int_S1_c0_b
+               OpStore %x_114 %146
+        %147 = OpLoad %bool %x_114
+               OpStore %x_115_phi %147
+               OpBranch %144
+        %144 = OpLabel
+        %148 = OpLoad %bool %x_115_phi
+               OpSelectionMerge %149 None
+               OpBranchConditional %148 %150 %151
+        %150 = OpLabel
+        %154 = OpAccessChain %_ptr_Uniform_v4float %x_4 %uint_2
+        %155 = OpLoad %v4float %154
+               OpStore %x_116 %155
+               OpBranch %149
+        %151 = OpLabel
+        %157 = OpAccessChain %_ptr_Uniform_v4float %x_4 %uint_1
+        %158 = OpLoad %v4float %157
+               OpStore %x_116 %158
+               OpBranch %149
+        %149 = OpLabel
+        %159 = OpLoad %v4float %x_116
+               OpStore %output_S1 %159
+               OpStore %sk_FragColor %159
+               OpReturn
+               OpFunctionEnd
+ %main_inner = OpFunction %main_out None %160
+%sk_Clockwise_param = OpFunctionParameter %bool
+%vcolor_S0_param = OpFunctionParameter %v4float
+        %165 = OpLabel
+               OpStore %sk_Clockwise %sk_Clockwise_param
+               OpStore %vcolor_S0 %vcolor_S0_param
+        %166 = OpFunctionCall %void %main_1
+        %167 = OpLoad %v4float %sk_FragColor
+        %168 = OpCompositeConstruct %main_out %167
+               OpReturnValue %168
+               OpFunctionEnd
+       %main = OpFunction %void None %85
+        %170 = OpLabel
+        %172 = OpLoad %bool %sk_Clockwise_param_1
+        %173 = OpLoad %v4float %vcolor_S0_param_1
+        %171 = OpFunctionCall %main_out %main_inner %172 %173
+        %174 = OpCompositeExtract %v4float %171 0
+               OpStore %sk_FragColor_1_1 %174
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/bug/tint/1520.spvasm.expected.wgsl b/test/tint/bug/tint/1520.spvasm.expected.wgsl
new file mode 100644
index 0000000..359389e
--- /dev/null
+++ b/test/tint/bug/tint/1520.spvasm.expected.wgsl
@@ -0,0 +1,165 @@
+struct UniformBuffer {
+  @size(16)
+  padding : u32,
+  unknownInput_S1_c0 : f32,
+  @size(12)
+  padding_1 : u32,
+  ucolorRed_S1_c0 : vec4<f32>,
+  ucolorGreen_S1_c0 : vec4<f32>,
+  umatrix_S1 : mat3x3<f32>,
+}
+
+@binding(0) @group(0) var<uniform> x_4 : UniformBuffer;
+
+var<private> sk_FragColor : vec4<f32>;
+
+var<private> sk_Clockwise : bool;
+
+var<private> vcolor_S0 : vec4<f32>;
+
+fn test_int_S1_c0_b() -> bool {
+  var unknown : i32;
+  var ok : bool;
+  var val : vec4<i32>;
+  var x_40 : bool;
+  var x_54 : bool;
+  var x_65 : bool;
+  var x_41_phi : bool;
+  var x_55_phi : bool;
+  var x_66_phi : bool;
+  let x_26 : f32 = x_4.unknownInput_S1_c0;
+  let x_27 : i32 = i32(x_26);
+  unknown = x_27;
+  ok = true;
+  x_41_phi = false;
+  if (true) {
+    x_40 = all(((vec4<i32>(0, 0, 0, 0) / vec4<i32>(x_27, x_27, x_27, x_27)) == vec4<i32>(0, 0, 0, 0)));
+    x_41_phi = x_40;
+  }
+  let x_41 : bool = x_41_phi;
+  ok = x_41;
+  let x_44 : vec4<i32> = vec4<i32>(x_27, x_27, x_27, x_27);
+  val = x_44;
+  let x_47 : vec4<i32> = (x_44 + vec4<i32>(1, 1, 1, 1));
+  val = x_47;
+  let x_48 : vec4<i32> = (x_47 - vec4<i32>(1, 1, 1, 1));
+  val = x_48;
+  let x_49 : vec4<i32> = (x_48 + vec4<i32>(1, 1, 1, 1));
+  val = x_49;
+  let x_50 : vec4<i32> = (x_49 - vec4<i32>(1, 1, 1, 1));
+  val = x_50;
+  x_55_phi = false;
+  if (x_41) {
+    x_54 = all((x_50 == x_44));
+    x_55_phi = x_54;
+  }
+  let x_55 : bool = x_55_phi;
+  ok = x_55;
+  let x_58 : vec4<i32> = (x_50 * vec4<i32>(2, 2, 2, 2));
+  val = x_58;
+  let x_59 : vec4<i32> = (x_58 / vec4<i32>(2, 2, 2, 2));
+  val = x_59;
+  let x_60 : vec4<i32> = (x_59 * vec4<i32>(2, 2, 2, 2));
+  val = x_60;
+  let x_61 : vec4<i32> = (x_60 / vec4<i32>(2, 2, 2, 2));
+  val = x_61;
+  x_66_phi = false;
+  if (x_55) {
+    x_65 = all((x_61 == x_44));
+    x_66_phi = x_65;
+  }
+  let x_66 : bool = x_66_phi;
+  ok = x_66;
+  return x_66;
+}
+
+fn main_1() {
+  var outputColor_S0 : vec4<f32>;
+  var output_S1 : vec4<f32>;
+  var x_8_unknown : f32;
+  var x_9_ok : bool;
+  var x_10_val : vec4<f32>;
+  var x_116 : vec4<f32>;
+  var x_86 : bool;
+  var x_99 : bool;
+  var x_110 : bool;
+  var x_114 : bool;
+  var x_87_phi : bool;
+  var x_100_phi : bool;
+  var x_111_phi : bool;
+  var x_115_phi : bool;
+  let x_72 : vec4<f32> = vcolor_S0;
+  outputColor_S0 = x_72;
+  let x_77 : f32 = x_4.unknownInput_S1_c0;
+  x_8_unknown = x_77;
+  x_9_ok = true;
+  x_87_phi = false;
+  if (true) {
+    x_86 = all(((vec4<f32>(0.0, 0.0, 0.0, 0.0) / vec4<f32>(x_77, x_77, x_77, x_77)) == vec4<f32>(0.0, 0.0, 0.0, 0.0)));
+    x_87_phi = x_86;
+  }
+  let x_87 : bool = x_87_phi;
+  x_9_ok = x_87;
+  let x_89 : vec4<f32> = vec4<f32>(x_77, x_77, x_77, x_77);
+  x_10_val = x_89;
+  let x_92 : vec4<f32> = (x_89 + vec4<f32>(1.0, 1.0, 1.0, 1.0));
+  x_10_val = x_92;
+  let x_93 : vec4<f32> = (x_92 - vec4<f32>(1.0, 1.0, 1.0, 1.0));
+  x_10_val = x_93;
+  let x_94 : vec4<f32> = (x_93 + vec4<f32>(1.0, 1.0, 1.0, 1.0));
+  x_10_val = x_94;
+  let x_95 : vec4<f32> = (x_94 - vec4<f32>(1.0, 1.0, 1.0, 1.0));
+  x_10_val = x_95;
+  x_100_phi = false;
+  if (x_87) {
+    x_99 = all((x_95 == x_89));
+    x_100_phi = x_99;
+  }
+  let x_100 : bool = x_100_phi;
+  x_9_ok = x_100;
+  let x_103 : vec4<f32> = (x_95 * vec4<f32>(2.0, 2.0, 2.0, 2.0));
+  x_10_val = x_103;
+  let x_104 : vec4<f32> = (x_103 / vec4<f32>(2.0, 2.0, 2.0, 2.0));
+  x_10_val = x_104;
+  let x_105 : vec4<f32> = (x_104 * vec4<f32>(2.0, 2.0, 2.0, 2.0));
+  x_10_val = x_105;
+  let x_106 : vec4<f32> = (x_105 / vec4<f32>(2.0, 2.0, 2.0, 2.0));
+  x_10_val = x_106;
+  x_111_phi = false;
+  if (x_100) {
+    x_110 = all((x_106 == x_89));
+    x_111_phi = x_110;
+  }
+  let x_111 : bool = x_111_phi;
+  x_9_ok = x_111;
+  x_115_phi = false;
+  if (x_111) {
+    x_114 = test_int_S1_c0_b();
+    x_115_phi = x_114;
+  }
+  let x_115 : bool = x_115_phi;
+  if (x_115) {
+    let x_122 : vec4<f32> = x_4.ucolorGreen_S1_c0;
+    x_116 = x_122;
+  } else {
+    let x_124 : vec4<f32> = x_4.ucolorRed_S1_c0;
+    x_116 = x_124;
+  }
+  let x_125 : vec4<f32> = x_116;
+  output_S1 = x_125;
+  sk_FragColor = x_125;
+  return;
+}
+
+struct main_out {
+  @location(0)
+  sk_FragColor_1 : vec4<f32>,
+}
+
+@stage(fragment)
+fn main(@builtin(front_facing) sk_Clockwise_param : bool, @location(0) vcolor_S0_param : vec4<f32>) -> main_out {
+  sk_Clockwise = sk_Clockwise_param;
+  vcolor_S0 = vcolor_S0_param;
+  main_1();
+  return main_out(sk_FragColor);
+}