// Copyright 2023 The Dawn & Tint Authors
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
//    list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its
//    contributors may be used to endorse or promote products derived from
//    this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include "src/tint/lang/spirv/reader/parser/helper_test.h"

namespace tint::spirv::reader {
namespace {

TEST_F(SpirvParserTest, FunctionVar) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %u32 = OpTypeInt 32 0
    %u32_ptr = OpTypePointer Function %u32
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
 %main_start = OpLabel
        %var = OpVariable %u32_ptr Function
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    %2:ptr<function, u32, read_write> = var undef
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, FunctionVar_RelaxedPrecision) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpDecorate %var RelaxedPrecision
       %void = OpTypeVoid
        %u32 = OpTypeInt 32 0
    %u32_ptr = OpTypePointer Function %u32
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
 %main_start = OpLabel
        %var = OpVariable %u32_ptr Function
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    %2:ptr<function, u32, read_write> = var undef
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, FunctionVar_Initializer) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %u32 = OpTypeInt 32 0
     %u32_42 = OpConstant %u32 42
    %u32_ptr = OpTypePointer Function %u32
    %ep_type = OpTypeFunction %void
       %main = OpFunction %void None %ep_type
 %main_start = OpLabel
        %var = OpVariable %u32_ptr Function %u32_42
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    %2:ptr<function, u32, read_write> = var 42u
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, PrivateVar) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %u32 = OpTypeInt 32 0
    %u32_ptr = OpTypePointer Private %u32
    %ep_type = OpTypeFunction %void
        %var = OpVariable %u32_ptr Private
       %main = OpFunction %void None %ep_type
 %main_start = OpLabel
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %1:ptr<private, u32, read_write> = var undef
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, PrivateVar_Initializer) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
       %void = OpTypeVoid
        %u32 = OpTypeInt 32 0
     %u32_42 = OpConstant %u32 42
    %u32_ptr = OpTypePointer Private %u32
    %ep_type = OpTypeFunction %void
        %var = OpVariable %u32_ptr Private %u32_42
       %main = OpFunction %void None %ep_type
 %main_start = OpLabel
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %1:ptr<private, u32, read_write> = var 42u
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, StorageVar_ReadOnly) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpExtension "SPV_KHR_storage_buffer_storage_class"
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %1 "main"
               OpExecutionMode %1 LocalSize 1 1 1
               OpDecorate %str Block
               OpMemberDecorate %str 0 Offset 0
               OpDecorate %6 NonWritable
               OpDecorate %6 DescriptorSet 1
               OpDecorate %6 Binding 2
       %void = OpTypeVoid
       %uint = OpTypeInt 32 0
        %str = OpTypeStruct %uint
%_ptr_StorageBuffer_str = OpTypePointer StorageBuffer %str
          %5 = OpTypeFunction %void
          %6 = OpVariable %_ptr_StorageBuffer_str StorageBuffer
          %1 = OpFunction %void None %5
          %7 = OpLabel
               OpReturn
               OpFunctionEnd
)",
              R"(
tint_symbol_1 = struct @align(4) {
  tint_symbol:u32 @offset(0)
}

$B1: {  # root
  %1:ptr<storage, tint_symbol_1, read> = var undef @binding_point(1, 2)
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, StorageVar_ReadWrite) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpExtension "SPV_KHR_storage_buffer_storage_class"
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %1 "main"
               OpExecutionMode %1 LocalSize 1 1 1
               OpDecorate %str Block
               OpMemberDecorate %str 0 Offset 0
               OpDecorate %6 DescriptorSet 1
               OpDecorate %6 Binding 2
       %void = OpTypeVoid
       %uint = OpTypeInt 32 0
        %str = OpTypeStruct %uint
%_ptr_StorageBuffer_str = OpTypePointer StorageBuffer %str
          %5 = OpTypeFunction %void
          %6 = OpVariable %_ptr_StorageBuffer_str StorageBuffer
          %1 = OpFunction %void None %5
          %7 = OpLabel
               OpReturn
               OpFunctionEnd
)",
              R"(
tint_symbol_1 = struct @align(4) {
  tint_symbol:u32 @offset(0)
}

$B1: {  # root
  %1:ptr<storage, tint_symbol_1, read_write> = var undef @binding_point(1, 2)
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, StorageVar_Coherent) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpExtension "SPV_KHR_storage_buffer_storage_class"
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %1 "main"
               OpExecutionMode %1 LocalSize 1 1 1
               OpDecorate %str Block
               OpMemberDecorate %str 0 Offset 0
               OpDecorate %6 DescriptorSet 1
               OpDecorate %6 Binding 2
               OpDecorate %6 Coherent
       %void = OpTypeVoid
       %uint = OpTypeInt 32 0
     %uint_0 = OpConstant %uint 0
%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
        %str = OpTypeStruct %uint
%_ptr_StorageBuffer_str = OpTypePointer StorageBuffer %str
          %5 = OpTypeFunction %void
          %6 = OpVariable %_ptr_StorageBuffer_str StorageBuffer
          %1 = OpFunction %void None %5
          %7 = OpLabel
          %8 = OpAccessChain %_ptr_StorageBuffer_uint %6 %uint_0
          %9 = OpLoad %uint %8
               OpStore %8 %9
               OpReturn
               OpFunctionEnd
)",
              R"(
tint_symbol_1 = struct @align(4) {
  tint_symbol:u32 @offset(0)
}

$B1: {  # root
  %1:ptr<storage, tint_symbol_1, read_write> = var undef @binding_point(1, 2)
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    %3:ptr<storage, u32, read_write> = access %1, 0u
    %4:u32 = load %3
    store %3, %4
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, StorageVar_Restrict) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpExtension "SPV_KHR_storage_buffer_storage_class"
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %1 "main"
               OpExecutionMode %1 LocalSize 1 1 1
               OpDecorate %str Block
               OpMemberDecorate %str 0 Offset 0
               OpDecorate %6 DescriptorSet 1
               OpDecorate %6 Binding 2
               OpDecorate %6 Restrict
       %void = OpTypeVoid
       %uint = OpTypeInt 32 0
     %uint_0 = OpConstant %uint 0
%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
        %str = OpTypeStruct %uint
%_ptr_StorageBuffer_str = OpTypePointer StorageBuffer %str
          %5 = OpTypeFunction %void
          %6 = OpVariable %_ptr_StorageBuffer_str StorageBuffer
          %1 = OpFunction %void None %5
          %7 = OpLabel
          %8 = OpAccessChain %_ptr_StorageBuffer_uint %6 %uint_0
          %9 = OpLoad %uint %8
               OpStore %8 %9
               OpReturn
               OpFunctionEnd
)",
              R"(
tint_symbol_1 = struct @align(4) {
  tint_symbol:u32 @offset(0)
}

$B1: {  # root
  %1:ptr<storage, tint_symbol_1, read_write> = var undef @binding_point(1, 2)
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    %3:ptr<storage, u32, read_write> = access %1, 0u
    %4:u32 = load %3
    store %3, %4
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, StorageVar_ReadOnly_And_ReadWrite) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpExtension "SPV_KHR_storage_buffer_storage_class"
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %1 "main"
               OpExecutionMode %1 LocalSize 1 1 1
               OpDecorate %str Block
               OpMemberDecorate %str 0 Offset 0
               OpDecorate %6 NonWritable
               OpDecorate %6 DescriptorSet 1
               OpDecorate %6 Binding 2
               OpDecorate %7 DescriptorSet 1
               OpDecorate %7 Binding 3
       %void = OpTypeVoid
       %uint = OpTypeInt 32 0
        %str = OpTypeStruct %uint
%_ptr_StorageBuffer_str = OpTypePointer StorageBuffer %str
          %5 = OpTypeFunction %void
          %6 = OpVariable %_ptr_StorageBuffer_str StorageBuffer
          %7 = OpVariable %_ptr_StorageBuffer_str StorageBuffer
          %1 = OpFunction %void None %5
          %8 = OpLabel
               OpReturn
               OpFunctionEnd
)",
              R"(
tint_symbol_1 = struct @align(4) {
  tint_symbol:u32 @offset(0)
}

$B1: {  # root
  %1:ptr<storage, tint_symbol_1, read> = var undef @binding_point(1, 2)
  %2:ptr<storage, tint_symbol_1, read_write> = var undef @binding_point(1, 3)
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, UniformVar) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %1 "main"
               OpExecutionMode %1 LocalSize 1 1 1
               OpDecorate %str Block
               OpMemberDecorate %str 0 Offset 0
               OpDecorate %6 NonWritable
               OpDecorate %6 DescriptorSet 1
               OpDecorate %6 Binding 2
       %void = OpTypeVoid
       %uint = OpTypeInt 32 0
        %str = OpTypeStruct %uint
%_ptr_Uniform_str = OpTypePointer Uniform %str
          %5 = OpTypeFunction %void
          %6 = OpVariable %_ptr_Uniform_str Uniform
          %1 = OpFunction %void None %5
          %7 = OpLabel
               OpReturn
               OpFunctionEnd
)",
              R"(
tint_symbol_1 = struct @align(4) {
  tint_symbol:u32 @offset(0)
}

$B1: {  # root
  %1:ptr<uniform, tint_symbol_1, read> = var undef @binding_point(1, 2)
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, UniformVar_BufferBlock) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %1 "main"
               OpExecutionMode %1 LocalSize 1 1 1
               OpMemberDecorate %str 0 Offset 0
               OpDecorate %6 DescriptorSet 1
               OpDecorate %6 Binding 2
               OpDecorate %str BufferBlock
       %void = OpTypeVoid
       %uint = OpTypeInt 32 0
        %str = OpTypeStruct %uint
%_ptr_Uniform_str = OpTypePointer Uniform %str
          %5 = OpTypeFunction %void
          %6 = OpVariable %_ptr_Uniform_str Uniform
          %1 = OpFunction %void None %5
          %7 = OpLabel
               OpReturn
               OpFunctionEnd
)",
              R"(
tint_symbol_1 = struct @align(4) {
  tint_symbol:u32 @offset(0)
}

$B1: {  # root
  %1:ptr<storage, tint_symbol_1, read_write> = var undef @binding_point(1, 2)
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, UniformVar_BufferBlock_NonReadable) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %1 "main"
               OpExecutionMode %1 LocalSize 1 1 1
               OpMemberDecorate %str 0 Offset 0
               OpDecorate %6 NonReadable
               OpDecorate %6 DescriptorSet 1
               OpDecorate %6 Binding 2
               OpDecorate %str BufferBlock
       %void = OpTypeVoid
       %uint = OpTypeInt 32 0
        %str = OpTypeStruct %uint
%_ptr_Uniform_str = OpTypePointer Uniform %str
          %5 = OpTypeFunction %void
          %6 = OpVariable %_ptr_Uniform_str Uniform
          %1 = OpFunction %void None %5
          %7 = OpLabel
               OpReturn
               OpFunctionEnd
)",
              R"(
tint_symbol_1 = struct @align(4) {
  tint_symbol:u32 @offset(0)
}

$B1: {  # root
  %1:ptr<storage, tint_symbol_1, read_write> = var undef @binding_point(1, 2)
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, UniformVar_BufferBlock_Propagate) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %1 "main"
               OpExecutionMode %1 LocalSize 1 1 1
               OpMemberDecorate %str 0 Offset 0
               OpDecorate %6 DescriptorSet 1
               OpDecorate %6 Binding 2
               OpDecorate %str BufferBlock
       %void = OpTypeVoid
       %uint = OpTypeInt 32 0
     %uint_0 = OpConstant %uint 0
        %str = OpTypeStruct %uint
%_ptr_Uniform_str = OpTypePointer Uniform %str
%_ptr_Uniform_uint = OpTypePointer Uniform %uint
          %5 = OpTypeFunction %void
          %6 = OpVariable %_ptr_Uniform_str Uniform
          %1 = OpFunction %void None %5
          %7 = OpLabel
          %8 = OpCopyObject %_ptr_Uniform_str %6
          %9 = OpAccessChain %_ptr_Uniform_uint %8 %uint_0
         %10 = OpCopyObject %_ptr_Uniform_uint %9
               OpStore %10 %uint_0
               OpReturn
               OpFunctionEnd
)",
              R"(
tint_symbol_1 = struct @align(4) {
  tint_symbol:u32 @offset(0)
}

$B1: {  # root
  %1:ptr<storage, tint_symbol_1, read_write> = var undef @binding_point(1, 2)
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    %3:ptr<storage, tint_symbol_1, read_write> = let %1
    %4:ptr<storage, u32, read_write> = access %3, 0u
    %5:ptr<storage, u32, read_write> = let %4
    store %5, 0u
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, UniformVar_BufferBlock_NonWritable) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %1 "main"
               OpExecutionMode %1 LocalSize 1 1 1
               OpMemberDecorate %str 0 Offset 0
               OpDecorate %6 NonWritable
               OpDecorate %6 DescriptorSet 1
               OpDecorate %6 Binding 2
               OpDecorate %str BufferBlock
       %void = OpTypeVoid
       %uint = OpTypeInt 32 0
        %str = OpTypeStruct %uint
%_ptr_Uniform_str = OpTypePointer Uniform %str
          %5 = OpTypeFunction %void
          %6 = OpVariable %_ptr_Uniform_str Uniform
          %1 = OpFunction %void None %5
          %7 = OpLabel
               OpReturn
               OpFunctionEnd
)",
              R"(
tint_symbol_1 = struct @align(4) {
  tint_symbol:u32 @offset(0)
}

$B1: {  # root
  %1:ptr<storage, tint_symbol_1, read> = var undef @binding_point(1, 2)
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, UniformConstantVar) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %1 "main"
               OpExecutionMode %1 LocalSize 1 1 1
               OpDecorate %6 DescriptorSet 1
               OpDecorate %6 Binding 2
       %void = OpTypeVoid
       %samp = OpTypeSampler
%_ptr_Uniform_samp = OpTypePointer UniformConstant %samp
          %5 = OpTypeFunction %void
          %6 = OpVariable %_ptr_Uniform_samp UniformConstant
          %1 = OpFunction %void None %5
          %7 = OpLabel
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %1:ptr<handle, sampler, read> = var undef @binding_point(1, 2)
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, PushConstantVar) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %1 "main"
               OpExecutionMode %1 LocalSize 1 1 1
               OpDecorate %pc_struct Block
               OpMemberDecorate %pc_struct 0 Offset 0
               OpMemberDecorate %pc_struct 1 Offset 4
       %void = OpTypeVoid
      %float = OpTypeFloat 32
  %pc_struct = OpTypeStruct %float %float
%ptr_pc_struct = OpTypePointer PushConstant %pc_struct
         %pc = OpVariable %ptr_pc_struct PushConstant
          %5 = OpTypeFunction %void
          %1 = OpFunction %void None %5
          %7 = OpLabel
               OpReturn
               OpFunctionEnd
)",
              R"(
tint_symbol_2 = struct @align(4) {
  tint_symbol:f32 @offset(0)
  tint_symbol_1:f32 @offset(4)
}

$B1: {  # root
  %1:ptr<immediate, tint_symbol_2, read> = var undef
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    ret
  }
}
)");
}

struct BuiltinCase {
    std::string spirv_type;
    std::string spirv_builtin;
    std::string ir;
};
std::string PrintBuiltinCase(testing::TestParamInfo<BuiltinCase> bc) {
    return bc.param.spirv_builtin;
}

using BuiltinInputTest = SpirvParserTestWithParam<BuiltinCase>;

TEST_P(BuiltinInputTest, Enum) {
    auto params = GetParam();
    EXPECT_IR_SPV(R"(
               OpCapability GroupNonUniform
               OpCapability Shader
               OpCapability SampleRateShading
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpDecorate %var BuiltIn )" +
                      params.spirv_builtin + R"(
       %void = OpTypeVoid
       %bool = OpTypeBool
        %u32 = OpTypeInt 32 0
      %vec3u = OpTypeVector %u32 3
        %f32 = OpTypeFloat 32
      %vec4f = OpTypeVector %f32 4
      %u32_1 = OpConstant %u32 1
  %arr_u32_1 = OpTypeArray %u32 %u32_1
    %fn_type = OpTypeFunction %void

 %_ptr_Input = OpTypePointer Input %)" +
                      params.spirv_type + R"(
        %var = OpVariable %_ptr_Input Input

       %main = OpFunction %void None %fn_type
 %main_start = OpLabel
               OpReturn
               OpFunctionEnd
)",
                  params.ir, SPV_ENV_VULKAN_1_1);
}

INSTANTIATE_TEST_SUITE_P(
    SpirvParser,
    BuiltinInputTest,
    testing::Values(
        BuiltinCase{
            "vec4f",
            "FragCoord",
            "%1:ptr<__in, vec4<f32>, read> = var undef @builtin(position)",
        },
        BuiltinCase{
            "bool",
            "FrontFacing",
            "%1:ptr<__in, bool, read> = var undef @builtin(front_facing)",
        },
        BuiltinCase{
            "vec3u",
            "GlobalInvocationId",
            "%1:ptr<__in, vec3<u32>, read> = var undef @builtin(global_invocation_id)",
        },
        BuiltinCase{
            "u32",
            "InstanceIndex",
            "%1:ptr<__in, u32, read> = var undef @builtin(instance_index)",
        },
        BuiltinCase{
            "vec3u",
            "LocalInvocationId",
            "%1:ptr<__in, vec3<u32>, read> = var undef @builtin(local_invocation_id)",
        },
        BuiltinCase{
            "u32",
            "LocalInvocationIndex",
            "%1:ptr<__in, u32, read> = var undef @builtin(local_invocation_index)",
        },
        BuiltinCase{
            "vec3u",
            "NumWorkgroups",
            "%1:ptr<__in, vec3<u32>, read> = var undef @builtin(num_workgroups)",
        },
        BuiltinCase{
            "u32",
            "SampleId",
            "%1:ptr<__in, u32, read> = var undef @builtin(sample_index)",
        },
        BuiltinCase{
            "arr_u32_1",
            "SampleMask",
            "%1:ptr<__in, array<u32, 1>, read> = var undef @builtin(sample_mask)",
        },
        BuiltinCase{
            "u32",
            "SubgroupId",
            "%1:ptr<__in, u32, read> = var undef @builtin(subgroup_id)",
        },
        BuiltinCase{
            "u32",
            "SubgroupSize",
            "%1:ptr<__in, u32, read> = var undef @builtin(subgroup_size)",
        },
        BuiltinCase{
            "u32",
            "SubgroupLocalInvocationId",
            "%1:ptr<__in, u32, read> = var undef @builtin(subgroup_invocation_id)",
        },
        BuiltinCase{
            "u32",
            "NumSubgroups",
            "%1:ptr<__in, u32, read> = var undef @builtin(num_subgroups)",
        },
        BuiltinCase{
            "u32",
            "VertexIndex",
            "%1:ptr<__in, u32, read> = var undef @builtin(vertex_index)",
        },
        BuiltinCase{
            "vec3u",
            "WorkgroupId",
            "%1:ptr<__in, vec3<u32>, read> = var undef @builtin(workgroup_id)",
        }),
    PrintBuiltinCase);

using BuiltinOutputTest = SpirvParserTestWithParam<BuiltinCase>;

TEST_P(BuiltinOutputTest, Enum) {
    auto params = GetParam();
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpDecorate %var BuiltIn )" +
                  params.spirv_builtin + R"(
       %void = OpTypeVoid
        %u32 = OpTypeInt 32 0
        %f32 = OpTypeFloat 32
      %vec4f = OpTypeVector %f32 4
      %u32_1 = OpConstant %u32 1
  %arr_u32_1 = OpTypeArray %u32 %u32_1
    %fn_type = OpTypeFunction %void

%_ptr_Output = OpTypePointer Output %)" +
                  params.spirv_type + R"(
        %var = OpVariable %_ptr_Output Output

       %main = OpFunction %void None %fn_type
 %main_start = OpLabel
               OpReturn
               OpFunctionEnd
)",
              params.ir);
}

INSTANTIATE_TEST_SUITE_P(
    SpirvParser,
    BuiltinOutputTest,
    testing::Values(
        BuiltinCase{
            "f32",
            "PointSize",
            "%1:ptr<__out, f32, read_write> = var undef @builtin(__point_size)",
        },
        BuiltinCase{
            "vec4f",
            "Position",
            "%1:ptr<__out, vec4<f32>, read_write> = var undef @builtin(position)",
        },
        BuiltinCase{
            "arr_u32_1",
            "SampleMask",
            "%1:ptr<__out, array<u32, 1>, read_write> = var undef @builtin(sample_mask)",
        }),
    PrintBuiltinCase);

TEST_F(SpirvParserTest, Invariant_OnVariable) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpDecorate %var BuiltIn Position
               OpDecorate %var Invariant
       %void = OpTypeVoid
        %f32 = OpTypeFloat 32
      %vec4f = OpTypeVector %f32 4
    %fn_type = OpTypeFunction %void

%_ptr_Output = OpTypePointer Output %vec4f
        %var = OpVariable %_ptr_Output Output

       %main = OpFunction %void None %fn_type
 %main_start = OpLabel
               OpReturn
               OpFunctionEnd
)",
              "%1:ptr<__out, vec4<f32>, read_write> = var undef @invariant @builtin(position)");
}

struct LocationCase {
    std::string spirv_decorations;
    std::string ir;
};

using LocationVarTest = SpirvParserTestWithParam<LocationCase>;

TEST_P(LocationVarTest, Input) {
    auto params = GetParam();
    EXPECT_IR(R"(
               OpCapability Shader
               OpCapability SampleRateShading
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
          )" + params.spirv_decorations +
                  R"(
       %void = OpTypeVoid
        %f32 = OpTypeFloat 32
      %vec4f = OpTypeVector %f32 4
    %fn_type = OpTypeFunction %void

%_ptr_Input = OpTypePointer Input %vec4f
        %var = OpVariable %_ptr_Input Input

       %main = OpFunction %void None %fn_type
 %main_start = OpLabel
               OpReturn
               OpFunctionEnd
)",
              "%1:ptr<__in, vec4<f32>, read> = " + params.ir);
}

TEST_P(LocationVarTest, Output) {
    auto params = GetParam();
    EXPECT_IR(R"(
               OpCapability Shader
               OpCapability SampleRateShading
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
          )" + params.spirv_decorations +
                  R"(
       %void = OpTypeVoid
        %f32 = OpTypeFloat 32
      %vec4f = OpTypeVector %f32 4
    %fn_type = OpTypeFunction %void

%_ptr_Output = OpTypePointer Output %vec4f
        %var = OpVariable %_ptr_Output Output

       %main = OpFunction %void None %fn_type
 %main_start = OpLabel
               OpReturn
               OpFunctionEnd
)",
              "%1:ptr<__out, vec4<f32>, read_write> = " + params.ir);
}

INSTANTIATE_TEST_SUITE_P(SpirvParser,
                         LocationVarTest,
                         testing::Values(
                             LocationCase{
                                 "OpDecorate %var Location 1 ",
                                 "var undef @location(1)",
                             },
                             LocationCase{
                                 "OpDecorate %var Location 2 "
                                 "OpDecorate %var NoPerspective ",
                                 "var undef @location(2) @interpolate(linear)",
                             },
                             LocationCase{
                                 "OpDecorate %var Location 3 "
                                 "OpDecorate %var Flat ",
                                 "var undef @location(3) @interpolate(flat)",
                             },
                             LocationCase{
                                 "OpDecorate %var Location 4 "
                                 "OpDecorate %var Centroid ",
                                 "var undef @location(4) @interpolate(perspective, centroid)",
                             },
                             LocationCase{
                                 "OpDecorate %var Location 5 "
                                 "OpDecorate %var Sample ",
                                 "var undef @location(5) @interpolate(perspective, sample)",
                             },
                             LocationCase{
                                 "OpDecorate %var Location 6 "
                                 "OpDecorate %var NoPerspective "
                                 "OpDecorate %var Centroid ",
                                 "var undef @location(6) @interpolate(linear, centroid)",
                             }));

TEST_F(SpirvParserTest, Vertex_Output_I32) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpCapability SampleRateShading
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpDecorate %var Location 0
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
    %fn_type = OpTypeFunction %void

%_ptr_Output = OpTypePointer Output %i32
        %var = OpVariable %_ptr_Output Output

       %main = OpFunction %void None %fn_type
 %main_start = OpLabel
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %1:ptr<__out, i32, read_write> = var undef @location(0) @interpolate(flat)
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Vertex_Output_U32) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpCapability SampleRateShading
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpDecorate %var Location 0
       %void = OpTypeVoid
        %u32 = OpTypeInt 32 0
    %fn_type = OpTypeFunction %void

%_ptr_Output = OpTypePointer Output %u32
        %var = OpVariable %_ptr_Output Output

       %main = OpFunction %void None %fn_type
 %main_start = OpLabel
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %1:ptr<__out, u32, read_write> = var undef @location(0) @interpolate(flat)
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Vertex_Output_Vec3_I32) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpCapability SampleRateShading
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpDecorate %var Location 0
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
      %v3i32 = OpTypeVector %i32 3
    %fn_type = OpTypeFunction %void

%_ptr_Output = OpTypePointer Output %v3i32
        %var = OpVariable %_ptr_Output Output

       %main = OpFunction %void None %fn_type
 %main_start = OpLabel
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %1:ptr<__out, vec3<i32>, read_write> = var undef @location(0) @interpolate(flat)
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Vertex_Output_F32) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpCapability SampleRateShading
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpDecorate %var Location 0
       %void = OpTypeVoid
        %f32 = OpTypeFloat 32
    %fn_type = OpTypeFunction %void

%_ptr_Output = OpTypePointer Output %f32
        %var = OpVariable %_ptr_Output Output

       %main = OpFunction %void None %fn_type
 %main_start = OpLabel
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %1:ptr<__out, f32, read_write> = var undef @location(0)
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Var_OpSpecConstantTrue) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpName %c "myconst"
               OpDecorate %c SpecId 12
       %void = OpTypeVoid
       %bool = OpTypeBool
        %f32 = OpTypeFloat 32
      %vec4f = OpTypeVector %f32 4
          %c = OpSpecConstantTrue %bool
     %voidfn = OpTypeFunction %void
       %main = OpFunction %void None %voidfn
 %main_entry = OpLabel
          %b = OpLogicalAnd %bool %c %c
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %myconst:bool = override true @id(12)
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    %3:bool = and %myconst, %myconst
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Var_OpSpecConstant_f16) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpCapability Float16
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpName %c "myconst"
               OpDecorate %c SpecId 12
       %void = OpTypeVoid
        %f16 = OpTypeFloat 16
          %c = OpSpecConstant %f16 2
     %voidfn = OpTypeFunction %void
       %main = OpFunction %void None %voidfn
 %main_entry = OpLabel
          %b = OpFAdd %f16 %c %c
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %myconst:f16 = override 2.0h @id(12)
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    %3:f16 = add %myconst, %myconst
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Var_OpSpecConstant_f32) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpName %c "myconst"
               OpDecorate %c SpecId 12
       %void = OpTypeVoid
        %f32 = OpTypeFloat 32
          %c = OpSpecConstant %f32 2
     %voidfn = OpTypeFunction %void
       %main = OpFunction %void None %voidfn
 %main_entry = OpLabel
          %b = OpFAdd %f32 %c %c
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %myconst:f32 = override 2.0f @id(12)
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    %3:f32 = add %myconst, %myconst
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Var_OpSpecConstant_u32) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpName %c "myconst"
               OpDecorate %c SpecId 12
       %void = OpTypeVoid
        %u32 = OpTypeInt 32 0
          %c = OpSpecConstant %u32 2
     %voidfn = OpTypeFunction %void
       %main = OpFunction %void None %voidfn
 %main_entry = OpLabel
          %b = OpIAdd %u32 %c %c
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %myconst:u32 = override 2u @id(12)
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    %3:u32 = spirv.add<u32> %myconst, %myconst
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Var_OpSpecConstant_i32) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpName %c "myconst"
               OpDecorate %c SpecId 12
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
          %c = OpSpecConstant %i32 2
     %voidfn = OpTypeFunction %void
       %main = OpFunction %void None %voidfn
 %main_entry = OpLabel
          %b = OpIAdd %i32 %c %c
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %myconst:i32 = override 2i @id(12)
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    %3:i32 = spirv.add<i32> %myconst, %myconst
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Var_OpSpecConstantTrue_NoSpecId) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpName %c "myconst"
       %void = OpTypeVoid
       %bool = OpTypeBool
        %f32 = OpTypeFloat 32
      %vec4f = OpTypeVector %f32 4
          %c = OpSpecConstantTrue %bool
     %voidfn = OpTypeFunction %void
       %main = OpFunction %void None %voidfn
 %main_entry = OpLabel
          %b = OpLogicalAnd %bool %c %c
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    %2:bool = and true, true
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Var_OpSpecConstantFalse) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpName %c "myconst"
               OpDecorate %c SpecId 12
       %void = OpTypeVoid
       %bool = OpTypeBool
        %f32 = OpTypeFloat 32
      %vec4f = OpTypeVector %f32 4
          %c = OpSpecConstantFalse %bool
     %voidfn = OpTypeFunction %void
       %main = OpFunction %void None %voidfn
 %main_entry = OpLabel
          %b = OpLogicalAnd %bool %c %c
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %myconst:bool = override false @id(12)
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    %3:bool = and %myconst, %myconst
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Var_OpSpecConstantFalse_NoSpecId) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpName %c "myconst"
       %void = OpTypeVoid
       %bool = OpTypeBool
        %f32 = OpTypeFloat 32
      %vec4f = OpTypeVector %f32 4
          %c = OpSpecConstantFalse %bool
     %voidfn = OpTypeFunction %void
       %main = OpFunction %void None %voidfn
 %main_entry = OpLabel
          %b = OpLogicalAnd %bool %c %c
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    %2:bool = and false, false
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Var_OpSpecConstantOp_LogicalAnd) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpName %cond "myconst"
       %void = OpTypeVoid
       %bool = OpTypeBool
      %false = OpSpecConstantFalse %bool
       %true = OpSpecConstantTrue %bool
       %cond = OpSpecConstantOp %bool LogicalAnd %false %true
     %voidfn = OpTypeFunction %void
       %main = OpFunction %void None %voidfn
 %main_entry = OpLabel
          %b = OpLogicalAnd %bool %cond %cond
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %1:bool = and false, true
  %myconst:bool = override %1
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    %4:bool = and %myconst, %myconst
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Var_OpSpecConstantOp_LogicalOr) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpName %cond "myconst"
       %void = OpTypeVoid
       %bool = OpTypeBool
      %false = OpSpecConstantFalse %bool
       %true = OpSpecConstantTrue %bool
       %cond = OpSpecConstantOp %bool LogicalOr %false %true
     %voidfn = OpTypeFunction %void
       %main = OpFunction %void None %voidfn
 %main_entry = OpLabel
          %b = OpLogicalOr %bool %cond %cond
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %1:bool = or false, true
  %myconst:bool = override %1
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    %4:bool = or %myconst, %myconst
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Var_OpSpecConstantOp_LogicalNot) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpName %cond "myconst"
       %void = OpTypeVoid
       %bool = OpTypeBool
      %false = OpSpecConstantFalse %bool
       %true = OpSpecConstantTrue %bool
       %cond = OpSpecConstantOp %bool LogicalNot %false
     %voidfn = OpTypeFunction %void
       %main = OpFunction %void None %voidfn
 %main_entry = OpLabel
          %b = OpLogicalNot %bool %cond
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %1:bool = not false
  %myconst:bool = override %1
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    %4:bool = not %myconst
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Var_OpSpecConstantOp_LogicalEqual) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpName %cond "myconst"
       %void = OpTypeVoid
       %bool = OpTypeBool
      %false = OpSpecConstantFalse %bool
       %true = OpSpecConstantTrue %bool
       %cond = OpSpecConstantOp %bool LogicalEqual %true %false
     %voidfn = OpTypeFunction %void
       %main = OpFunction %void None %voidfn
 %main_entry = OpLabel
          %1 = OpLogicalEqual %bool %cond %cond
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %1:bool = eq true, false
  %myconst:bool = override %1
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    %4:bool = eq %myconst, %myconst
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Var_OpSpecConstantOp_LogicalNotEqual) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpName %cond "myconst"
       %void = OpTypeVoid
       %bool = OpTypeBool
      %false = OpSpecConstantFalse %bool
       %true = OpSpecConstantTrue %bool
       %cond = OpSpecConstantOp %bool LogicalNotEqual %true %false
     %voidfn = OpTypeFunction %void
       %main = OpFunction %void None %voidfn
 %main_entry = OpLabel
          %1 = OpLogicalNotEqual %bool %cond %cond
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %1:bool = neq true, false
  %myconst:bool = override %1
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    %4:bool = neq %myconst, %myconst
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Var_OpSpecConstantOp_Not_ResultI32_i32) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpName %cond "myconst"
       %void = OpTypeVoid
       %bool = OpTypeBool
        %i32 = OpTypeInt 32 1
      %false = OpSpecConstantFalse %bool
       %true = OpSpecConstantTrue %bool
        %one = OpConstant %i32 1
       %cond = OpSpecConstantOp %i32 Not %one
     %voidfn = OpTypeFunction %void
       %main = OpFunction %void None %voidfn
 %main_entry = OpLabel
          %1 = OpNot %i32 %cond
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %1:i32 = spirv.not<i32> 1i
  %myconst:i32 = override %1
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    %4:i32 = spirv.not<i32> %myconst
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Var_OpSpecConstantOp_Not_ResultI32_u32) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpName %myconst "myconst"
       %void = OpTypeVoid
       %bool = OpTypeBool
        %i32 = OpTypeInt 32 1
        %u32 = OpTypeInt 32 0
      %false = OpSpecConstantFalse %bool
       %true = OpSpecConstantTrue %bool
        %one = OpConstant %u32 1
    %myconst = OpSpecConstantOp %i32 Not %one
     %voidfn = OpTypeFunction %void
       %main = OpFunction %void None %voidfn
 %main_entry = OpLabel
          %1 = OpCopyObject %i32 %myconst
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %1:i32 = spirv.not<i32> 1u
  %myconst:i32 = override %1
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    %4:i32 = let %myconst
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Var_OpSpecConstantOp_Not_ResultU32_i32) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpName %myconst "myconst"
       %void = OpTypeVoid
       %bool = OpTypeBool
        %i32 = OpTypeInt 32 1
        %u32 = OpTypeInt 32 0
      %false = OpSpecConstantFalse %bool
       %true = OpSpecConstantTrue %bool
        %one = OpConstant %i32 1
    %myconst = OpSpecConstantOp %u32 Not %one
     %voidfn = OpTypeFunction %void
       %main = OpFunction %void None %voidfn
 %main_entry = OpLabel
          %1 = OpCopyObject %u32 %myconst
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %1:u32 = spirv.not<u32> 1i
  %myconst:u32 = override %1
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    %4:u32 = let %myconst
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Var_OpSpecConstantOp_Not_ResultU32_u32) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpName %myconst "myconst"
       %void = OpTypeVoid
       %bool = OpTypeBool
        %i32 = OpTypeInt 32 1
        %u32 = OpTypeInt 32 0
      %false = OpSpecConstantFalse %bool
       %true = OpSpecConstantTrue %bool
        %one = OpConstant %u32 1
    %myconst = OpSpecConstantOp %u32 Not %one
     %voidfn = OpTypeFunction %void
       %main = OpFunction %void None %voidfn
 %main_entry = OpLabel
          %1 = OpCopyObject %u32 %myconst
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %1:u32 = spirv.not<u32> 1u
  %myconst:u32 = override %1
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    %4:u32 = let %myconst
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Var_OpSpecConstantOp_FConvert) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpCapability Float16
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpName %myconst "myconst"
       %void = OpTypeVoid
        %f32 = OpTypeFloat 32
        %f16 = OpTypeFloat 16
        %one = OpConstant %f16 1.0
    %myconst = OpSpecConstantOp %f32 FConvert %one
     %voidfn = OpTypeFunction %void
       %main = OpFunction %void None %voidfn
 %main_entry = OpLabel
          %1 = OpCopyObject %f32 %myconst
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %1:f32 = convert 1.0h
  %myconst:f32 = override %1
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    %4:f32 = let %myconst
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Var_OpSpecConstantOp_SNegate_ResultI32_i32) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpName %myconst "myconst"
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
        %one = OpConstant %i32 1
    %myconst = OpSpecConstantOp %i32 SNegate %one
     %voidfn = OpTypeFunction %void
       %main = OpFunction %void None %voidfn
 %main_entry = OpLabel
          %1 = OpCopyObject %i32 %myconst
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %1:i32 = spirv.s_negate<i32> 1i
  %myconst:i32 = override %1
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    %4:i32 = let %myconst
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Var_OpSpecConstantOp_SNegate_ResultI32_u32) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpName %myconst "myconst"
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
        %u32 = OpTypeInt 32 0
        %one = OpConstant %u32 1
    %myconst = OpSpecConstantOp %i32 SNegate %one
     %voidfn = OpTypeFunction %void
       %main = OpFunction %void None %voidfn
 %main_entry = OpLabel
          %1 = OpCopyObject %i32 %myconst
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %1:i32 = spirv.s_negate<i32> 1u
  %myconst:i32 = override %1
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    %4:i32 = let %myconst
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Var_OpSpecConstantOp_SNegate_ResultU32_i32) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpName %myconst "myconst"
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
        %u32 = OpTypeInt 32 0
        %one = OpConstant %i32 1
    %myconst = OpSpecConstantOp %u32 SNegate %one
     %voidfn = OpTypeFunction %void
       %main = OpFunction %void None %voidfn
 %main_entry = OpLabel
          %1 = OpCopyObject %u32 %myconst
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %1:u32 = spirv.s_negate<u32> 1i
  %myconst:u32 = override %1
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    %4:u32 = let %myconst
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Var_OpSpecConstantOp_SNegate_ResultU32_u32) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpName %myconst "myconst"
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
        %u32 = OpTypeInt 32 0
        %one = OpConstant %u32 1
    %myconst = OpSpecConstantOp %u32 SNegate %one
     %voidfn = OpTypeFunction %void
       %main = OpFunction %void None %voidfn
 %main_entry = OpLabel
          %1 = OpCopyObject %u32 %myconst
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %1:u32 = spirv.s_negate<u32> 1u
  %myconst:u32 = override %1
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    %4:u32 = let %myconst
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Var_OpSpecConstantOp_IAdd_ResultI32_i32_i32) {
    // All signed
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpName %myconst "myconst"
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
    %myconst = OpSpecConstantOp %i32 IAdd %one %two
     %voidfn = OpTypeFunction %void
       %main = OpFunction %void None %voidfn
 %main_entry = OpLabel
          %1 = OpCopyObject %i32 %myconst
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %1:i32 = spirv.add<i32> 1i, 2i
  %myconst:i32 = override %1
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    %4:i32 = let %myconst
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Var_OpSpecConstantOp_IAdd_ResultI32_i32_u32) {
    // Last arg different
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpName %myconst "myconst"
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
        %u32 = OpTypeInt 32 0
        %one = OpConstant %i32 1
        %two = OpConstant %u32 2
    %myconst = OpSpecConstantOp %i32 IAdd %one %two
     %voidfn = OpTypeFunction %void
       %main = OpFunction %void None %voidfn
 %main_entry = OpLabel
          %1 = OpCopyObject %i32 %myconst
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %1:i32 = spirv.add<i32> 1i, 2u
  %myconst:i32 = override %1
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    %4:i32 = let %myconst
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Var_OpSpecConstantOp_IAdd_ResultI32_u32_i32) {
    // Second arg different
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpName %myconst "myconst"
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
        %u32 = OpTypeInt 32 0
        %one = OpConstant %u32 1
        %two = OpConstant %i32 2
    %myconst = OpSpecConstantOp %i32 IAdd %one %two
     %voidfn = OpTypeFunction %void
       %main = OpFunction %void None %voidfn
 %main_entry = OpLabel
          %1 = OpCopyObject %i32 %myconst
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %1:i32 = spirv.add<i32> 1u, 2i
  %myconst:i32 = override %1
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    %4:i32 = let %myconst
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Var_OpSpecConstantOp_IAdd_ResultU32_i32_i32) {
    // Result different
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpName %myconst "myconst"
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
        %u32 = OpTypeInt 32 0
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
    %myconst = OpSpecConstantOp %u32 IAdd %one %two
     %voidfn = OpTypeFunction %void
       %main = OpFunction %void None %voidfn
 %main_entry = OpLabel
          %1 = OpCopyObject %u32 %myconst
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %1:u32 = spirv.add<u32> 1i, 2i
  %myconst:u32 = override %1
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    %4:u32 = let %myconst
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Var_OpSpecConstantOp_ISub_ResultI32_i32_i32) {
    // All signed
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpName %myconst "myconst"
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
    %myconst = OpSpecConstantOp %i32 ISub %one %two
     %voidfn = OpTypeFunction %void
       %main = OpFunction %void None %voidfn
 %main_entry = OpLabel
          %1 = OpCopyObject %i32 %myconst
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %1:i32 = spirv.sub<i32> 1i, 2i
  %myconst:i32 = override %1
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    %4:i32 = let %myconst
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Var_OpSpecConstantOp_ISub_ResultI32_i32_u32) {
    // Last arg different
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpName %myconst "myconst"
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
        %u32 = OpTypeInt 32 0
        %one = OpConstant %i32 1
        %two = OpConstant %u32 2
    %myconst = OpSpecConstantOp %i32 ISub %one %two
     %voidfn = OpTypeFunction %void
       %main = OpFunction %void None %voidfn
 %main_entry = OpLabel
          %1 = OpCopyObject %i32 %myconst
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %1:i32 = spirv.sub<i32> 1i, 2u
  %myconst:i32 = override %1
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    %4:i32 = let %myconst
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Var_OpSpecConstantOp_ISub_ResultI32_u32_i32) {
    // Second arg different
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpName %myconst "myconst"
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
        %u32 = OpTypeInt 32 0
        %one = OpConstant %u32 1
        %two = OpConstant %i32 2
    %myconst = OpSpecConstantOp %i32 ISub %one %two
     %voidfn = OpTypeFunction %void
       %main = OpFunction %void None %voidfn
 %main_entry = OpLabel
          %1 = OpCopyObject %i32 %myconst
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %1:i32 = spirv.sub<i32> 1u, 2i
  %myconst:i32 = override %1
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    %4:i32 = let %myconst
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Var_OpSpecConstantOp_ISub_ResultU32_i32_i32) {
    // Result different
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpName %myconst "myconst"
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
        %u32 = OpTypeInt 32 0
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
    %myconst = OpSpecConstantOp %u32 ISub %one %two
     %voidfn = OpTypeFunction %void
       %main = OpFunction %void None %voidfn
 %main_entry = OpLabel
          %1 = OpCopyObject %u32 %myconst
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %1:u32 = spirv.sub<u32> 1i, 2i
  %myconst:u32 = override %1
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    %4:u32 = let %myconst
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Var_OpSpecConstantOp_IMul_ResultI32_i32_i32) {
    // All signed
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpName %myconst "myconst"
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
    %myconst = OpSpecConstantOp %i32 IMul %one %two
     %voidfn = OpTypeFunction %void
       %main = OpFunction %void None %voidfn
 %main_entry = OpLabel
          %1 = OpCopyObject %i32 %myconst
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %1:i32 = spirv.mul<i32> 1i, 2i
  %myconst:i32 = override %1
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    %4:i32 = let %myconst
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Var_OpSpecConstantOp_IMul_ResultI32_i32_u32) {
    // Last arg different
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpName %myconst "myconst"
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
        %u32 = OpTypeInt 32 0
        %one = OpConstant %i32 1
        %two = OpConstant %u32 2
    %myconst = OpSpecConstantOp %i32 IMul %one %two
     %voidfn = OpTypeFunction %void
       %main = OpFunction %void None %voidfn
 %main_entry = OpLabel
          %1 = OpCopyObject %i32 %myconst
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %1:i32 = spirv.mul<i32> 1i, 2u
  %myconst:i32 = override %1
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    %4:i32 = let %myconst
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Var_OpSpecConstantOp_IMul_ResultI32_u32_i32) {
    // Second arg different
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpName %myconst "myconst"
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
        %u32 = OpTypeInt 32 0
        %one = OpConstant %u32 1
        %two = OpConstant %i32 2
    %myconst = OpSpecConstantOp %i32 IMul %one %two
     %voidfn = OpTypeFunction %void
       %main = OpFunction %void None %voidfn
 %main_entry = OpLabel
          %1 = OpCopyObject %i32 %myconst
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %1:i32 = spirv.mul<i32> 1u, 2i
  %myconst:i32 = override %1
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    %4:i32 = let %myconst
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Var_OpSpecConstantOp_IMul_ResultU32_i32_i32) {
    // Result different
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpName %myconst "myconst"
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
        %u32 = OpTypeInt 32 0
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
    %myconst = OpSpecConstantOp %u32 IMul %one %two
     %voidfn = OpTypeFunction %void
       %main = OpFunction %void None %voidfn
 %main_entry = OpLabel
          %1 = OpCopyObject %u32 %myconst
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %1:u32 = spirv.mul<u32> 1i, 2i
  %myconst:u32 = override %1
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    %4:u32 = let %myconst
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Var_OpSpecConstantOp_SDiv) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpName %myconst "myconst"
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
        %u32 = OpTypeInt 32 0
        %one = OpConstant %i32 1
        %two = OpConstant %u32 2
    %myconst = OpSpecConstantOp %u32 SDiv %one %two
     %voidfn = OpTypeFunction %void
       %main = OpFunction %void None %voidfn
 %main_entry = OpLabel
          %1 = OpCopyObject %u32 %myconst
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %1:u32 = spirv.s_div<u32> 1i, 2u
  %myconst:u32 = override %1
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    %4:u32 = let %myconst
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Var_OpSpecConstantOp_UDiv) {
    // OpUDiv requires signedness 0 for both args and the result type.
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpName %myconst "myconst"
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
        %u32 = OpTypeInt 32 0
        %one = OpConstant %u32 1
        %two = OpConstant %u32 2
    %myconst = OpSpecConstantOp %u32 UDiv %one %two
     %voidfn = OpTypeFunction %void
       %main = OpFunction %void None %voidfn
 %main_entry = OpLabel
          %1 = OpCopyObject %u32 %myconst
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %1:u32 = div 1u, 2u
  %myconst:u32 = override %1
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    %4:u32 = let %myconst
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Var_OpSpecConstantOp_UMod) {
    // SPIR-V UMod requires arguments and result type to be unsigned.
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpName %myconst "myconst"
       %void = OpTypeVoid
        %u32 = OpTypeInt 32 0
        %one = OpConstant %u32 1
        %two = OpConstant %u32 2
    %myconst = OpSpecConstantOp %u32 UMod %one %two
     %voidfn = OpTypeFunction %void
       %main = OpFunction %void None %voidfn
 %main_entry = OpLabel
          %1 = OpCopyObject %u32 %myconst
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %1:u32 = mod 1u, 2u
  %myconst:u32 = override %1
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    %4:u32 = let %myconst
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Var_OpSpecConstantOp_SMod) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpName %myconst "myconst"
       %void = OpTypeVoid
        %u32 = OpTypeInt 32 0
        %i32 = OpTypeInt 32 1
        %one = OpConstant %u32 1
        %two = OpConstant %i32 2
    %myconst = OpSpecConstantOp %u32 SMod %one %two
     %voidfn = OpTypeFunction %void
       %main = OpFunction %void None %voidfn
 %main_entry = OpLabel
          %1 = OpCopyObject %u32 %myconst
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %1:u32 = spirv.s_mod<u32> 1u, 2i
  %myconst:u32 = override %1
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    %4:u32 = let %myconst
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Var_OpSpecConstantOp_SRem) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpName %myconst "myconst"
       %void = OpTypeVoid
        %u32 = OpTypeInt 32 0
        %i32 = OpTypeInt 32 1
        %one = OpConstant %i32 1
        %two = OpConstant %u32 2
    %myconst = OpSpecConstantOp %u32 SRem %one %two
     %voidfn = OpTypeFunction %void
       %main = OpFunction %void None %voidfn
 %main_entry = OpLabel
          %1 = OpCopyObject %u32 %myconst
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %1:u32 = spirv.s_mod<u32> 1i, 2u
  %myconst:u32 = override %1
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    %4:u32 = let %myconst
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Var_OpSpecConstantOp_BitwiseAnd) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpName %myconst "myconst"
       %void = OpTypeVoid
        %u32 = OpTypeInt 32 0
        %i32 = OpTypeInt 32 1
        %one = OpConstant %i32 1
        %two = OpConstant %u32 2
    %myconst = OpSpecConstantOp %u32 BitwiseAnd %one %two
     %voidfn = OpTypeFunction %void
       %main = OpFunction %void None %voidfn
 %main_entry = OpLabel
          %1 = OpCopyObject %u32 %myconst
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %1:u32 = spirv.bitwise_and<u32> 1i, 2u
  %myconst:u32 = override %1
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    %4:u32 = let %myconst
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Var_OpSpecConstantOp_BitwiseOr) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpName %myconst "myconst"
       %void = OpTypeVoid
        %u32 = OpTypeInt 32 0
        %i32 = OpTypeInt 32 1
        %one = OpConstant %i32 1
        %two = OpConstant %u32 2
    %myconst = OpSpecConstantOp %u32 BitwiseOr %one %two
     %voidfn = OpTypeFunction %void
       %main = OpFunction %void None %voidfn
 %main_entry = OpLabel
          %1 = OpCopyObject %u32 %myconst
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %1:u32 = spirv.bitwise_or<u32> 1i, 2u
  %myconst:u32 = override %1
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    %4:u32 = let %myconst
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Var_OpSpecConstantOp_BitwiseXor) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpName %myconst "myconst"
       %void = OpTypeVoid
        %u32 = OpTypeInt 32 0
        %i32 = OpTypeInt 32 1
        %one = OpConstant %i32 1
        %two = OpConstant %u32 2
    %myconst = OpSpecConstantOp %u32 BitwiseXor %one %two
     %voidfn = OpTypeFunction %void
       %main = OpFunction %void None %voidfn
 %main_entry = OpLabel
          %1 = OpCopyObject %u32 %myconst
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %1:u32 = spirv.bitwise_xor<u32> 1i, 2u
  %myconst:u32 = override %1
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    %4:u32 = let %myconst
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Var_OpSpecConstantOp_IEqual) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpName %myconst "myconst"
       %void = OpTypeVoid
       %bool = OpTypeBool
        %u32 = OpTypeInt 32 0
        %i32 = OpTypeInt 32 1
        %one = OpConstant %i32 1
        %two = OpConstant %u32 2
    %myconst = OpSpecConstantOp %bool IEqual %one %two
     %voidfn = OpTypeFunction %void
       %main = OpFunction %void None %voidfn
 %main_entry = OpLabel
          %1 = OpCopyObject %bool %myconst
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %1:bool = spirv.equal 1i, 2u
  %myconst:bool = override %1
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    %4:bool = let %myconst
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Var_OpSpecConstantOp_INotEqual) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpName %myconst "myconst"
       %void = OpTypeVoid
       %bool = OpTypeBool
        %u32 = OpTypeInt 32 0
        %i32 = OpTypeInt 32 1
        %one = OpConstant %i32 1
        %two = OpConstant %u32 2
    %myconst = OpSpecConstantOp %bool INotEqual %one %two
     %voidfn = OpTypeFunction %void
       %main = OpFunction %void None %voidfn
 %main_entry = OpLabel
          %1 = OpCopyObject %bool %myconst
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %1:bool = spirv.not_equal 1i, 2u
  %myconst:bool = override %1
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    %4:bool = let %myconst
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Var_OpSpecConstantOp_SGreaterThan) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpName %myconst "myconst"
       %void = OpTypeVoid
       %bool = OpTypeBool
        %u32 = OpTypeInt 32 0
        %i32 = OpTypeInt 32 1
        %one = OpConstant %i32 1
        %two = OpConstant %u32 2
    %myconst = OpSpecConstantOp %bool SGreaterThan %one %two
     %voidfn = OpTypeFunction %void
       %main = OpFunction %void None %voidfn
 %main_entry = OpLabel
          %1 = OpCopyObject %bool %myconst
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %1:bool = spirv.s_greater_than 1i, 2u
  %myconst:bool = override %1
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    %4:bool = let %myconst
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Var_OpSpecConstantOp_SGreaterThanEqual) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpName %myconst "myconst"
       %void = OpTypeVoid
       %bool = OpTypeBool
        %u32 = OpTypeInt 32 0
        %i32 = OpTypeInt 32 1
        %one = OpConstant %i32 1
        %two = OpConstant %u32 2
    %myconst = OpSpecConstantOp %bool SGreaterThanEqual %one %two
     %voidfn = OpTypeFunction %void
       %main = OpFunction %void None %voidfn
 %main_entry = OpLabel
          %1 = OpCopyObject %bool %myconst
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %1:bool = spirv.s_greater_than_equal 1i, 2u
  %myconst:bool = override %1
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    %4:bool = let %myconst
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Var_OpSpecConstantOp_SLessThan) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpName %myconst "myconst"
       %void = OpTypeVoid
       %bool = OpTypeBool
        %u32 = OpTypeInt 32 0
        %i32 = OpTypeInt 32 1
        %one = OpConstant %i32 1
        %two = OpConstant %u32 2
    %myconst = OpSpecConstantOp %bool SLessThan %one %two
     %voidfn = OpTypeFunction %void
       %main = OpFunction %void None %voidfn
 %main_entry = OpLabel
          %1 = OpCopyObject %bool %myconst
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %1:bool = spirv.s_less_than 1i, 2u
  %myconst:bool = override %1
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    %4:bool = let %myconst
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Var_OpSpecConstantOp_SLessThanEqual) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpName %myconst "myconst"
       %void = OpTypeVoid
       %bool = OpTypeBool
        %u32 = OpTypeInt 32 0
        %i32 = OpTypeInt 32 1
        %one = OpConstant %i32 1
        %two = OpConstant %u32 2
    %myconst = OpSpecConstantOp %bool SLessThanEqual %one %two
     %voidfn = OpTypeFunction %void
       %main = OpFunction %void None %voidfn
 %main_entry = OpLabel
          %1 = OpCopyObject %bool %myconst
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %1:bool = spirv.s_less_than_equal 1i, 2u
  %myconst:bool = override %1
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    %4:bool = let %myconst
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Var_OpSpecConstantOp_UGreaterThan) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpName %myconst "myconst"
       %void = OpTypeVoid
       %bool = OpTypeBool
        %u32 = OpTypeInt 32 0
        %i32 = OpTypeInt 32 1
        %one = OpConstant %i32 1
        %two = OpConstant %u32 2
    %myconst = OpSpecConstantOp %bool UGreaterThan %one %two
     %voidfn = OpTypeFunction %void
       %main = OpFunction %void None %voidfn
 %main_entry = OpLabel
          %1 = OpCopyObject %bool %myconst
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %1:bool = spirv.u_greater_than 1i, 2u
  %myconst:bool = override %1
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    %4:bool = let %myconst
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Var_OpSpecConstantOp_UGreaterThanEqual) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpName %myconst "myconst"
       %void = OpTypeVoid
       %bool = OpTypeBool
        %u32 = OpTypeInt 32 0
        %i32 = OpTypeInt 32 1
        %one = OpConstant %i32 1
        %two = OpConstant %u32 2
    %myconst = OpSpecConstantOp %bool UGreaterThanEqual %one %two
     %voidfn = OpTypeFunction %void
       %main = OpFunction %void None %voidfn
 %main_entry = OpLabel
          %1 = OpCopyObject %bool %myconst
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %1:bool = spirv.u_greater_than_equal 1i, 2u
  %myconst:bool = override %1
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    %4:bool = let %myconst
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Var_OpSpecConstantOp_ULessThan) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpName %myconst "myconst"
       %void = OpTypeVoid
       %bool = OpTypeBool
        %u32 = OpTypeInt 32 0
        %i32 = OpTypeInt 32 1
        %one = OpConstant %i32 1
        %two = OpConstant %u32 2
    %myconst = OpSpecConstantOp %bool ULessThan %one %two
     %voidfn = OpTypeFunction %void
       %main = OpFunction %void None %voidfn
 %main_entry = OpLabel
          %1 = OpCopyObject %bool %myconst
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %1:bool = spirv.u_less_than 1i, 2u
  %myconst:bool = override %1
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    %4:bool = let %myconst
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Var_OpSpecConstantOp_ULessThanEqual) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpName %myconst "myconst"
       %void = OpTypeVoid
       %bool = OpTypeBool
        %u32 = OpTypeInt 32 0
        %i32 = OpTypeInt 32 1
        %one = OpConstant %i32 1
        %two = OpConstant %u32 2
    %myconst = OpSpecConstantOp %bool ULessThanEqual %one %two
     %voidfn = OpTypeFunction %void
       %main = OpFunction %void None %voidfn
 %main_entry = OpLabel
          %1 = OpCopyObject %bool %myconst
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %1:bool = spirv.u_less_than_equal 1i, 2u
  %myconst:bool = override %1
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    %4:bool = let %myconst
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Var_OpSpecConstantOp_ShiftLeftLogical) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpName %myconst "myconst"
       %void = OpTypeVoid
        %u32 = OpTypeInt 32 0
        %i32 = OpTypeInt 32 1
        %one = OpConstant %i32 1
        %two = OpConstant %u32 2
    %myconst = OpSpecConstantOp %u32 ShiftLeftLogical %one %two
     %voidfn = OpTypeFunction %void
       %main = OpFunction %void None %voidfn
 %main_entry = OpLabel
          %1 = OpCopyObject %u32 %myconst
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %1:u32 = spirv.shift_left_logical<u32> 1i, 2u
  %myconst:u32 = override %1
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    %4:u32 = let %myconst
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Var_OpSpecConstantOp_ShiftRightLogical) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpName %myconst "myconst"
       %void = OpTypeVoid
        %u32 = OpTypeInt 32 0
        %i32 = OpTypeInt 32 1
        %one = OpConstant %i32 1
        %two = OpConstant %u32 2
    %myconst = OpSpecConstantOp %u32 ShiftRightLogical %one %two
     %voidfn = OpTypeFunction %void
       %main = OpFunction %void None %voidfn
 %main_entry = OpLabel
          %1 = OpCopyObject %u32 %myconst
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %1:u32 = spirv.shift_right_logical<u32> 1i, 2u
  %myconst:u32 = override %1
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    %4:u32 = let %myconst
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Var_OpSpecConstantOp_ShiftRightArithmetic) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpName %myconst "myconst"
       %void = OpTypeVoid
        %u32 = OpTypeInt 32 0
        %i32 = OpTypeInt 32 1
        %one = OpConstant %i32 1
        %two = OpConstant %u32 2
    %myconst = OpSpecConstantOp %u32 ShiftRightArithmetic %one %two
     %voidfn = OpTypeFunction %void
       %main = OpFunction %void None %voidfn
 %main_entry = OpLabel
          %1 = OpCopyObject %u32 %myconst
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %1:u32 = spirv.shift_right_arithmetic<u32> 1i, 2u
  %myconst:u32 = override %1
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    %4:u32 = let %myconst
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Var_OpSpecConstantOp_CompositeExtract) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpName %myconst "myconst"
       %void = OpTypeVoid
        %u32 = OpTypeInt 32 0
        %i32 = OpTypeInt 32 1
      %vec2i = OpTypeVector %i32 2
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
    %one_two = OpConstantComposite %vec2i %one %two
    %myconst = OpSpecConstantOp %i32 CompositeExtract %one_two 1
     %voidfn = OpTypeFunction %void
       %main = OpFunction %void None %voidfn
 %main_entry = OpLabel
          %1 = OpCopyObject %i32 %myconst
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %1:i32 = access vec2<i32>(1i, 2i), 1u
  %myconst:i32 = override %1
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    %4:i32 = let %myconst
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Var_OpSpecConstantOp_Select_CondScalar_ArgsScalar) {
    // In SPIR-V the arg types must be the same as the result type.
    // WGSL only supports overrides with scalar type, so only test the
    // case where the result type is scalar.
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpName %myconst "myconst"
       %void = OpTypeVoid
       %bool = OpTypeBool
        %u32 = OpTypeInt 32 0
        %i32 = OpTypeInt 32 1
       %true = OpConstantTrue %bool
      %false = OpConstantFalse %bool
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
    %myconst = OpSpecConstantOp %i32 Select %true %one %two
     %voidfn = OpTypeFunction %void
       %main = OpFunction %void None %voidfn
 %main_entry = OpLabel
          %1 = OpCopyObject %i32 %myconst
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %1:i32 = spirv.select true, 1i, 2i
  %myconst:i32 = override %1
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    %4:i32 = let %myconst
    ret
  }
}
)");
}

// In the case of all literals, SPIR-V opt treats the `OpSpecConstantComposite` as an
// `OpConstantComposite` so it appears in the constant manager already. This then needs no handling
// on our side.
TEST_F(SpirvParserTest, Var_OpSpecConstantComposite_vec2_literals) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpName %1 "myconst"
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
        %v2i = OpTypeVector %i32 2
        %one = OpConstant %i32 1
        %two = OpConstant %i32 2
          %1 = OpSpecConstantComposite %v2i %one %two
     %voidfn = OpTypeFunction %void
       %main = OpFunction %void None %voidfn
 %main_entry = OpLabel
          %2 = OpIAdd %v2i %1 %1
               OpReturn
               OpFunctionEnd
)",
              R"(
%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B1: {
    %2:vec2<i32> = spirv.add<i32> vec2<i32>(1i, 2i), vec2<i32>(1i, 2i)
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Var_OpSpecConstantComposite_vec2_SpecConstants) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpName %1 "myconst"
               OpDecorate %one SpecId 1
               OpDecorate %two SpecId 2
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
        %v2i = OpTypeVector %i32 2
        %one = OpSpecConstant %i32 1
        %two = OpSpecConstant %i32 2
          %1 = OpSpecConstantComposite %v2i %one %two
     %voidfn = OpTypeFunction %void
       %main = OpFunction %void None %voidfn
 %main_entry = OpLabel
          %2 = OpIAdd %v2i %1 %1
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %1:i32 = override 1i @id(1)
  %2:i32 = override 2i @id(2)
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    %4:vec2<i32> = construct %1, %2
    %5:vec2<i32> = spirv.add<i32> %4, %4
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Var_OpSpecConstantComposite_vec4_Mixed) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpName %1 "myconst"
               OpDecorate %one SpecId 1
               OpDecorate %three SpecId 3
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
        %v4i = OpTypeVector %i32 4
        %one = OpSpecConstant %i32 1
        %two = OpConstant %i32 2
      %three = OpSpecConstant %i32 3
       %four = OpConstant %i32 4
          %1 = OpSpecConstantComposite %v4i %one %two %three %four
     %voidfn = OpTypeFunction %void
       %main = OpFunction %void None %voidfn
 %main_entry = OpLabel
          %2 = OpIAdd %v4i %1 %1
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %1:i32 = override 1i @id(1)
  %2:i32 = override 3i @id(3)
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    %4:vec4<i32> = construct %1, 2i, %2, 4i
    %5:vec4<i32> = spirv.add<i32> %4, %4
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Var_OpSpecConstantComposite_mat3x4_Mixed) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpName %1 "myconst"
               OpDecorate %one SpecId 1
               OpDecorate %three SpecId 3
       %void = OpTypeVoid
        %f32 = OpTypeFloat 32
        %v3f = OpTypeVector %f32 3
     %mat4x3 = OpTypeMatrix %v3f 4
        %one = OpSpecConstant %f32 1
        %two = OpConstant %f32 2
      %three = OpSpecConstant %f32 3
       %four = OpConstant %f32 4
          %1 = OpSpecConstantComposite %v3f %one %two %three
          %2 = OpSpecConstantComposite %mat4x3 %1 %1 %1 %1
     %voidfn = OpTypeFunction %void
       %main = OpFunction %void None %voidfn
 %main_entry = OpLabel
          %3 = OpMatrixTimesScalar %mat4x3 %2 %four
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %1:f32 = override 1.0f @id(1)
  %2:f32 = override 3.0f @id(3)
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    %4:vec3<f32> = construct %1, 2.0f, %2
    %5:mat4x3<f32> = construct %4, %4, %4, %4
    %6:mat4x3<f32> = mul %5, 4.0f
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Var_OpSpecConstantComposite_array_Mixed) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpName %1 "myconst"
               OpDecorate %one SpecId 1
               OpDecorate %three SpecId 3
       %void = OpTypeVoid
       %uint = OpTypeInt 32 0
     %uint_2 = OpConstant %uint 2
     %uint_3 = OpConstant %uint 3
        %f32 = OpTypeFloat 32
        %ary = OpTypeArray %f32 %uint_3
        %ptr = OpTypePointer Function %f32
     %fn_ptr = OpTypePointer Function %ary
        %one = OpSpecConstant %f32 1
        %two = OpConstant %f32 2
      %three = OpSpecConstant %f32 3
          %1 = OpSpecConstantComposite %ary %one %two %three
     %voidfn = OpTypeFunction %void
       %main = OpFunction %void None %voidfn
 %main_entry = OpLabel
  %indexable = OpVariable %fn_ptr Function
               OpStore %indexable %1
         %20 = OpAccessChain %ptr %indexable %uint_2
         %21 = OpLoad %f32 %20
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %1:f32 = override 1.0f @id(1)
  %2:f32 = override 3.0f @id(3)
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    %4:ptr<function, array<f32, 3>, read_write> = var undef
    %5:array<f32, 3> = construct %1, 2.0f, %2
    store %4, %5
    %6:ptr<function, f32, read_write> = access %4, 2u
    %7:f32 = load %6
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Var_OpSpecConstantComposite_struct_Mixed) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpExtension "SPV_KHR_storage_buffer_storage_class"
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %1 "main"
               OpExecutionMode %1 LocalSize 1 1 1
               OpDecorate %str Block
               OpMemberDecorate %str 0 Offset 0
       %void = OpTypeVoid
       %uint = OpTypeInt 32 0
        %int = OpTypeInt 32 1
        %f32 = OpTypeFloat 32
        %str = OpTypeStruct %uint %f32
    %ptr_str = OpTypePointer Function %str
    %ptr_f32 = OpTypePointer Function %f32
      %int_1 = OpConstant %int 1
        %one = OpSpecConstant %uint 1
        %two = OpConstant %f32 2
          %5 = OpTypeFunction %void
          %2 = OpSpecConstantComposite %str %one %two
          %1 = OpFunction %void None %5
         %11 = OpLabel
          %b = OpVariable %ptr_str Function
               OpStore %b %2
         %24 = OpAccessChain %ptr_f32 %b %int_1
         %17 = OpLoad %f32 %24
         %25 = OpFAdd %f32 %17 %17
               OpReturn
               OpFunctionEnd
)",
              R"(
tint_symbol_2 = struct @align(4) {
  tint_symbol:u32 @offset(0)
  tint_symbol_1:f32 @offset(4)
}

$B1: {  # root
  %1:u32 = override 1u
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    %3:ptr<function, tint_symbol_2, read_write> = var undef
    %4:tint_symbol_2 = construct %1, 2.0f
    store %3, %4
    %5:ptr<function, f32, read_write> = access %3, 1i
    %6:f32 = load %5
    %7:f32 = add %6, %6
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, Var_OpSpecConstantComposite_Extracted) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpName %1 "myconst"
               OpDecorate %one SpecId 1
               OpDecorate %two SpecId 2
       %void = OpTypeVoid
        %i32 = OpTypeInt 32 1
        %v2i = OpTypeVector %i32 2
        %one = OpSpecConstant %i32 1
        %two = OpSpecConstant %i32 2
          %1 = OpSpecConstantComposite %v2i %one %two
        %foo = OpSpecConstantOp %i32 CompositeExtract %1 1
     %voidfn = OpTypeFunction %void
       %main = OpFunction %void None %voidfn
 %main_entry = OpLabel
           %3 = OpIAdd %i32 %foo %foo
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %1:i32 = override 1i @id(1)
  %2:i32 = override 2i @id(2)
  %3:vec2<i32> = construct %1, %2
  %4:i32 = access %3, 1u
  %5:i32 = override %4
}

%main = @compute @workgroup_size(1u, 1u, 1u) func():void {
  $B2: {
    %7:i32 = spirv.add<i32> %5, %5
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, InterpolationFlatNoLocation) {
    EXPECT_IR(R"(
             OpCapability Shader
             OpCapability SampleRateShading
             OpMemoryModel Logical Simple
             OpEntryPoint Fragment %3 "main" %gl_SampleID
             OpExecutionMode %3 OriginUpperLeft
             OpDecorate %gl_SampleID BuiltIn SampleId
             OpDecorate %gl_SampleID Flat
     %void = OpTypeVoid
        %5 = OpTypeFunction %void
    %float = OpTypeFloat 32
     %uint = OpTypeInt 32 0
      %int = OpTypeInt 32 1
%_ptr_Input_uint = OpTypePointer Input %uint
%gl_SampleID = OpVariable %_ptr_Input_uint Input
        %3 = OpFunction %void None %5
       %10 = OpLabel
        %2 = OpLoad %uint %gl_SampleID
             OpReturn
             OpFunctionEnd
)",
              R"(
$B1: {  # root
  %1:ptr<__in, u32, read> = var undef @builtin(sample_index)
}

%main = @fragment func():void {
  $B2: {
    undef = phony %1
    %3:u32 = load %1
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, VarMarkedFragDepth) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpCapability SampleRateShading
               OpMemoryModel Logical Simple
               OpEntryPoint Fragment %2 "main" %gl_FragDepth
               OpExecutionMode %2 OriginUpperLeft
               OpDecorate %gl_FragDepth BuiltIn FragDepth
       %void = OpTypeVoid
          %4 = OpTypeFunction %void
      %float = OpTypeFloat 32
    %float_0 = OpConstant %float 0
%_ptr_Output_float = OpTypePointer Output %float
%gl_FragDepth = OpVariable %_ptr_Output_float Output %float_0
          %2 = OpFunction %void None %4
         %29 = OpLabel
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %1:ptr<__out, f32, read_write> = var 0.0f @builtin(frag_depth)
}

%main = @fragment func():void {
  $B2: {
    undef = phony %1
    ret
  }
}
)");
}

TEST_F(SpirvParserTest, PrimitiveIndex) {
    EXPECT_IR(R"(
               OpCapability Shader
               OpCapability Geometry
               OpMemoryModel Logical GLSL450
               OpEntryPoint Fragment %main "main" %main_primitive_id_Input %main_loc0_Output
               OpExecutionMode %main OriginUpperLeft
               OpName %main_primitive_id_Input "main_primitive_id_Input"
               OpName %main_loc0_Output "main_loc0_Output"
               OpName %main_inner "main_inner"
               OpName %prim_id "prim_id"
               OpName %main "main"
               OpDecorate %main_primitive_id_Input Flat
               OpDecorate %main_primitive_id_Input BuiltIn PrimitiveId
               OpDecorate %main_loc0_Output Location 0
       %uint = OpTypeInt 32 0
%_ptr_Input_uint = OpTypePointer Input %uint
%main_primitive_id_Input = OpVariable %_ptr_Input_uint Input
      %float = OpTypeFloat 32
    %v4float = OpTypeVector %float 4
%_ptr_Output_v4float = OpTypePointer Output %v4float
%main_loc0_Output = OpVariable %_ptr_Output_v4float Output
         %10 = OpTypeFunction %v4float %uint
       %void = OpTypeVoid
         %16 = OpTypeFunction %void
 %main_inner = OpFunction %v4float None %10
    %prim_id = OpFunctionParameter %uint
         %11 = OpLabel
         %12 = OpConvertUToF %float %prim_id
         %13 = OpCompositeConstruct %v4float %12 %12 %12 %12
               OpReturnValue %13
               OpFunctionEnd
       %main = OpFunction %void None %16
         %17 = OpLabel
         %18 = OpLoad %uint %main_primitive_id_Input None
         %19 = OpFunctionCall %v4float %main_inner %18
               OpStore %main_loc0_Output %19 None
               OpReturn
               OpFunctionEnd
)",
              R"(
$B1: {  # root
  %main_primitive_id_Input:ptr<__in, u32, read> = var undef @builtin(primitive_index)
  %main_loc0_Output:ptr<__out, vec4<f32>, read_write> = var undef @location(0)
}

%main_inner = func(%prim_id:u32):vec4<f32> {
  $B2: {
    %5:f32 = spirv.convert_u_to_f<f32> %prim_id
    %6:vec4<f32> = construct %5, %5, %5, %5
    ret %6
  }
}
%main = @fragment func():void {
  $B3: {
    undef = phony %main_loc0_Output
    undef = phony %main_primitive_id_Input
    %8:u32 = load %main_primitive_id_Input
    %9:vec4<f32> = call %main_inner, %8
    store %main_loc0_Output, %9
    ret
  }
}
)");
}

}  // namespace
}  // namespace tint::spirv::reader
