[tint][exe] Add --use-storage-input-output-16 flag

Add an E2E test that uses it, to show the polyfill working.

Bug: tint:2161
Change-Id: I445078579b5aefcbbc688af2a6c79e844549ead6
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/173707
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
diff --git a/src/tint/cmd/tint/main.cc b/src/tint/cmd/tint/main.cc
index 85d8d3e..4d18bb9 100644
--- a/src/tint/cmd/tint/main.cc
+++ b/src/tint/cmd/tint/main.cc
@@ -178,6 +178,10 @@
 
     tint::Vector<std::string, 4> transforms;
 
+#if TINT_BUILD_SPV_WRITER
+    bool use_storage_input_output_16 = true;
+#endif  // TINT_BULD_SPV_WRITER
+
 #if TINT_BUILD_HLSL_WRITER
     std::string fxc_path;
     std::string dxc_path;
@@ -345,6 +349,13 @@
     });
 #endif
 
+#if TINT_BUILD_SPV_WRITER
+    auto& use_storage_input_output_16 =
+        options.Add<BoolOption>("use-storage-input-output-16",
+                                "Use the StorageInputOutput16 SPIR-V capability", Default{true});
+    TINT_DEFER(opts->use_storage_input_output_16 = *use_storage_input_output_16.value);
+#endif
+
     auto& disable_wg_init = options.Add<BoolOption>(
         "disable-workgroup-init", "Disable workgroup memory zero initialization", Default{false});
     TINT_DEFER(opts->disable_workgroup_init = *disable_wg_init.value);
@@ -687,6 +698,7 @@
     tint::spirv::writer::Options gen_options;
     gen_options.disable_robustness = !options.enable_robustness;
     gen_options.disable_workgroup_init = options.disable_workgroup_init;
+    gen_options.use_storage_input_output_16 = options.use_storage_input_output_16;
     gen_options.bindings = tint::spirv::writer::GenerateBindings(program);
 
     tint::Result<tint::spirv::writer::Output> result;
diff --git a/test/tint/types/functions/shader_io/fragment_f16_io_polyfill.wgsl b/test/tint/types/functions/shader_io/fragment_f16_io_polyfill.wgsl
new file mode 100644
index 0000000..c784e9b
--- /dev/null
+++ b/test/tint/types/functions/shader_io/fragment_f16_io_polyfill.wgsl
@@ -0,0 +1,13 @@
+// flags:  --hlsl_shader_model 62 --use-storage-input-output-16=false
+enable f16;
+
+struct Outputs {
+  @location(1) a : f16,
+  @location(2) b : vec4<f16>,
+}
+
+@fragment
+fn frag_main(@location(1) loc1 : f16,
+             @location(2) loc2 : vec4<f16>) -> Outputs {
+  return Outputs(loc1 * 2, loc2 * 3);
+}
diff --git a/test/tint/types/functions/shader_io/fragment_f16_io_polyfill.wgsl.expected.dxc.hlsl b/test/tint/types/functions/shader_io/fragment_f16_io_polyfill.wgsl.expected.dxc.hlsl
new file mode 100644
index 0000000..510a946
--- /dev/null
+++ b/test/tint/types/functions/shader_io/fragment_f16_io_polyfill.wgsl.expected.dxc.hlsl
@@ -0,0 +1,25 @@
+struct Outputs {
+  float16_t a;
+  vector<float16_t, 4> b;
+};
+struct tint_symbol_1 {
+  float16_t loc1 : TEXCOORD1;
+  vector<float16_t, 4> loc2 : TEXCOORD2;
+};
+struct tint_symbol_2 {
+  float16_t a : SV_Target1;
+  vector<float16_t, 4> b : SV_Target2;
+};
+
+Outputs frag_main_inner(float16_t loc1, vector<float16_t, 4> loc2) {
+  Outputs tint_symbol_3 = {(loc1 * float16_t(2.0h)), (loc2 * float16_t(3.0h))};
+  return tint_symbol_3;
+}
+
+tint_symbol_2 frag_main(tint_symbol_1 tint_symbol) {
+  Outputs inner_result = frag_main_inner(tint_symbol.loc1, tint_symbol.loc2);
+  tint_symbol_2 wrapper_result = (tint_symbol_2)0;
+  wrapper_result.a = inner_result.a;
+  wrapper_result.b = inner_result.b;
+  return wrapper_result;
+}
diff --git a/test/tint/types/functions/shader_io/fragment_f16_io_polyfill.wgsl.expected.fxc.hlsl b/test/tint/types/functions/shader_io/fragment_f16_io_polyfill.wgsl.expected.fxc.hlsl
new file mode 100644
index 0000000..98230ce
--- /dev/null
+++ b/test/tint/types/functions/shader_io/fragment_f16_io_polyfill.wgsl.expected.fxc.hlsl
@@ -0,0 +1,27 @@
+SKIP: FAILED
+
+struct Outputs {
+  float16_t a;
+  vector<float16_t, 4> b;
+};
+struct tint_symbol_1 {
+  float16_t loc1 : TEXCOORD1;
+  vector<float16_t, 4> loc2 : TEXCOORD2;
+};
+struct tint_symbol_2 {
+  float16_t a : SV_Target1;
+  vector<float16_t, 4> b : SV_Target2;
+};
+
+Outputs frag_main_inner(float16_t loc1, vector<float16_t, 4> loc2) {
+  Outputs tint_symbol_3 = {(loc1 * float16_t(2.0h)), (loc2 * float16_t(3.0h))};
+  return tint_symbol_3;
+}
+
+tint_symbol_2 frag_main(tint_symbol_1 tint_symbol) {
+  Outputs inner_result = frag_main_inner(tint_symbol.loc1, tint_symbol.loc2);
+  tint_symbol_2 wrapper_result = (tint_symbol_2)0;
+  wrapper_result.a = inner_result.a;
+  wrapper_result.b = inner_result.b;
+  return wrapper_result;
+}
diff --git a/test/tint/types/functions/shader_io/fragment_f16_io_polyfill.wgsl.expected.glsl b/test/tint/types/functions/shader_io/fragment_f16_io_polyfill.wgsl.expected.glsl
new file mode 100644
index 0000000..598b9e9
--- /dev/null
+++ b/test/tint/types/functions/shader_io/fragment_f16_io_polyfill.wgsl.expected.glsl
@@ -0,0 +1,24 @@
+#version 310 es
+#extension GL_AMD_gpu_shader_half_float : require
+precision highp float;
+
+layout(location = 1) in float16_t loc1_1;
+layout(location = 2) in f16vec4 loc2_1;
+layout(location = 1) out float16_t a_1;
+layout(location = 2) out f16vec4 b_1;
+struct Outputs {
+  float16_t a;
+  f16vec4 b;
+};
+
+Outputs frag_main(float16_t loc1, f16vec4 loc2) {
+  Outputs tint_symbol = Outputs((loc1 * 2.0hf), (loc2 * 3.0hf));
+  return tint_symbol;
+}
+
+void main() {
+  Outputs inner_result = frag_main(loc1_1, loc2_1);
+  a_1 = inner_result.a;
+  b_1 = inner_result.b;
+  return;
+}
diff --git a/test/tint/types/functions/shader_io/fragment_f16_io_polyfill.wgsl.expected.msl b/test/tint/types/functions/shader_io/fragment_f16_io_polyfill.wgsl.expected.msl
new file mode 100644
index 0000000..c07c23c
--- /dev/null
+++ b/test/tint/types/functions/shader_io/fragment_f16_io_polyfill.wgsl.expected.msl
@@ -0,0 +1,31 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct Outputs {
+  half a;
+  half4 b;
+};
+
+struct tint_symbol_1 {
+  half loc1 [[user(locn1)]];
+  half4 loc2 [[user(locn2)]];
+};
+
+struct tint_symbol_2 {
+  half a [[color(1)]];
+  half4 b [[color(2)]];
+};
+
+Outputs frag_main_inner(half loc1, half4 loc2) {
+  Outputs const tint_symbol_3 = {.a=(loc1 * 2.0h), .b=(loc2 * 3.0h)};
+  return tint_symbol_3;
+}
+
+fragment tint_symbol_2 frag_main(tint_symbol_1 tint_symbol [[stage_in]]) {
+  Outputs const inner_result = frag_main_inner(tint_symbol.loc1, tint_symbol.loc2);
+  tint_symbol_2 wrapper_result = {};
+  wrapper_result.a = inner_result.a;
+  wrapper_result.b = inner_result.b;
+  return wrapper_result;
+}
+
diff --git a/test/tint/types/functions/shader_io/fragment_f16_io_polyfill.wgsl.expected.spvasm b/test/tint/types/functions/shader_io/fragment_f16_io_polyfill.wgsl.expected.spvasm
new file mode 100644
index 0000000..baf6ffd
--- /dev/null
+++ b/test/tint/types/functions/shader_io/fragment_f16_io_polyfill.wgsl.expected.spvasm
@@ -0,0 +1,73 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 39
+; Schema: 0
+               OpCapability Shader
+               OpCapability Float16
+               OpCapability UniformAndStorageBuffer16BitAccess
+               OpCapability StorageBuffer16BitAccess
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %frag_main "frag_main" %loc1_1 %loc2_1 %a_1 %b_1
+               OpExecutionMode %frag_main OriginUpperLeft
+               OpName %loc1_1 "loc1_1"
+               OpName %loc2_1 "loc2_1"
+               OpName %a_1 "a_1"
+               OpName %b_1 "b_1"
+               OpName %Outputs "Outputs"
+               OpMemberName %Outputs 0 "a"
+               OpMemberName %Outputs 1 "b"
+               OpName %frag_main_inner "frag_main_inner"
+               OpName %loc1 "loc1"
+               OpName %loc2 "loc2"
+               OpName %frag_main "frag_main"
+               OpDecorate %loc1_1 Location 1
+               OpDecorate %loc2_1 Location 2
+               OpDecorate %a_1 Location 1
+               OpDecorate %b_1 Location 2
+               OpMemberDecorate %Outputs 0 Offset 0
+               OpMemberDecorate %Outputs 1 Offset 8
+      %float = OpTypeFloat 32
+%_ptr_Input_float = OpTypePointer Input %float
+     %loc1_1 = OpVariable %_ptr_Input_float Input
+    %v4float = OpTypeVector %float 4
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+     %loc2_1 = OpVariable %_ptr_Input_v4float Input
+%_ptr_Output_float = OpTypePointer Output %float
+          %9 = OpConstantNull %float
+        %a_1 = OpVariable %_ptr_Output_float Output %9
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+         %12 = OpConstantNull %v4float
+        %b_1 = OpVariable %_ptr_Output_v4float Output %12
+       %half = OpTypeFloat 16
+     %v4half = OpTypeVector %half 4
+    %Outputs = OpTypeStruct %half %v4half
+         %13 = OpTypeFunction %Outputs %half %v4half
+%half_0x1p_1 = OpConstant %half 0x1p+1
+%half_0x1_8p_1 = OpConstant %half 0x1.8p+1
+       %void = OpTypeVoid
+         %26 = OpTypeFunction %void
+%frag_main_inner = OpFunction %Outputs None %13
+       %loc1 = OpFunctionParameter %half
+       %loc2 = OpFunctionParameter %v4half
+         %20 = OpLabel
+         %22 = OpFMul %half %loc1 %half_0x1p_1
+         %24 = OpVectorTimesScalar %v4half %loc2 %half_0x1_8p_1
+         %25 = OpCompositeConstruct %Outputs %22 %24
+               OpReturnValue %25
+               OpFunctionEnd
+  %frag_main = OpFunction %void None %26
+         %29 = OpLabel
+         %32 = OpLoad %float %loc1_1
+         %31 = OpFConvert %half %32
+         %34 = OpLoad %v4float %loc2_1
+         %33 = OpFConvert %v4half %34
+         %30 = OpFunctionCall %Outputs %frag_main_inner %31 %33
+         %36 = OpCompositeExtract %half %30 0
+         %35 = OpFConvert %float %36
+               OpStore %a_1 %35
+         %38 = OpCompositeExtract %v4half %30 1
+         %37 = OpFConvert %v4float %38
+               OpStore %b_1 %37
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/types/functions/shader_io/fragment_f16_io_polyfill.wgsl.expected.wgsl b/test/tint/types/functions/shader_io/fragment_f16_io_polyfill.wgsl.expected.wgsl
new file mode 100644
index 0000000..80eb8cd
--- /dev/null
+++ b/test/tint/types/functions/shader_io/fragment_f16_io_polyfill.wgsl.expected.wgsl
@@ -0,0 +1,13 @@
+enable f16;
+
+struct Outputs {
+  @location(1)
+  a : f16,
+  @location(2)
+  b : vec4<f16>,
+}
+
+@fragment
+fn frag_main(@location(1) loc1 : f16, @location(2) loc2 : vec4<f16>) -> Outputs {
+  return Outputs((loc1 * 2), (loc2 * 3));
+}