spirv-reader: polyfill scalar reflect

Fixed: tint:1018
Change-Id: I60916d6c4ac4ae8c1a88763c12acf83d19bb2e68
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/58821
Commit-Queue: David Neto <dneto@google.com>
Auto-Submit: David Neto <dneto@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Ben Clayton <bclayton@chromium.org>
diff --git a/src/reader/spirv/function.cc b/src/reader/spirv/function.cc
index 43401f5..90e63e7 100644
--- a/src/reader/spirv/function.cc
+++ b/src/reader/spirv/function.cc
@@ -3996,6 +3996,7 @@
         // WGSL does not have scalar form of the normalize builtin.
         // The answer would be 1 anyway, so return that directly.
         return {ty_.F32(), builder_.Expr(1.0f)};
+
       case GLSLstd450FaceForward: {
         // If dot(Nref, Incident) < 0, the result is Normal, otherwise -Normal.
         // Also: select(-normal,normal, Incident*Nref < 0)
@@ -4021,6 +4022,21 @@
                             builder_.Expr(0.0f))})};
       }
 
+      case GLSLstd450Reflect: {
+        // Compute  Incident - 2 * Normal * Normal * Incident
+        auto incident = MakeOperand(inst, 2);
+        auto normal = MakeOperand(inst, 3);
+        TINT_ASSERT(Reader, incident.type->Is<F32>());
+        TINT_ASSERT(Reader, normal.type->Is<F32>());
+        return {
+            ty_.F32(),
+            builder_.Sub(
+                incident.expr,
+                builder_.Mul(2.0f, builder_.Mul(normal.expr,
+                                                builder_.Mul(normal.expr,
+                                                             incident.expr))))};
+      }
+
       case GLSLstd450Refract: {
         // It's a complicated expression. Compute it in two dimensions, but
         // with a 0-valued y component in both the incident and normal vectors,
@@ -4701,6 +4717,10 @@
           // The "normal" operand expression is used twice in code generation.
           require_named_const_def(inst, 2);
           break;
+        case GLSLstd450Reflect:
+          require_named_const_def(inst, 2);  // Incident
+          require_named_const_def(inst, 3);  // Normal
+          break;
         default:
           break;
       }
diff --git a/src/reader/spirv/function_glsl_std_450_test.cc b/src/reader/spirv/function_glsl_std_450_test.cc
index 0699005..be6d5a4 100644
--- a/src/reader/spirv/function_glsl_std_450_test.cc
+++ b/src/reader/spirv/function_glsl_std_450_test.cc
@@ -717,7 +717,6 @@
                              {"FMax", "max"},  // WGSL max promises more for NaN
                              {"FMin", "min"},  // WGSL min promises more for NaN
                              {"Pow", "pow"},
-                             {"Reflect", "reflect"},
                              {"Step", "step"},
                          }));
 
@@ -1913,6 +1912,145 @@
   EXPECT_THAT(body, HasSubstr(expected));
 }
 
+TEST_F(SpvParserTest, GlslStd450_Reflect_Scalar) {
+  const auto assembly = Preamble() + R"(
+     %98 = OpFAdd %float %f1 %f1 ; has only one use
+     %99 = OpFAdd %float %f2 %f2 ; has only one use
+     %1 = OpExtInst %float %glsl Reflect %98 %99
+     OpReturn
+     OpFunctionEnd
+  )";
+  auto p = parser(test::Assemble(assembly));
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
+  auto fe = p->function_emitter(100);
+  EXPECT_TRUE(fe.EmitBody()) << p->error();
+  const auto body = ToString(p->builder(), fe.ast_body());
+  // The %99 sum only has one use.  Ensure it is evaluated only once by
+  // making a let-declaration for it, since it is the normal operand to
+  // the builtin function, and code generation uses it twice.
+  const auto* expected = R"(VariableDeclStatement{
+  VariableConst{
+    x_98
+    none
+    undefined
+    __f32
+    {
+      Binary[not set]{
+        Identifier[not set]{f1}
+        add
+        Identifier[not set]{f1}
+      }
+    }
+  }
+}
+VariableDeclStatement{
+  VariableConst{
+    x_99
+    none
+    undefined
+    __f32
+    {
+      Binary[not set]{
+        Identifier[not set]{f2}
+        add
+        Identifier[not set]{f2}
+      }
+    }
+  }
+}
+VariableDeclStatement{
+  VariableConst{
+    x_1
+    none
+    undefined
+    __f32
+    {
+      Binary[not set]{
+        Identifier[not set]{x_98}
+        subtract
+        Binary[not set]{
+          ScalarConstructor[not set]{2.000000}
+          multiply
+          Binary[not set]{
+            Identifier[not set]{x_99}
+            multiply
+            Binary[not set]{
+              Identifier[not set]{x_99}
+              multiply
+              Identifier[not set]{x_98}
+            }
+          }
+        }
+      }
+    }
+  }
+})";
+
+  EXPECT_THAT(body, HasSubstr(expected)) << body;
+}
+
+TEST_F(SpvParserTest, GlslStd450_Reflect_Vector) {
+  const auto assembly = Preamble() + R"(
+     %98 = OpFAdd %v2float %v2f1 %v2f1
+     %99 = OpFAdd %v2float %v2f2 %v2f2
+     %1 = OpExtInst %v2float %glsl Reflect %98 %99
+     OpReturn
+     OpFunctionEnd
+  )";
+  auto p = parser(test::Assemble(assembly));
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
+  auto fe = p->function_emitter(100);
+  EXPECT_TRUE(fe.EmitBody()) << p->error();
+  const auto body = ToString(p->builder(), fe.ast_body());
+  const auto* expected = R"(VariableDeclStatement{
+  VariableConst{
+    x_98
+    none
+    undefined
+    __vec_2__f32
+    {
+      Binary[not set]{
+        Identifier[not set]{v2f1}
+        add
+        Identifier[not set]{v2f1}
+      }
+    }
+  }
+}
+VariableDeclStatement{
+  VariableConst{
+    x_99
+    none
+    undefined
+    __vec_2__f32
+    {
+      Binary[not set]{
+        Identifier[not set]{v2f2}
+        add
+        Identifier[not set]{v2f2}
+      }
+    }
+  }
+}
+VariableDeclStatement{
+  VariableConst{
+    x_1
+    none
+    undefined
+    __vec_2__f32
+    {
+      Call[not set]{
+        Identifier[not set]{reflect}
+        (
+          Identifier[not set]{x_98}
+          Identifier[not set]{x_99}
+        )
+      }
+    })";
+
+  EXPECT_THAT(body, HasSubstr(expected)) << body;
+}
+
 }  // namespace
 }  // namespace spirv
 }  // namespace reader
diff --git a/test/vk-gl-cts/graphicsfuzz/cov-undefined-inversesqrt-reflect/0-opt.spvasm.expected.hlsl b/test/vk-gl-cts/graphicsfuzz/cov-undefined-inversesqrt-reflect/0-opt.spvasm.expected.hlsl
index a4a3ad2..3fb8095 100644
--- a/test/vk-gl-cts/graphicsfuzz/cov-undefined-inversesqrt-reflect/0-opt.spvasm.expected.hlsl
+++ b/test/vk-gl-cts/graphicsfuzz/cov-undefined-inversesqrt-reflect/0-opt.spvasm.expected.hlsl
@@ -21,7 +21,9 @@
   m24 = float2x2(float2(x_40, x_42), float2((x_44 * 1.0f), x_47));
   a = m24[0u].x;
   v2 = float2(asfloat(0x7fc00000u), 1.0f);
-  v3 = reflect(v2, float2(a, 1.0f));
+  const float2 x_53 = v2;
+  const float2 x_55 = float2(a, 1.0f);
+  v3 = reflect(x_53, x_55);
   const uint scalar_offset_3 = ((16u * uint(0))) / 4;
   const float x_58 = asfloat(x_6[scalar_offset_3 / 4][scalar_offset_3 % 4]);
   const float2 x_59 = v3;
diff --git a/test/vk-gl-cts/graphicsfuzz/cov-undefined-inversesqrt-reflect/0-opt.spvasm.expected.msl b/test/vk-gl-cts/graphicsfuzz/cov-undefined-inversesqrt-reflect/0-opt.spvasm.expected.msl
index 570e97a..ec52a78 100644
--- a/test/vk-gl-cts/graphicsfuzz/cov-undefined-inversesqrt-reflect/0-opt.spvasm.expected.msl
+++ b/test/vk-gl-cts/graphicsfuzz/cov-undefined-inversesqrt-reflect/0-opt.spvasm.expected.msl
@@ -36,7 +36,8 @@
   v2 = float2(NAN, 1.0f);
   float2 const x_53 = v2;
   float const x_54 = a;
-  v3 = reflect(x_53, float2(x_54, 1.0f));
+  float2 const x_55 = float2(x_54, 1.0f);
+  v3 = reflect(x_53, x_55);
   float const x_58 = x_6.x_GLF_uniform_float_values.arr[0].el;
   float2 const x_59 = v3;
   float const x_61 = x_6.x_GLF_uniform_float_values.arr[0].el;
diff --git a/test/vk-gl-cts/graphicsfuzz/cov-undefined-inversesqrt-reflect/0-opt.spvasm.expected.spvasm b/test/vk-gl-cts/graphicsfuzz/cov-undefined-inversesqrt-reflect/0-opt.spvasm.expected.spvasm
index a0d5039..6061eec 100644
--- a/test/vk-gl-cts/graphicsfuzz/cov-undefined-inversesqrt-reflect/0-opt.spvasm.expected.spvasm
+++ b/test/vk-gl-cts/graphicsfuzz/cov-undefined-inversesqrt-reflect/0-opt.spvasm.expected.spvasm
@@ -4,7 +4,7 @@
 ; Bound: 98
 ; Schema: 0
                OpCapability Shader
-         %58 = OpExtInstImport "GLSL.std.450"
+         %59 = OpExtInstImport "GLSL.std.450"
                OpMemoryModel Logical GLSL450
                OpEntryPoint Fragment %main "main" %tint_symbol_1
                OpExecutionMode %main OriginUpperLeft
@@ -104,9 +104,9 @@
                OpStore %v2 %54
          %55 = OpLoad %v2float %v2
          %56 = OpLoad %float %a
-         %59 = OpCompositeConstruct %v2float %56 %float_1
-         %57 = OpExtInst %v2float %58 Reflect %55 %59
-               OpStore %v3 %57
+         %57 = OpCompositeConstruct %v2float %56 %float_1
+         %58 = OpExtInst %v2float %59 Reflect %55 %57
+               OpStore %v3 %58
          %60 = OpAccessChain %_ptr_Uniform_float %x_6 %uint_0 %int_0
          %61 = OpLoad %float %60
          %62 = OpLoad %v2float %v3
diff --git a/test/vk-gl-cts/graphicsfuzz/cov-undefined-inversesqrt-reflect/0-opt.spvasm.expected.wgsl b/test/vk-gl-cts/graphicsfuzz/cov-undefined-inversesqrt-reflect/0-opt.spvasm.expected.wgsl
index 1e71e59..483443b 100644
--- a/test/vk-gl-cts/graphicsfuzz/cov-undefined-inversesqrt-reflect/0-opt.spvasm.expected.wgsl
+++ b/test/vk-gl-cts/graphicsfuzz/cov-undefined-inversesqrt-reflect/0-opt.spvasm.expected.wgsl
@@ -31,7 +31,8 @@
   v2 = vec2<f32>(-0x1.8p+128, 1.0);
   let x_53 : vec2<f32> = v2;
   let x_54 : f32 = a;
-  v3 = reflect(x_53, vec2<f32>(x_54, 1.0));
+  let x_55 : vec2<f32> = vec2<f32>(x_54, 1.0);
+  v3 = reflect(x_53, x_55);
   let x_58 : f32 = x_6.x_GLF_uniform_float_values[0];
   let x_59 : vec2<f32> = v3;
   let x_61 : f32 = x_6.x_GLF_uniform_float_values[0];