[spirv-writer] Use OpConstantNull for composites

When a constant composite is all zeros, generate OpConstantNull
instead of OpConstantComposite. The latter can exceed SPIR-V's maximum
operand count for large arrays.

Bug: tint:1906
Change-Id: I4c7c58142c790d4086578ff7b5811b187d7fac24
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/154040
Reviewed-by: Ben Clayton <bclayton@google.com>
Reviewed-by: dan sinclair <dsinclair@chromium.org>
Commit-Queue: James Price <jrprice@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/src/tint/lang/core/ir/builder.h b/src/tint/lang/core/ir/builder.h
index 64448cf..75f7266 100644
--- a/src/tint/lang/core/ir/builder.h
+++ b/src/tint/lang/core/ir/builder.h
@@ -356,6 +356,11 @@
             ir.constant_values.Composite(ty, Vector{ConstantValue(std::forward<ARGS>(values))...}));
     }
 
+    /// Creates a new zero-value ir::Constant
+    /// @param ty the constant type
+    /// @returns the new constant
+    ir::Constant* Zero(const core::type::Type* ty) { return Constant(ir.constant_values.Zero(ty)); }
+
     /// @param in the input value. One of: nullptr, ir::Value*, ir::Instruction* or a numeric value.
     /// @returns an ir::Value* from the given argument.
     template <typename T>
diff --git a/src/tint/lang/spirv/writer/binary_test.cc b/src/tint/lang/spirv/writer/binary_test.cc
index 8e3fe2e..45eb416 100644
--- a/src/tint/lang/spirv/writer/binary_test.cc
+++ b/src/tint/lang/spirv/writer/binary_test.cc
@@ -408,6 +408,7 @@
     });
 
     ASSERT_TRUE(Generate()) << Error() << output_;
+    EXPECT_INST("%16 = OpConstantNull %v4int");
     EXPECT_INST(R"(
                ; Function foo
         %foo = OpFunction %v4int None %6
@@ -425,13 +426,13 @@
       %rhs_0 = OpFunctionParameter %v4int
          %14 = OpLabel
          %15 = OpIEqual %v4bool %rhs_0 %16
-         %20 = OpIEqual %v4bool %lhs_0 %21
-         %23 = OpIEqual %v4bool %rhs_0 %24
-         %26 = OpLogicalAnd %v4bool %20 %23
-         %27 = OpLogicalOr %v4bool %15 %26
-         %28 = OpSelect %v4int %27 %29 %rhs_0
-         %31 = OpSDiv %v4int %lhs_0 %28
-               OpReturnValue %31
+         %19 = OpIEqual %v4bool %lhs_0 %20
+         %22 = OpIEqual %v4bool %rhs_0 %23
+         %25 = OpLogicalAnd %v4bool %19 %22
+         %26 = OpLogicalOr %v4bool %15 %25
+         %27 = OpSelect %v4int %26 %28 %rhs_0
+         %30 = OpSDiv %v4int %lhs_0 %27
+               OpReturnValue %30
                OpFunctionEnd
 )");
 }
@@ -449,6 +450,7 @@
     });
 
     ASSERT_TRUE(Generate()) << Error() << output_;
+    EXPECT_INST("%16 = OpConstantNull %v4int");
     EXPECT_INST(R"(
                ; Function foo
         %foo = OpFunction %v4int None %6
@@ -466,13 +468,13 @@
       %rhs_0 = OpFunctionParameter %v4int
          %14 = OpLabel
          %15 = OpIEqual %v4bool %rhs_0 %16
-         %20 = OpIEqual %v4bool %lhs_0 %21
-         %23 = OpIEqual %v4bool %rhs_0 %24
-         %26 = OpLogicalAnd %v4bool %20 %23
-         %27 = OpLogicalOr %v4bool %15 %26
-         %28 = OpSelect %v4int %27 %29 %rhs_0
-         %31 = OpSDiv %v4int %lhs_0 %28
-               OpReturnValue %31
+         %19 = OpIEqual %v4bool %lhs_0 %20
+         %22 = OpIEqual %v4bool %rhs_0 %23
+         %25 = OpLogicalAnd %v4bool %19 %22
+         %26 = OpLogicalOr %v4bool %15 %25
+         %27 = OpSelect %v4int %26 %28 %rhs_0
+         %30 = OpSDiv %v4int %lhs_0 %27
+               OpReturnValue %30
                OpFunctionEnd
 )");
 }
@@ -570,6 +572,7 @@
     });
 
     ASSERT_TRUE(Generate()) << Error() << output_;
+    EXPECT_INST("%16 = OpConstantNull %v4int");
     EXPECT_INST(R"(
                ; Function foo
         %foo = OpFunction %v4int None %6
@@ -587,15 +590,15 @@
       %rhs_0 = OpFunctionParameter %v4int
          %14 = OpLabel
          %15 = OpIEqual %v4bool %rhs_0 %16
-         %20 = OpIEqual %v4bool %lhs_0 %21
-         %23 = OpIEqual %v4bool %rhs_0 %24
-         %26 = OpLogicalAnd %v4bool %20 %23
-         %27 = OpLogicalOr %v4bool %15 %26
-         %28 = OpSelect %v4int %27 %29 %rhs_0
-         %31 = OpSDiv %v4int %lhs_0 %28
-         %32 = OpIMul %v4int %31 %28
-         %33 = OpISub %v4int %lhs_0 %32
-               OpReturnValue %33
+         %19 = OpIEqual %v4bool %lhs_0 %20
+         %22 = OpIEqual %v4bool %rhs_0 %23
+         %25 = OpLogicalAnd %v4bool %19 %22
+         %26 = OpLogicalOr %v4bool %15 %25
+         %27 = OpSelect %v4int %26 %28 %rhs_0
+         %30 = OpSDiv %v4int %lhs_0 %27
+         %31 = OpIMul %v4int %30 %27
+         %32 = OpISub %v4int %lhs_0 %31
+               OpReturnValue %32
                OpFunctionEnd
 )");
 }
@@ -613,6 +616,7 @@
     });
 
     ASSERT_TRUE(Generate()) << Error() << output_;
+    EXPECT_INST("%16 = OpConstantNull %v4int");
     EXPECT_INST(R"(
                ; Function foo
         %foo = OpFunction %v4int None %6
@@ -630,15 +634,15 @@
       %rhs_0 = OpFunctionParameter %v4int
          %14 = OpLabel
          %15 = OpIEqual %v4bool %rhs_0 %16
-         %20 = OpIEqual %v4bool %lhs_0 %21
-         %23 = OpIEqual %v4bool %rhs_0 %24
-         %26 = OpLogicalAnd %v4bool %20 %23
-         %27 = OpLogicalOr %v4bool %15 %26
-         %28 = OpSelect %v4int %27 %29 %rhs_0
-         %31 = OpSDiv %v4int %lhs_0 %28
-         %32 = OpIMul %v4int %31 %28
-         %33 = OpISub %v4int %lhs_0 %32
-               OpReturnValue %33
+         %19 = OpIEqual %v4bool %lhs_0 %20
+         %22 = OpIEqual %v4bool %rhs_0 %23
+         %25 = OpLogicalAnd %v4bool %19 %22
+         %26 = OpLogicalOr %v4bool %15 %25
+         %27 = OpSelect %v4int %26 %28 %rhs_0
+         %30 = OpSDiv %v4int %lhs_0 %27
+         %31 = OpIMul %v4int %30 %27
+         %32 = OpISub %v4int %lhs_0 %31
+               OpReturnValue %32
                OpFunctionEnd
 )");
 }
diff --git a/src/tint/lang/spirv/writer/builtin_test.cc b/src/tint/lang/spirv/writer/builtin_test.cc
index 3597ed8..adce399 100644
--- a/src/tint/lang/spirv/writer/builtin_test.cc
+++ b/src/tint/lang/spirv/writer/builtin_test.cc
@@ -687,38 +687,38 @@
     ASSERT_TRUE(Generate()) << Error() << output_;
     EXPECT_INST("%8 = OpConstantComposite %v2uint %uint_65535 %uint_65535");
     EXPECT_INST("%13 = OpConstantComposite %v2uint %uint_16 %uint_16");
-    EXPECT_INST("%15 = OpConstantComposite %v2uint %uint_0 %uint_0");
-    EXPECT_INST("%19 = OpConstantComposite %v2uint %uint_16777215 %uint_16777215");
-    EXPECT_INST("%22 = OpConstantComposite %v2uint %uint_8 %uint_8");
-    EXPECT_INST("%26 = OpConstantComposite %v2uint %uint_268435455 %uint_268435455");
-    EXPECT_INST("%29 = OpConstantComposite %v2uint %uint_4 %uint_4");
-    EXPECT_INST("%33 = OpConstantComposite %v2uint %uint_1073741823 %uint_1073741823");
-    EXPECT_INST("%36 = OpConstantComposite %v2uint %uint_2 %uint_2");
-    EXPECT_INST("%40 = OpConstantComposite %v2uint %uint_2147483647 %uint_2147483647");
-    EXPECT_INST("%43 = OpConstantComposite %v2uint %uint_1 %uint_1");
+    EXPECT_INST("%15 = OpConstantNull %v2uint");
+    EXPECT_INST("%18 = OpConstantComposite %v2uint %uint_16777215 %uint_16777215");
+    EXPECT_INST("%21 = OpConstantComposite %v2uint %uint_8 %uint_8");
+    EXPECT_INST("%25 = OpConstantComposite %v2uint %uint_268435455 %uint_268435455");
+    EXPECT_INST("%28 = OpConstantComposite %v2uint %uint_4 %uint_4");
+    EXPECT_INST("%32 = OpConstantComposite %v2uint %uint_1073741823 %uint_1073741823");
+    EXPECT_INST("%35 = OpConstantComposite %v2uint %uint_2 %uint_2");
+    EXPECT_INST("%39 = OpConstantComposite %v2uint %uint_2147483647 %uint_2147483647");
+    EXPECT_INST("%42 = OpConstantComposite %v2uint %uint_1 %uint_1");
     EXPECT_INST(R"(
           %7 = OpULessThanEqual %v2bool %arg %8
          %12 = OpSelect %v2uint %7 %13 %15
-         %17 = OpShiftLeftLogical %v2uint %arg %12
-         %18 = OpULessThanEqual %v2bool %17 %19
-         %21 = OpSelect %v2uint %18 %22 %15
-         %24 = OpShiftLeftLogical %v2uint %17 %21
-         %25 = OpULessThanEqual %v2bool %24 %26
-         %28 = OpSelect %v2uint %25 %29 %15
-         %31 = OpShiftLeftLogical %v2uint %24 %28
-         %32 = OpULessThanEqual %v2bool %31 %33
-         %35 = OpSelect %v2uint %32 %36 %15
-         %38 = OpShiftLeftLogical %v2uint %31 %35
-         %39 = OpULessThanEqual %v2bool %38 %40
-         %42 = OpSelect %v2uint %39 %43 %15
-         %45 = OpIEqual %v2bool %38 %15
-         %46 = OpSelect %v2uint %45 %43 %15
-         %47 = OpBitwiseOr %v2uint %42 %46
-         %48 = OpBitwiseOr %v2uint %35 %47
-         %49 = OpBitwiseOr %v2uint %28 %48
-         %50 = OpBitwiseOr %v2uint %21 %49
-         %51 = OpBitwiseOr %v2uint %12 %50
-     %result = OpIAdd %v2uint %51 %46
+         %16 = OpShiftLeftLogical %v2uint %arg %12
+         %17 = OpULessThanEqual %v2bool %16 %18
+         %20 = OpSelect %v2uint %17 %21 %15
+         %23 = OpShiftLeftLogical %v2uint %16 %20
+         %24 = OpULessThanEqual %v2bool %23 %25
+         %27 = OpSelect %v2uint %24 %28 %15
+         %30 = OpShiftLeftLogical %v2uint %23 %27
+         %31 = OpULessThanEqual %v2bool %30 %32
+         %34 = OpSelect %v2uint %31 %35 %15
+         %37 = OpShiftLeftLogical %v2uint %30 %34
+         %38 = OpULessThanEqual %v2bool %37 %39
+         %41 = OpSelect %v2uint %38 %42 %15
+         %44 = OpIEqual %v2bool %37 %15
+         %45 = OpSelect %v2uint %44 %42 %15
+         %46 = OpBitwiseOr %v2uint %41 %45
+         %47 = OpBitwiseOr %v2uint %34 %46
+         %48 = OpBitwiseOr %v2uint %27 %47
+         %49 = OpBitwiseOr %v2uint %20 %48
+         %50 = OpBitwiseOr %v2uint %12 %49
+     %result = OpIAdd %v2uint %50 %45
 )");
 }
 
@@ -818,42 +818,42 @@
 
     ASSERT_TRUE(Generate()) << Error() << output_;
     EXPECT_INST("%8 = OpConstantComposite %v2uint %uint_65535 %uint_65535");
-    EXPECT_INST("%11 = OpConstantComposite %v2uint %uint_0 %uint_0");
-    EXPECT_INST("%16 = OpConstantComposite %v2uint %uint_16 %uint_16");
-    EXPECT_INST("%20 = OpConstantComposite %v2uint %uint_255 %uint_255");
-    EXPECT_INST("%24 = OpConstantComposite %v2uint %uint_8 %uint_8");
-    EXPECT_INST("%28 = OpConstantComposite %v2uint %uint_15 %uint_15");
-    EXPECT_INST("%32 = OpConstantComposite %v2uint %uint_4 %uint_4");
-    EXPECT_INST("%36 = OpConstantComposite %v2uint %uint_3 %uint_3");
-    EXPECT_INST("%40 = OpConstantComposite %v2uint %uint_2 %uint_2");
-    EXPECT_INST("%44 = OpConstantComposite %v2uint %uint_1 %uint_1");
+    EXPECT_INST("%11 = OpConstantNull %v2uint");
+    EXPECT_INST("%15 = OpConstantComposite %v2uint %uint_16 %uint_16");
+    EXPECT_INST("%19 = OpConstantComposite %v2uint %uint_255 %uint_255");
+    EXPECT_INST("%23 = OpConstantComposite %v2uint %uint_8 %uint_8");
+    EXPECT_INST("%27 = OpConstantComposite %v2uint %uint_15 %uint_15");
+    EXPECT_INST("%31 = OpConstantComposite %v2uint %uint_4 %uint_4");
+    EXPECT_INST("%35 = OpConstantComposite %v2uint %uint_3 %uint_3");
+    EXPECT_INST("%39 = OpConstantComposite %v2uint %uint_2 %uint_2");
+    EXPECT_INST("%43 = OpConstantComposite %v2uint %uint_1 %uint_1");
     EXPECT_INST(R"(
           %7 = OpBitwiseAnd %v2uint %arg %8
          %10 = OpIEqual %v2bool %7 %11
-         %15 = OpSelect %v2uint %10 %16 %11
-         %18 = OpShiftRightLogical %v2uint %arg %15
-         %19 = OpBitwiseAnd %v2uint %18 %20
-         %22 = OpIEqual %v2bool %19 %11
-         %23 = OpSelect %v2uint %22 %24 %11
-         %26 = OpShiftRightLogical %v2uint %18 %23
-         %27 = OpBitwiseAnd %v2uint %26 %28
-         %30 = OpIEqual %v2bool %27 %11
-         %31 = OpSelect %v2uint %30 %32 %11
-         %34 = OpShiftRightLogical %v2uint %26 %31
-         %35 = OpBitwiseAnd %v2uint %34 %36
-         %38 = OpIEqual %v2bool %35 %11
-         %39 = OpSelect %v2uint %38 %40 %11
-         %42 = OpShiftRightLogical %v2uint %34 %39
-         %43 = OpBitwiseAnd %v2uint %42 %44
-         %46 = OpIEqual %v2bool %43 %11
-         %47 = OpSelect %v2uint %46 %44 %11
-         %48 = OpIEqual %v2bool %42 %11
-         %49 = OpSelect %v2uint %48 %44 %11
-         %50 = OpBitwiseOr %v2uint %39 %47
-         %51 = OpBitwiseOr %v2uint %31 %50
-         %52 = OpBitwiseOr %v2uint %23 %51
-         %53 = OpBitwiseOr %v2uint %15 %52
-     %result = OpIAdd %v2uint %53 %49
+         %14 = OpSelect %v2uint %10 %15 %11
+         %17 = OpShiftRightLogical %v2uint %arg %14
+         %18 = OpBitwiseAnd %v2uint %17 %19
+         %21 = OpIEqual %v2bool %18 %11
+         %22 = OpSelect %v2uint %21 %23 %11
+         %25 = OpShiftRightLogical %v2uint %17 %22
+         %26 = OpBitwiseAnd %v2uint %25 %27
+         %29 = OpIEqual %v2bool %26 %11
+         %30 = OpSelect %v2uint %29 %31 %11
+         %33 = OpShiftRightLogical %v2uint %25 %30
+         %34 = OpBitwiseAnd %v2uint %33 %35
+         %37 = OpIEqual %v2bool %34 %11
+         %38 = OpSelect %v2uint %37 %39 %11
+         %41 = OpShiftRightLogical %v2uint %33 %38
+         %42 = OpBitwiseAnd %v2uint %41 %43
+         %45 = OpIEqual %v2bool %42 %11
+         %46 = OpSelect %v2uint %45 %43 %11
+         %47 = OpIEqual %v2bool %41 %11
+         %48 = OpSelect %v2uint %47 %43 %11
+         %49 = OpBitwiseOr %v2uint %38 %46
+         %50 = OpBitwiseOr %v2uint %30 %49
+         %51 = OpBitwiseOr %v2uint %22 %50
+         %52 = OpBitwiseOr %v2uint %14 %51
+     %result = OpIAdd %v2uint %52 %48
 )");
 }
 
@@ -954,42 +954,42 @@
 
     ASSERT_TRUE(Generate()) << Error() << output_;
     EXPECT_INST("%8 = OpConstantComposite %v2uint %uint_4294901760 %uint_4294901760");
-    EXPECT_INST("%11 = OpConstantComposite %v2uint %uint_0 %uint_0");
-    EXPECT_INST("%16 = OpConstantComposite %v2uint %uint_16 %uint_16");
-    EXPECT_INST("%20 = OpConstantComposite %v2uint %uint_65280 %uint_65280");
-    EXPECT_INST("%24 = OpConstantComposite %v2uint %uint_8 %uint_8");
-    EXPECT_INST("%28 = OpConstantComposite %v2uint %uint_240 %uint_240");
-    EXPECT_INST("%32 = OpConstantComposite %v2uint %uint_4 %uint_4");
-    EXPECT_INST("%36 = OpConstantComposite %v2uint %uint_12 %uint_12");
-    EXPECT_INST("%40 = OpConstantComposite %v2uint %uint_2 %uint_2");
-    EXPECT_INST("%46 = OpConstantComposite %v2uint %uint_1 %uint_1");
-    EXPECT_INST("%54 = OpConstantComposite %v2uint %uint_4294967295 %uint_4294967295");
+    EXPECT_INST("%11 = OpConstantNull %v2uint");
+    EXPECT_INST("%15 = OpConstantComposite %v2uint %uint_16 %uint_16");
+    EXPECT_INST("%19 = OpConstantComposite %v2uint %uint_65280 %uint_65280");
+    EXPECT_INST("%23 = OpConstantComposite %v2uint %uint_8 %uint_8");
+    EXPECT_INST("%27 = OpConstantComposite %v2uint %uint_240 %uint_240");
+    EXPECT_INST("%31 = OpConstantComposite %v2uint %uint_4 %uint_4");
+    EXPECT_INST("%35 = OpConstantComposite %v2uint %uint_12 %uint_12");
+    EXPECT_INST("%39 = OpConstantComposite %v2uint %uint_2 %uint_2");
+    EXPECT_INST("%45 = OpConstantComposite %v2uint %uint_1 %uint_1");
+    EXPECT_INST("%53 = OpConstantComposite %v2uint %uint_4294967295 %uint_4294967295");
     EXPECT_INST(R"(
           %7 = OpBitwiseAnd %v2uint %arg %8
          %10 = OpIEqual %v2bool %7 %11
-         %15 = OpSelect %v2uint %10 %11 %16
-         %18 = OpShiftRightLogical %v2uint %arg %15
-         %19 = OpBitwiseAnd %v2uint %18 %20
-         %22 = OpIEqual %v2bool %19 %11
-         %23 = OpSelect %v2uint %22 %11 %24
-         %26 = OpShiftRightLogical %v2uint %18 %23
-         %27 = OpBitwiseAnd %v2uint %26 %28
-         %30 = OpIEqual %v2bool %27 %11
-         %31 = OpSelect %v2uint %30 %11 %32
-         %34 = OpShiftRightLogical %v2uint %26 %31
-         %35 = OpBitwiseAnd %v2uint %34 %36
-         %38 = OpIEqual %v2bool %35 %11
-         %39 = OpSelect %v2uint %38 %11 %40
-         %42 = OpShiftRightLogical %v2uint %34 %39
-         %43 = OpBitwiseAnd %v2uint %42 %40
-         %44 = OpIEqual %v2bool %43 %11
-         %45 = OpSelect %v2uint %44 %11 %46
-         %48 = OpBitwiseOr %v2uint %39 %45
-         %49 = OpBitwiseOr %v2uint %31 %48
-         %50 = OpBitwiseOr %v2uint %23 %49
-         %51 = OpBitwiseOr %v2uint %15 %50
-         %52 = OpIEqual %v2bool %42 %11
-     %result = OpSelect %v2uint %52 %54 %51
+         %14 = OpSelect %v2uint %10 %11 %15
+         %17 = OpShiftRightLogical %v2uint %arg %14
+         %18 = OpBitwiseAnd %v2uint %17 %19
+         %21 = OpIEqual %v2bool %18 %11
+         %22 = OpSelect %v2uint %21 %11 %23
+         %25 = OpShiftRightLogical %v2uint %17 %22
+         %26 = OpBitwiseAnd %v2uint %25 %27
+         %29 = OpIEqual %v2bool %26 %11
+         %30 = OpSelect %v2uint %29 %11 %31
+         %33 = OpShiftRightLogical %v2uint %25 %30
+         %34 = OpBitwiseAnd %v2uint %33 %35
+         %37 = OpIEqual %v2bool %34 %11
+         %38 = OpSelect %v2uint %37 %11 %39
+         %41 = OpShiftRightLogical %v2uint %33 %38
+         %42 = OpBitwiseAnd %v2uint %41 %39
+         %43 = OpIEqual %v2bool %42 %11
+         %44 = OpSelect %v2uint %43 %11 %45
+         %47 = OpBitwiseOr %v2uint %38 %44
+         %48 = OpBitwiseOr %v2uint %30 %47
+         %49 = OpBitwiseOr %v2uint %22 %48
+         %50 = OpBitwiseOr %v2uint %14 %49
+         %51 = OpIEqual %v2bool %41 %11
+     %result = OpSelect %v2uint %51 %53 %50
 )");
 }
 
@@ -1087,42 +1087,42 @@
 
     ASSERT_TRUE(Generate()) << Error() << output_;
     EXPECT_INST("%8 = OpConstantComposite %v2uint %uint_65535 %uint_65535");
-    EXPECT_INST("%11 = OpConstantComposite %v2uint %uint_0 %uint_0");
-    EXPECT_INST("%16 = OpConstantComposite %v2uint %uint_16 %uint_16");
-    EXPECT_INST("%20 = OpConstantComposite %v2uint %uint_255 %uint_255");
-    EXPECT_INST("%24 = OpConstantComposite %v2uint %uint_8 %uint_8");
-    EXPECT_INST("%28 = OpConstantComposite %v2uint %uint_15 %uint_15");
-    EXPECT_INST("%32 = OpConstantComposite %v2uint %uint_4 %uint_4");
-    EXPECT_INST("%36 = OpConstantComposite %v2uint %uint_3 %uint_3");
-    EXPECT_INST("%40 = OpConstantComposite %v2uint %uint_2 %uint_2");
-    EXPECT_INST("%44 = OpConstantComposite %v2uint %uint_1 %uint_1");
-    EXPECT_INST("%54 = OpConstantComposite %v2uint %uint_4294967295 %uint_4294967295");
+    EXPECT_INST("%11 = OpConstantNull %v2uint");
+    EXPECT_INST("%15 = OpConstantComposite %v2uint %uint_16 %uint_16");
+    EXPECT_INST("%19 = OpConstantComposite %v2uint %uint_255 %uint_255");
+    EXPECT_INST("%23 = OpConstantComposite %v2uint %uint_8 %uint_8");
+    EXPECT_INST("%27 = OpConstantComposite %v2uint %uint_15 %uint_15");
+    EXPECT_INST("%31 = OpConstantComposite %v2uint %uint_4 %uint_4");
+    EXPECT_INST("%35 = OpConstantComposite %v2uint %uint_3 %uint_3");
+    EXPECT_INST("%39 = OpConstantComposite %v2uint %uint_2 %uint_2");
+    EXPECT_INST("%43 = OpConstantComposite %v2uint %uint_1 %uint_1");
+    EXPECT_INST("%53 = OpConstantComposite %v2uint %uint_4294967295 %uint_4294967295");
     EXPECT_INST(R"(
           %7 = OpBitwiseAnd %v2uint %arg %8
          %10 = OpIEqual %v2bool %7 %11
-         %15 = OpSelect %v2uint %10 %16 %11
-         %18 = OpShiftRightLogical %v2uint %arg %15
-         %19 = OpBitwiseAnd %v2uint %18 %20
-         %22 = OpIEqual %v2bool %19 %11
-         %23 = OpSelect %v2uint %22 %24 %11
-         %26 = OpShiftRightLogical %v2uint %18 %23
-         %27 = OpBitwiseAnd %v2uint %26 %28
-         %30 = OpIEqual %v2bool %27 %11
-         %31 = OpSelect %v2uint %30 %32 %11
-         %34 = OpShiftRightLogical %v2uint %26 %31
-         %35 = OpBitwiseAnd %v2uint %34 %36
-         %38 = OpIEqual %v2bool %35 %11
-         %39 = OpSelect %v2uint %38 %40 %11
-         %42 = OpShiftRightLogical %v2uint %34 %39
-         %43 = OpBitwiseAnd %v2uint %42 %44
-         %46 = OpIEqual %v2bool %43 %11
-         %47 = OpSelect %v2uint %46 %44 %11
-         %48 = OpBitwiseOr %v2uint %39 %47
-         %49 = OpBitwiseOr %v2uint %31 %48
-         %50 = OpBitwiseOr %v2uint %23 %49
-         %51 = OpBitwiseOr %v2uint %15 %50
-         %52 = OpIEqual %v2bool %42 %11
-     %result = OpSelect %v2uint %52 %54 %51
+         %14 = OpSelect %v2uint %10 %15 %11
+         %17 = OpShiftRightLogical %v2uint %arg %14
+         %18 = OpBitwiseAnd %v2uint %17 %19
+         %21 = OpIEqual %v2bool %18 %11
+         %22 = OpSelect %v2uint %21 %23 %11
+         %25 = OpShiftRightLogical %v2uint %17 %22
+         %26 = OpBitwiseAnd %v2uint %25 %27
+         %29 = OpIEqual %v2bool %26 %11
+         %30 = OpSelect %v2uint %29 %31 %11
+         %33 = OpShiftRightLogical %v2uint %25 %30
+         %34 = OpBitwiseAnd %v2uint %33 %35
+         %37 = OpIEqual %v2bool %34 %11
+         %38 = OpSelect %v2uint %37 %39 %11
+         %41 = OpShiftRightLogical %v2uint %33 %38
+         %42 = OpBitwiseAnd %v2uint %41 %43
+         %45 = OpIEqual %v2bool %42 %11
+         %46 = OpSelect %v2uint %45 %43 %11
+         %47 = OpBitwiseOr %v2uint %38 %46
+         %48 = OpBitwiseOr %v2uint %30 %47
+         %49 = OpBitwiseOr %v2uint %22 %48
+         %50 = OpBitwiseOr %v2uint %14 %49
+         %51 = OpIEqual %v2bool %41 %11
+     %result = OpSelect %v2uint %51 %53 %50
 )");
 }
 
@@ -1151,11 +1151,10 @@
     });
 
     ASSERT_TRUE(Generate()) << Error() << output_;
+    EXPECT_INST("%9 = OpConstantNull %v4half");
     EXPECT_INST(
-        "%9 = OpConstantComposite %v4half %half_0x0p_0 %half_0x0p_0 %half_0x0p_0 %half_0x0p_0");
-    EXPECT_INST(
-        "%11 = OpConstantComposite %v4half %half_0x1p_0 %half_0x1p_0 %half_0x1p_0 %half_0x1p_0");
-    EXPECT_INST("%result = OpExtInst %v4half %8 NClamp %arg %9 %11");
+        "%10 = OpConstantComposite %v4half %half_0x1p_0 %half_0x1p_0 %half_0x1p_0 %half_0x1p_0");
+    EXPECT_INST("%result = OpExtInst %v4half %8 NClamp %arg %9 %10");
 }
 
 // Tests for builtins with the signature: T = func(T, T)
diff --git a/src/tint/lang/spirv/writer/constant_test.cc b/src/tint/lang/spirv/writer/constant_test.cc
index e54f8ef7..1241487 100644
--- a/src/tint/lang/spirv/writer/constant_test.cc
+++ b/src/tint/lang/spirv/writer/constant_test.cc
@@ -148,6 +148,12 @@
 )");
 }
 
+TEST_F(SpirvWriterTest, Constant_Array_LargeAllZero) {
+    writer_.Constant(b.Zero(ty.array<i32, 65535>()));
+    ASSERT_TRUE(Generate()) << Error() << output_;
+    EXPECT_INST("%1 = OpConstantNull %_arr_int_uint_65535");
+}
+
 TEST_F(SpirvWriterTest, Constant_Struct) {
     auto* str_ty = ty.Struct(mod.symbols.New("MyStruct"), {
                                                               {mod.symbols.New("a"), ty.i32()},
diff --git a/src/tint/lang/spirv/writer/printer/printer.cc b/src/tint/lang/spirv/writer/printer/printer.cc
index 43ed467..7d13e1c 100644
--- a/src/tint/lang/spirv/writer/printer/printer.cc
+++ b/src/tint/lang/spirv/writer/printer/printer.cc
@@ -247,8 +247,14 @@
 
 uint32_t Printer::Constant(const core::constant::Value* constant) {
     return constants_.GetOrCreate(constant, [&] {
-        auto id = module_.NextId();
         auto* ty = constant->Type();
+
+        // Use OpConstantNull for zero-valued composite constants.
+        if (!ty->Is<core::type::Scalar>() && constant->AllZero()) {
+            return ConstantNull(ty);
+        }
+
+        auto id = module_.NextId();
         Switch(
             ty,  //
             [&](const core::type::Bool*) {