spirv-reader: handle OpUndef at module scope

Fixed: tint:523
Change-Id: I47639c8f701ca049c7215874cdea27f86d8a415b
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/43760
Auto-Submit: David Neto <dneto@google.com>
Commit-Queue: David Neto <dneto@google.com>
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
diff --git a/src/reader/spirv/function.cc b/src/reader/spirv/function.cc
index e9f0494..663d5cd 100644
--- a/src/reader/spirv/function.cc
+++ b/src/reader/spirv/function.cc
@@ -2091,6 +2091,13 @@
                              create<ast::IdentifierExpression>(
                                  Source{}, builder_.Symbols().Register(name))};
     }
+    case SpvOpUndef:
+      // Substitute a null value for undef.
+      // This case occurs when OpUndef appears at module scope, as if it were
+      // a constant.
+      return parser_impl_.MakeNullExpression(
+          parser_impl_.ConvertType(inst->type_id()));
+
     default:
       break;
   }
@@ -3294,7 +3301,7 @@
 
   if (opcode == SpvOpUndef) {
     // Replace undef with the null value.
-    return {ast_type, parser_impl_.MakeNullValue(ast_type)};
+    return parser_impl_.MakeNullExpression(ast_type);
   }
 
   if (opcode == SpvOpSelect) {
diff --git a/src/reader/spirv/function_misc_test.cc b/src/reader/spirv/function_misc_test.cc
index 4971866..8cf1b36 100644
--- a/src/reader/spirv/function_misc_test.cc
+++ b/src/reader/spirv/function_misc_test.cc
@@ -40,6 +40,7 @@
   %int = OpTypeInt 32 1
   %float = OpTypeFloat 32
 
+  %v2bool = OpTypeVector %bool 2
   %v2uint = OpTypeVector %uint 2
   %v2int = OpTypeVector %int 2
   %v2float = OpTypeVector %float 2
@@ -48,6 +49,149 @@
 
 using SpvParserTestMiscInstruction = SpvParserTest;
 
+TEST_F(SpvParserTestMiscInstruction, OpUndef_BeforeFunction_Scalar) {
+  const auto assembly = CommonTypes() + R"(
+     %1 = OpUndef %bool
+     %2 = OpUndef %uint
+     %3 = OpUndef %int
+     %4 = OpUndef %float
+
+     %100 = OpFunction %void None %voidfn
+     %entry = OpLabel
+     %11 = OpCopyObject %bool %1
+     %12 = OpCopyObject %uint %2
+     %13 = OpCopyObject %int %3
+     %14 = OpCopyObject %float %4
+     OpReturn
+     OpFunctionEnd
+)";
+  auto p = parser(test::Assemble(assembly));
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
+  FunctionEmitter fe(p.get(), *spirv_function(p.get(), 100));
+  EXPECT_TRUE(fe.EmitBody()) << p->error();
+  EXPECT_THAT(ToString(p->builder(), fe.ast_body()),
+              HasSubstr(R"(VariableDeclStatement{
+  VariableConst{
+    x_11
+    none
+    __bool
+    {
+      ScalarConstructor[not set]{false}
+    }
+  }
+}
+VariableDeclStatement{
+  VariableConst{
+    x_12
+    none
+    __u32
+    {
+      ScalarConstructor[not set]{0}
+    }
+  }
+}
+VariableDeclStatement{
+  VariableConst{
+    x_13
+    none
+    __i32
+    {
+      ScalarConstructor[not set]{0}
+    }
+  }
+}
+VariableDeclStatement{
+  VariableConst{
+    x_14
+    none
+    __f32
+    {
+      ScalarConstructor[not set]{0.000000}
+    }
+  }
+})")) << ToString(p->builder(), fe.ast_body());
+}
+
+TEST_F(SpvParserTestMiscInstruction, OpUndef_BeforeFunction_Vector) {
+  const auto assembly = CommonTypes() + R"(
+     %4 = OpUndef %v2bool
+     %1 = OpUndef %v2uint
+     %2 = OpUndef %v2int
+     %3 = OpUndef %v2float
+
+     %100 = OpFunction %void None %voidfn
+     %entry = OpLabel
+
+     %14 = OpCopyObject %v2uint %4
+     %11 = OpCopyObject %v2uint %1
+     %12 = OpCopyObject %v2int %2
+     %13 = OpCopyObject %v2float %3
+     OpReturn
+     OpFunctionEnd
+)";
+  auto p = parser(test::Assemble(assembly));
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
+  FunctionEmitter fe(p.get(), *spirv_function(p.get(), 100));
+  EXPECT_TRUE(fe.EmitBody()) << p->error();
+  EXPECT_THAT(ToString(p->builder(), fe.ast_body()),
+              HasSubstr(R"(VariableDeclStatement{
+  VariableConst{
+    x_14
+    none
+    __vec_2__bool
+    {
+      TypeConstructor[not set]{
+        __vec_2__bool
+        ScalarConstructor[not set]{false}
+        ScalarConstructor[not set]{false}
+      }
+    }
+  }
+}
+VariableDeclStatement{
+  VariableConst{
+    x_11
+    none
+    __vec_2__u32
+    {
+      TypeConstructor[not set]{
+        __vec_2__u32
+        ScalarConstructor[not set]{0}
+        ScalarConstructor[not set]{0}
+      }
+    }
+  }
+}
+VariableDeclStatement{
+  VariableConst{
+    x_12
+    none
+    __vec_2__i32
+    {
+      TypeConstructor[not set]{
+        __vec_2__i32
+        ScalarConstructor[not set]{0}
+        ScalarConstructor[not set]{0}
+      }
+    }
+  }
+}
+VariableDeclStatement{
+  VariableConst{
+    x_13
+    none
+    __vec_2__f32
+    {
+      TypeConstructor[not set]{
+        __vec_2__f32
+        ScalarConstructor[not set]{0.000000}
+        ScalarConstructor[not set]{0.000000}
+      }
+    }
+  }
+})")) << ToString(p->builder(), fe.ast_body());
+}
+
 TEST_F(SpvParserTestMiscInstruction, OpUndef_InFunction_Scalar) {
   const auto assembly = CommonTypes() + R"(
      %100 = OpFunction %void None %voidfn
diff --git a/src/reader/spirv/parser_impl.cc b/src/reader/spirv/parser_impl.cc
index 5d7af74..9a2855f 100644
--- a/src/reader/spirv/parser_impl.cc
+++ b/src/reader/spirv/parser_impl.cc
@@ -1538,6 +1538,10 @@
   return nullptr;
 }
 
+TypedExpression ParserImpl::MakeNullExpression(type::Type* type) {
+  return {type, MakeNullValue(type)};
+}
+
 TypedExpression ParserImpl::RectifyOperandSignedness(
     const spvtools::opt::Instruction& inst,
     TypedExpression&& expr) {
diff --git a/src/reader/spirv/parser_impl.h b/src/reader/spirv/parser_impl.h
index a023a3c..cdfa256 100644
--- a/src/reader/spirv/parser_impl.h
+++ b/src/reader/spirv/parser_impl.h
@@ -326,6 +326,11 @@
   /// @returns a new expression
   ast::Expression* MakeNullValue(type::Type* type);
 
+  /// Make a typed expression for the null value for the given type.
+  /// @param type the AST type
+  /// @returns a new typed expression
+  TypedExpression MakeNullExpression(type::Type* type);
+
   /// Converts a given expression to the signedness demanded for an operand
   /// of the given SPIR-V instruction, if required.  If the instruction assumes
   /// signed integer operands, and `expr` is unsigned, then return an