tint/transform: Polyfill bit-shift with RHS modulo

Fixed: tint:1453
Fixed: tint:1543
Change-Id: Idb5af752d7a3bb9e181cc47430ad4ddfb707873d
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/108440
Auto-Submit: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: Dan Sinclair <dsinclair@chromium.org>
diff --git a/src/tint/transform/builtin_polyfill.cc b/src/tint/transform/builtin_polyfill.cc
index db200e4..e80436d 100644
--- a/src/tint/transform/builtin_polyfill.cc
+++ b/src/tint/transform/builtin_polyfill.cc
@@ -614,7 +614,7 @@
 
     auto& builtins = cfg->builtins;
 
-    utils::Hashmap<const sem::Builtin*, Symbol, 8> polyfills;
+    utils::Hashmap<const sem::Builtin*, Symbol, 8> builtin_polyfills;
 
     ProgramBuilder b;
     CloneContext ctx{&b, src, /* auto_clone_symbols */ true};
@@ -622,114 +622,137 @@
 
     bool made_changes = false;
     for (auto* node : src->ASTNodes().Objects()) {
-        if (auto* call = src->Sem().Get<sem::Call>(node)) {
-            if (auto* builtin = call->Target()->As<sem::Builtin>()) {
-                if (call->Stage() == sem::EvaluationStage::kConstant) {
-                    continue;  // Don't polyfill @const expressions
-                }
-                Symbol polyfill;
-                switch (builtin->Type()) {
-                    case sem::BuiltinType::kAcosh:
-                        if (builtins.acosh != Level::kNone) {
-                            polyfill = polyfills.GetOrCreate(
-                                builtin, [&] { return s.acosh(builtin->ReturnType()); });
-                        }
-                        break;
-                    case sem::BuiltinType::kAsinh:
-                        if (builtins.asinh) {
-                            polyfill = polyfills.GetOrCreate(
-                                builtin, [&] { return s.asinh(builtin->ReturnType()); });
-                        }
-                        break;
-                    case sem::BuiltinType::kAtanh:
-                        if (builtins.atanh != Level::kNone) {
-                            polyfill = polyfills.GetOrCreate(
-                                builtin, [&] { return s.atanh(builtin->ReturnType()); });
-                        }
-                        break;
-                    case sem::BuiltinType::kClamp:
-                        if (builtins.clamp_int) {
-                            auto& sig = builtin->Signature();
-                            if (sig.parameters[0]->Type()->is_integer_scalar_or_vector()) {
-                                polyfill = polyfills.GetOrCreate(
-                                    builtin, [&] { return s.clampInteger(builtin->ReturnType()); });
-                            }
-                        }
-                        break;
-                    case sem::BuiltinType::kCountLeadingZeros:
-                        if (builtins.count_leading_zeros) {
-                            polyfill = polyfills.GetOrCreate(builtin, [&] {
-                                return s.countLeadingZeros(builtin->ReturnType());
-                            });
-                        }
-                        break;
-                    case sem::BuiltinType::kCountTrailingZeros:
-                        if (builtins.count_trailing_zeros) {
-                            polyfill = polyfills.GetOrCreate(builtin, [&] {
-                                return s.countTrailingZeros(builtin->ReturnType());
-                            });
-                        }
-                        break;
-                    case sem::BuiltinType::kExtractBits:
-                        if (builtins.extract_bits != Level::kNone) {
-                            polyfill = polyfills.GetOrCreate(
-                                builtin, [&] { return s.extractBits(builtin->ReturnType()); });
-                        }
-                        break;
-                    case sem::BuiltinType::kFirstLeadingBit:
-                        if (builtins.first_leading_bit) {
-                            polyfill = polyfills.GetOrCreate(
-                                builtin, [&] { return s.firstLeadingBit(builtin->ReturnType()); });
-                        }
-                        break;
-                    case sem::BuiltinType::kFirstTrailingBit:
-                        if (builtins.first_trailing_bit) {
-                            polyfill = polyfills.GetOrCreate(
-                                builtin, [&] { return s.firstTrailingBit(builtin->ReturnType()); });
-                        }
-                        break;
-                    case sem::BuiltinType::kInsertBits:
-                        if (builtins.insert_bits != Level::kNone) {
-                            polyfill = polyfills.GetOrCreate(
-                                builtin, [&] { return s.insertBits(builtin->ReturnType()); });
-                        }
-                        break;
-                    case sem::BuiltinType::kSaturate:
-                        if (builtins.saturate) {
-                            polyfill = polyfills.GetOrCreate(
-                                builtin, [&] { return s.saturate(builtin->ReturnType()); });
-                        }
-                        break;
-                    case sem::BuiltinType::kTextureSampleBaseClampToEdge:
-                        if (builtins.texture_sample_base_clamp_to_edge_2d_f32) {
-                            auto& sig = builtin->Signature();
-                            auto* tex = sig.Parameter(sem::ParameterUsage::kTexture);
-                            if (auto* stex = tex->Type()->As<sem::SampledTexture>()) {
-                                if (stex->type()->Is<sem::F32>()) {
-                                    polyfill = polyfills.GetOrCreate(builtin, [&] {
-                                        return s.textureSampleBaseClampToEdge_2d_f32();
-                                    });
-                                }
-                            }
-                        }
-                        break;
-                    case sem::BuiltinType::kQuantizeToF16:
-                        if (builtins.quantize_to_vec_f16) {
-                            if (auto* vec = builtin->ReturnType()->As<sem::Vector>()) {
-                                polyfill = polyfills.GetOrCreate(
-                                    builtin, [&] { return s.quantizeToF16(vec); });
-                            }
-                        }
-                        break;
+        auto* expr = src->Sem().Get<sem::Expression>(node);
+        if (!expr || expr->Stage() == sem::EvaluationStage::kConstant) {
+            continue;  // Don't polyfill @const expressions
+        }
 
-                    default:
-                        break;
-                }
-                if (polyfill.IsValid()) {
-                    auto* replacement = s.b.Call(polyfill, ctx.Clone(call->Declaration()->args));
-                    ctx.Replace(call->Declaration(), replacement);
-                    made_changes = true;
-                }
+        if (auto* call = expr->As<sem::Call>()) {
+            auto* builtin = call->Target()->As<sem::Builtin>();
+            if (!builtin) {
+                continue;
+            }
+            Symbol polyfill;
+            switch (builtin->Type()) {
+                case sem::BuiltinType::kAcosh:
+                    if (builtins.acosh != Level::kNone) {
+                        polyfill = builtin_polyfills.GetOrCreate(
+                            builtin, [&] { return s.acosh(builtin->ReturnType()); });
+                    }
+                    break;
+                case sem::BuiltinType::kAsinh:
+                    if (builtins.asinh) {
+                        polyfill = builtin_polyfills.GetOrCreate(
+                            builtin, [&] { return s.asinh(builtin->ReturnType()); });
+                    }
+                    break;
+                case sem::BuiltinType::kAtanh:
+                    if (builtins.atanh != Level::kNone) {
+                        polyfill = builtin_polyfills.GetOrCreate(
+                            builtin, [&] { return s.atanh(builtin->ReturnType()); });
+                    }
+                    break;
+                case sem::BuiltinType::kClamp:
+                    if (builtins.clamp_int) {
+                        auto& sig = builtin->Signature();
+                        if (sig.parameters[0]->Type()->is_integer_scalar_or_vector()) {
+                            polyfill = builtin_polyfills.GetOrCreate(
+                                builtin, [&] { return s.clampInteger(builtin->ReturnType()); });
+                        }
+                    }
+                    break;
+                case sem::BuiltinType::kCountLeadingZeros:
+                    if (builtins.count_leading_zeros) {
+                        polyfill = builtin_polyfills.GetOrCreate(
+                            builtin, [&] { return s.countLeadingZeros(builtin->ReturnType()); });
+                    }
+                    break;
+                case sem::BuiltinType::kCountTrailingZeros:
+                    if (builtins.count_trailing_zeros) {
+                        polyfill = builtin_polyfills.GetOrCreate(
+                            builtin, [&] { return s.countTrailingZeros(builtin->ReturnType()); });
+                    }
+                    break;
+                case sem::BuiltinType::kExtractBits:
+                    if (builtins.extract_bits != Level::kNone) {
+                        polyfill = builtin_polyfills.GetOrCreate(
+                            builtin, [&] { return s.extractBits(builtin->ReturnType()); });
+                    }
+                    break;
+                case sem::BuiltinType::kFirstLeadingBit:
+                    if (builtins.first_leading_bit) {
+                        polyfill = builtin_polyfills.GetOrCreate(
+                            builtin, [&] { return s.firstLeadingBit(builtin->ReturnType()); });
+                    }
+                    break;
+                case sem::BuiltinType::kFirstTrailingBit:
+                    if (builtins.first_trailing_bit) {
+                        polyfill = builtin_polyfills.GetOrCreate(
+                            builtin, [&] { return s.firstTrailingBit(builtin->ReturnType()); });
+                    }
+                    break;
+                case sem::BuiltinType::kInsertBits:
+                    if (builtins.insert_bits != Level::kNone) {
+                        polyfill = builtin_polyfills.GetOrCreate(
+                            builtin, [&] { return s.insertBits(builtin->ReturnType()); });
+                    }
+                    break;
+                case sem::BuiltinType::kSaturate:
+                    if (builtins.saturate) {
+                        polyfill = builtin_polyfills.GetOrCreate(
+                            builtin, [&] { return s.saturate(builtin->ReturnType()); });
+                    }
+                    break;
+                case sem::BuiltinType::kTextureSampleBaseClampToEdge:
+                    if (builtins.texture_sample_base_clamp_to_edge_2d_f32) {
+                        auto& sig = builtin->Signature();
+                        auto* tex = sig.Parameter(sem::ParameterUsage::kTexture);
+                        if (auto* stex = tex->Type()->As<sem::SampledTexture>()) {
+                            if (stex->type()->Is<sem::F32>()) {
+                                polyfill = builtin_polyfills.GetOrCreate(builtin, [&] {
+                                    return s.textureSampleBaseClampToEdge_2d_f32();
+                                });
+                            }
+                        }
+                    }
+                    break;
+                case sem::BuiltinType::kQuantizeToF16:
+                    if (builtins.quantize_to_vec_f16) {
+                        if (auto* vec = builtin->ReturnType()->As<sem::Vector>()) {
+                            polyfill = builtin_polyfills.GetOrCreate(
+                                builtin, [&] { return s.quantizeToF16(vec); });
+                        }
+                    }
+                    break;
+
+                default:
+                    break;
+            }
+
+            if (polyfill.IsValid()) {
+                auto* replacement = s.b.Call(polyfill, ctx.Clone(call->Declaration()->args));
+                ctx.Replace(call->Declaration(), replacement);
+                made_changes = true;
+            }
+        } else if (auto* bin_op = node->As<ast::BinaryExpression>()) {
+            switch (bin_op->op) {
+                case ast::BinaryOp::kShiftLeft:
+                case ast::BinaryOp::kShiftRight:
+                    if (builtins.bitshift_modulo) {
+                        auto* lhs_ty = src->TypeOf(bin_op->lhs)->UnwrapRef();
+                        auto* rhs_ty = src->TypeOf(bin_op->rhs)->UnwrapRef();
+                        auto* lhs_el_ty = sem::Type::DeepestElementOf(lhs_ty);
+                        const ast::Expression* mask = b.Expr(AInt(lhs_el_ty->Size() * 8 - 1));
+                        if (rhs_ty->Is<sem::Vector>()) {
+                            mask = b.Construct(CreateASTTypeFor(ctx, rhs_ty), mask);
+                        }
+                        auto* mod = b.And(ctx.Clone(bin_op->rhs), mask);
+                        ctx.Replace(bin_op->rhs, mod);
+                        made_changes = true;
+                    }
+                    break;
+                default:
+                    break;
             }
         }
     }
diff --git a/src/tint/transform/builtin_polyfill.h b/src/tint/transform/builtin_polyfill.h
index 231d753..7083aa7 100644
--- a/src/tint/transform/builtin_polyfill.h
+++ b/src/tint/transform/builtin_polyfill.h
@@ -47,6 +47,8 @@
         bool asinh = false;
         /// What level should `atanh` be polyfilled?
         Level atanh = Level::kNone;
+        /// Should the RHS of `<<` and `>>` be wrapped in a modulo bit-width of LHS?
+        bool bitshift_modulo = false;
         /// Should `clamp()` be polyfilled for integer values (scalar or vector)?
         bool clamp_int = false;
         /// Should `countLeadingZeros()` be polyfilled?
@@ -66,7 +68,7 @@
         /// Should `textureSampleBaseClampToEdge()` be polyfilled for texture_2d<f32> textures?
         bool texture_sample_base_clamp_to_edge_2d_f32 = false;
         /// Should the vector form of `quantizeToF16()` be polyfilled with a scalar implementation?
-         /// See crbug.com/tint/1741
+        /// See crbug.com/tint/1741
         bool quantize_to_vec_f16 = false;
     };
 
diff --git a/src/tint/transform/builtin_polyfill_test.cc b/src/tint/transform/builtin_polyfill_test.cc
index 4bff0ff..3b7a42c 100644
--- a/src/tint/transform/builtin_polyfill_test.cc
+++ b/src/tint/transform/builtin_polyfill_test.cc
@@ -399,6 +399,145 @@
 }
 
 ////////////////////////////////////////////////////////////////////////////////
+// bitshiftModulo
+////////////////////////////////////////////////////////////////////////////////
+DataMap polyfillBitshiftModulo() {
+    BuiltinPolyfill::Builtins builtins;
+    builtins.bitshift_modulo = true;
+    DataMap data;
+    data.Add<BuiltinPolyfill::Config>(builtins);
+    return data;
+}
+
+TEST_F(BuiltinPolyfillTest, ShouldRunBitshiftModulo_shl_scalar) {
+    auto* src = R"(
+fn f() {
+  let v = 15u;
+  let r = 1i << v;
+}
+)";
+
+    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
+    EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillBitshiftModulo()));
+}
+
+TEST_F(BuiltinPolyfillTest, ShouldRunBitshiftModulo_shl_vector) {
+    auto* src = R"(
+fn f() {
+  let v = 15u;
+  let r = vec3(1i) << vec3(v);
+}
+)";
+
+    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
+    EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillBitshiftModulo()));
+}
+
+TEST_F(BuiltinPolyfillTest, ShouldRunBitshiftModulo_shr_scalar) {
+    auto* src = R"(
+fn f() {
+  let v = 15u;
+  let r = 1i >> v;
+}
+)";
+
+    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
+    EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillBitshiftModulo()));
+}
+
+TEST_F(BuiltinPolyfillTest, ShouldRunBitshiftModulo_shr_vector) {
+    auto* src = R"(
+fn f() {
+  let v = 15u;
+  let r = vec3(1i) >> vec3(v);
+}
+)";
+
+    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
+    EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillBitshiftModulo()));
+}
+
+TEST_F(BuiltinPolyfillTest, BitshiftModulo_shl_scalar) {
+    auto* src = R"(
+fn f() {
+  let v = 15u;
+  let r = 1i << v;
+}
+)";
+
+    auto* expect = R"(
+fn f() {
+  let v = 15u;
+  let r = (1i << (v & 31));
+}
+)";
+
+    auto got = Run<BuiltinPolyfill>(src, polyfillBitshiftModulo());
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(BuiltinPolyfillTest, BitshiftModulo_shl_vector) {
+    auto* src = R"(
+fn f() {
+  let v = 15u;
+  let r = vec3(1i) << vec3(v);
+}
+)";
+
+    auto* expect = R"(
+fn f() {
+  let v = 15u;
+  let r = (vec3(1i) << (vec3(v) & vec3<u32>(31)));
+}
+)";
+
+    auto got = Run<BuiltinPolyfill>(src, polyfillBitshiftModulo());
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(BuiltinPolyfillTest, BitshiftModulo_shr_scalar) {
+    auto* src = R"(
+fn f() {
+  let v = 15u;
+  let r = 1i >> v;
+}
+)";
+
+    auto* expect = R"(
+fn f() {
+  let v = 15u;
+  let r = (1i >> (v & 31));
+}
+)";
+
+    auto got = Run<BuiltinPolyfill>(src, polyfillBitshiftModulo());
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(BuiltinPolyfillTest, BitshiftModulo_shr_vector) {
+    auto* src = R"(
+fn f() {
+  let v = 15u;
+  let r = vec3(1i) >> vec3(v);
+}
+)";
+
+    auto* expect = R"(
+fn f() {
+  let v = 15u;
+  let r = (vec3(1i) >> (vec3(v) & vec3<u32>(31)));
+}
+)";
+
+    auto got = Run<BuiltinPolyfill>(src, polyfillBitshiftModulo());
+
+    EXPECT_EQ(expect, str(got));
+}
+
+////////////////////////////////////////////////////////////////////////////////
 // clampInteger
 ////////////////////////////////////////////////////////////////////////////////
 DataMap polyfillClampInteger() {
diff --git a/src/tint/writer/glsl/generator_impl.cc b/src/tint/writer/glsl/generator_impl.cc
index a48d71b..50cdbcf 100644
--- a/src/tint/writer/glsl/generator_impl.cc
+++ b/src/tint/writer/glsl/generator_impl.cc
@@ -186,6 +186,7 @@
         transform::BuiltinPolyfill::Builtins polyfills;
         polyfills.acosh = transform::BuiltinPolyfill::Level::kRangeCheck;
         polyfills.atanh = transform::BuiltinPolyfill::Level::kRangeCheck;
+        polyfills.bitshift_modulo = true;
         polyfills.count_leading_zeros = true;
         polyfills.count_trailing_zeros = true;
         polyfills.extract_bits = transform::BuiltinPolyfill::Level::kClampParameters;
diff --git a/src/tint/writer/hlsl/generator_impl.cc b/src/tint/writer/hlsl/generator_impl.cc
index 248da38..92e0cc5 100644
--- a/src/tint/writer/hlsl/generator_impl.cc
+++ b/src/tint/writer/hlsl/generator_impl.cc
@@ -162,6 +162,7 @@
         polyfills.acosh = transform::BuiltinPolyfill::Level::kFull;
         polyfills.asinh = true;
         polyfills.atanh = transform::BuiltinPolyfill::Level::kFull;
+        polyfills.bitshift_modulo = true;
         polyfills.clamp_int = true;
         // TODO(crbug.com/tint/1449): Some of these can map to HLSL's `firstbitlow`
         // and `firstbithigh`.
diff --git a/src/tint/writer/msl/generator_impl.cc b/src/tint/writer/msl/generator_impl.cc
index 2cdbbf9..65b5275 100644
--- a/src/tint/writer/msl/generator_impl.cc
+++ b/src/tint/writer/msl/generator_impl.cc
@@ -171,6 +171,7 @@
         transform::BuiltinPolyfill::Builtins polyfills;
         polyfills.acosh = transform::BuiltinPolyfill::Level::kRangeCheck;
         polyfills.atanh = transform::BuiltinPolyfill::Level::kRangeCheck;
+        polyfills.bitshift_modulo = true;  // crbug.com/tint/1543
         polyfills.clamp_int = true;
         polyfills.extract_bits = transform::BuiltinPolyfill::Level::kClampParameters;
         polyfills.first_leading_bit = true;
diff --git a/src/tint/writer/spirv/generator_impl.cc b/src/tint/writer/spirv/generator_impl.cc
index 44744ea..aa5d449 100644
--- a/src/tint/writer/spirv/generator_impl.cc
+++ b/src/tint/writer/spirv/generator_impl.cc
@@ -52,6 +52,7 @@
         transform::BuiltinPolyfill::Builtins polyfills;
         polyfills.acosh = transform::BuiltinPolyfill::Level::kRangeCheck;
         polyfills.atanh = transform::BuiltinPolyfill::Level::kRangeCheck;
+        polyfills.bitshift_modulo = true;
         polyfills.clamp_int = true;
         polyfills.count_leading_zeros = true;
         polyfills.count_trailing_zeros = true;
diff --git a/test/tint/expressions/binary/left-shift/scalar-scalar/i32.wgsl.expected.dxc.hlsl b/test/tint/expressions/binary/left-shift/scalar-scalar/i32.wgsl.expected.dxc.hlsl
index 50ecc21..0b8fc7b 100644
--- a/test/tint/expressions/binary/left-shift/scalar-scalar/i32.wgsl.expected.dxc.hlsl
+++ b/test/tint/expressions/binary/left-shift/scalar-scalar/i32.wgsl.expected.dxc.hlsl
@@ -2,6 +2,6 @@
 void f() {
   const int a = 1;
   const uint b = 2u;
-  const int r = (a << b);
+  const int r = (a << (b & 31u));
   return;
 }
diff --git a/test/tint/expressions/binary/left-shift/scalar-scalar/i32.wgsl.expected.fxc.hlsl b/test/tint/expressions/binary/left-shift/scalar-scalar/i32.wgsl.expected.fxc.hlsl
index 50ecc21..0b8fc7b 100644
--- a/test/tint/expressions/binary/left-shift/scalar-scalar/i32.wgsl.expected.fxc.hlsl
+++ b/test/tint/expressions/binary/left-shift/scalar-scalar/i32.wgsl.expected.fxc.hlsl
@@ -2,6 +2,6 @@
 void f() {
   const int a = 1;
   const uint b = 2u;
-  const int r = (a << b);
+  const int r = (a << (b & 31u));
   return;
 }
diff --git a/test/tint/expressions/binary/left-shift/scalar-scalar/i32.wgsl.expected.glsl b/test/tint/expressions/binary/left-shift/scalar-scalar/i32.wgsl.expected.glsl
index 67be9ed..459fe5c 100644
--- a/test/tint/expressions/binary/left-shift/scalar-scalar/i32.wgsl.expected.glsl
+++ b/test/tint/expressions/binary/left-shift/scalar-scalar/i32.wgsl.expected.glsl
@@ -3,7 +3,7 @@
 void f() {
   int a = 1;
   uint b = 2u;
-  int r = (a << b);
+  int r = (a << (b & 31u));
 }
 
 layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
diff --git a/test/tint/expressions/binary/left-shift/scalar-scalar/i32.wgsl.expected.msl b/test/tint/expressions/binary/left-shift/scalar-scalar/i32.wgsl.expected.msl
index 3c5ec17..99bc013 100644
--- a/test/tint/expressions/binary/left-shift/scalar-scalar/i32.wgsl.expected.msl
+++ b/test/tint/expressions/binary/left-shift/scalar-scalar/i32.wgsl.expected.msl
@@ -4,7 +4,7 @@
 kernel void f() {
   int const a = 1;
   uint const b = 2u;
-  int const r = as_type<int>((as_type<uint>(a) << b));
+  int const r = as_type<int>((as_type<uint>(a) << (b & 31u)));
   return;
 }
 
diff --git a/test/tint/expressions/binary/left-shift/scalar-scalar/i32.wgsl.expected.spvasm b/test/tint/expressions/binary/left-shift/scalar-scalar/i32.wgsl.expected.spvasm
index d623dc4..999857a 100644
--- a/test/tint/expressions/binary/left-shift/scalar-scalar/i32.wgsl.expected.spvasm
+++ b/test/tint/expressions/binary/left-shift/scalar-scalar/i32.wgsl.expected.spvasm
@@ -1,7 +1,7 @@
 ; SPIR-V
 ; Version: 1.3
 ; Generator: Google Tint Compiler; 0
-; Bound: 10
+; Bound: 12
 ; Schema: 0
                OpCapability Shader
                OpMemoryModel Logical GLSL450
@@ -14,8 +14,10 @@
       %int_1 = OpConstant %int 1
        %uint = OpTypeInt 32 0
      %uint_2 = OpConstant %uint 2
+    %uint_31 = OpConstant %uint 31
           %f = OpFunction %void None %1
           %4 = OpLabel
-          %9 = OpShiftLeftLogical %int %int_1 %uint_2
+         %10 = OpBitwiseAnd %uint %uint_2 %uint_31
+         %11 = OpShiftLeftLogical %int %int_1 %10
                OpReturn
                OpFunctionEnd
diff --git a/test/tint/expressions/binary/left-shift/scalar-scalar/u32.wgsl.expected.dxc.hlsl b/test/tint/expressions/binary/left-shift/scalar-scalar/u32.wgsl.expected.dxc.hlsl
index 9134587..4db204f 100644
--- a/test/tint/expressions/binary/left-shift/scalar-scalar/u32.wgsl.expected.dxc.hlsl
+++ b/test/tint/expressions/binary/left-shift/scalar-scalar/u32.wgsl.expected.dxc.hlsl
@@ -2,6 +2,6 @@
 void f() {
   const uint a = 1u;
   const uint b = 2u;
-  const uint r = (a << b);
+  const uint r = (a << (b & 31u));
   return;
 }
diff --git a/test/tint/expressions/binary/left-shift/scalar-scalar/u32.wgsl.expected.fxc.hlsl b/test/tint/expressions/binary/left-shift/scalar-scalar/u32.wgsl.expected.fxc.hlsl
index 9134587..4db204f 100644
--- a/test/tint/expressions/binary/left-shift/scalar-scalar/u32.wgsl.expected.fxc.hlsl
+++ b/test/tint/expressions/binary/left-shift/scalar-scalar/u32.wgsl.expected.fxc.hlsl
@@ -2,6 +2,6 @@
 void f() {
   const uint a = 1u;
   const uint b = 2u;
-  const uint r = (a << b);
+  const uint r = (a << (b & 31u));
   return;
 }
diff --git a/test/tint/expressions/binary/left-shift/scalar-scalar/u32.wgsl.expected.glsl b/test/tint/expressions/binary/left-shift/scalar-scalar/u32.wgsl.expected.glsl
index 9fc3402..7679fbd 100644
--- a/test/tint/expressions/binary/left-shift/scalar-scalar/u32.wgsl.expected.glsl
+++ b/test/tint/expressions/binary/left-shift/scalar-scalar/u32.wgsl.expected.glsl
@@ -3,7 +3,7 @@
 void f() {
   uint a = 1u;
   uint b = 2u;
-  uint r = (a << b);
+  uint r = (a << (b & 31u));
 }
 
 layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
diff --git a/test/tint/expressions/binary/left-shift/scalar-scalar/u32.wgsl.expected.msl b/test/tint/expressions/binary/left-shift/scalar-scalar/u32.wgsl.expected.msl
index 90ec5fb..389cde8 100644
--- a/test/tint/expressions/binary/left-shift/scalar-scalar/u32.wgsl.expected.msl
+++ b/test/tint/expressions/binary/left-shift/scalar-scalar/u32.wgsl.expected.msl
@@ -4,7 +4,7 @@
 kernel void f() {
   uint const a = 1u;
   uint const b = 2u;
-  uint const r = (a << b);
+  uint const r = (a << (b & 31u));
   return;
 }
 
diff --git a/test/tint/expressions/binary/left-shift/scalar-scalar/u32.wgsl.expected.spvasm b/test/tint/expressions/binary/left-shift/scalar-scalar/u32.wgsl.expected.spvasm
index 17c61e3..4a2c2bb 100644
--- a/test/tint/expressions/binary/left-shift/scalar-scalar/u32.wgsl.expected.spvasm
+++ b/test/tint/expressions/binary/left-shift/scalar-scalar/u32.wgsl.expected.spvasm
@@ -1,7 +1,7 @@
 ; SPIR-V
 ; Version: 1.3
 ; Generator: Google Tint Compiler; 0
-; Bound: 9
+; Bound: 11
 ; Schema: 0
                OpCapability Shader
                OpMemoryModel Logical GLSL450
@@ -13,8 +13,10 @@
        %uint = OpTypeInt 32 0
      %uint_1 = OpConstant %uint 1
      %uint_2 = OpConstant %uint 2
+    %uint_31 = OpConstant %uint 31
           %f = OpFunction %void None %1
           %4 = OpLabel
-          %8 = OpShiftLeftLogical %uint %uint_1 %uint_2
+          %9 = OpBitwiseAnd %uint %uint_2 %uint_31
+         %10 = OpShiftLeftLogical %uint %uint_1 %9
                OpReturn
                OpFunctionEnd
diff --git a/test/tint/expressions/binary/left-shift/vector-vector/i32.wgsl.expected.dxc.hlsl b/test/tint/expressions/binary/left-shift/vector-vector/i32.wgsl.expected.dxc.hlsl
index d43dfb6..1cd18f9 100644
--- a/test/tint/expressions/binary/left-shift/vector-vector/i32.wgsl.expected.dxc.hlsl
+++ b/test/tint/expressions/binary/left-shift/vector-vector/i32.wgsl.expected.dxc.hlsl
@@ -2,6 +2,6 @@
 void f() {
   const int3 a = int3(1, 2, 3);
   const uint3 b = uint3(4u, 5u, 6u);
-  const int3 r = (a << b);
+  const int3 r = (a << (b & (31u).xxx));
   return;
 }
diff --git a/test/tint/expressions/binary/left-shift/vector-vector/i32.wgsl.expected.fxc.hlsl b/test/tint/expressions/binary/left-shift/vector-vector/i32.wgsl.expected.fxc.hlsl
index d43dfb6..1cd18f9 100644
--- a/test/tint/expressions/binary/left-shift/vector-vector/i32.wgsl.expected.fxc.hlsl
+++ b/test/tint/expressions/binary/left-shift/vector-vector/i32.wgsl.expected.fxc.hlsl
@@ -2,6 +2,6 @@
 void f() {
   const int3 a = int3(1, 2, 3);
   const uint3 b = uint3(4u, 5u, 6u);
-  const int3 r = (a << b);
+  const int3 r = (a << (b & (31u).xxx));
   return;
 }
diff --git a/test/tint/expressions/binary/left-shift/vector-vector/i32.wgsl.expected.glsl b/test/tint/expressions/binary/left-shift/vector-vector/i32.wgsl.expected.glsl
index 68dccd8..3421aca 100644
--- a/test/tint/expressions/binary/left-shift/vector-vector/i32.wgsl.expected.glsl
+++ b/test/tint/expressions/binary/left-shift/vector-vector/i32.wgsl.expected.glsl
@@ -3,7 +3,7 @@
 void f() {
   ivec3 a = ivec3(1, 2, 3);
   uvec3 b = uvec3(4u, 5u, 6u);
-  ivec3 r = (a << b);
+  ivec3 r = (a << (b & uvec3(31u)));
 }
 
 layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
diff --git a/test/tint/expressions/binary/left-shift/vector-vector/i32.wgsl.expected.msl b/test/tint/expressions/binary/left-shift/vector-vector/i32.wgsl.expected.msl
index f97db25..a446c3d 100644
--- a/test/tint/expressions/binary/left-shift/vector-vector/i32.wgsl.expected.msl
+++ b/test/tint/expressions/binary/left-shift/vector-vector/i32.wgsl.expected.msl
@@ -4,7 +4,7 @@
 kernel void f() {
   int3 const a = int3(1, 2, 3);
   uint3 const b = uint3(4u, 5u, 6u);
-  int3 const r = as_type<int3>((as_type<uint3>(a) << b));
+  int3 const r = as_type<int3>((as_type<uint3>(a) << (b & uint3(31u))));
   return;
 }
 
diff --git a/test/tint/expressions/binary/left-shift/vector-vector/i32.wgsl.expected.spvasm b/test/tint/expressions/binary/left-shift/vector-vector/i32.wgsl.expected.spvasm
index 42f104e..4f8a50d 100644
--- a/test/tint/expressions/binary/left-shift/vector-vector/i32.wgsl.expected.spvasm
+++ b/test/tint/expressions/binary/left-shift/vector-vector/i32.wgsl.expected.spvasm
@@ -1,7 +1,7 @@
 ; SPIR-V
 ; Version: 1.3
 ; Generator: Google Tint Compiler; 0
-; Bound: 18
+; Bound: 21
 ; Schema: 0
                OpCapability Shader
                OpMemoryModel Logical GLSL450
@@ -22,8 +22,11 @@
      %uint_5 = OpConstant %uint 5
      %uint_6 = OpConstant %uint 6
          %16 = OpConstantComposite %v3uint %uint_4 %uint_5 %uint_6
+    %uint_31 = OpConstant %uint 31
+         %18 = OpConstantComposite %v3uint %uint_31 %uint_31 %uint_31
           %f = OpFunction %void None %1
           %4 = OpLabel
-         %17 = OpShiftLeftLogical %v3int %10 %16
+         %19 = OpBitwiseAnd %v3uint %16 %18
+         %20 = OpShiftLeftLogical %v3int %10 %19
                OpReturn
                OpFunctionEnd
diff --git a/test/tint/expressions/binary/left-shift/vector-vector/u32.wgsl.expected.dxc.hlsl b/test/tint/expressions/binary/left-shift/vector-vector/u32.wgsl.expected.dxc.hlsl
index 2de16f0..5be626e 100644
--- a/test/tint/expressions/binary/left-shift/vector-vector/u32.wgsl.expected.dxc.hlsl
+++ b/test/tint/expressions/binary/left-shift/vector-vector/u32.wgsl.expected.dxc.hlsl
@@ -2,6 +2,6 @@
 void f() {
   const uint3 a = uint3(1u, 2u, 3u);
   const uint3 b = uint3(4u, 5u, 6u);
-  const uint3 r = (a << b);
+  const uint3 r = (a << (b & (31u).xxx));
   return;
 }
diff --git a/test/tint/expressions/binary/left-shift/vector-vector/u32.wgsl.expected.fxc.hlsl b/test/tint/expressions/binary/left-shift/vector-vector/u32.wgsl.expected.fxc.hlsl
index 2de16f0..5be626e 100644
--- a/test/tint/expressions/binary/left-shift/vector-vector/u32.wgsl.expected.fxc.hlsl
+++ b/test/tint/expressions/binary/left-shift/vector-vector/u32.wgsl.expected.fxc.hlsl
@@ -2,6 +2,6 @@
 void f() {
   const uint3 a = uint3(1u, 2u, 3u);
   const uint3 b = uint3(4u, 5u, 6u);
-  const uint3 r = (a << b);
+  const uint3 r = (a << (b & (31u).xxx));
   return;
 }
diff --git a/test/tint/expressions/binary/left-shift/vector-vector/u32.wgsl.expected.glsl b/test/tint/expressions/binary/left-shift/vector-vector/u32.wgsl.expected.glsl
index ba5db9c..a7038f0 100644
--- a/test/tint/expressions/binary/left-shift/vector-vector/u32.wgsl.expected.glsl
+++ b/test/tint/expressions/binary/left-shift/vector-vector/u32.wgsl.expected.glsl
@@ -3,7 +3,7 @@
 void f() {
   uvec3 a = uvec3(1u, 2u, 3u);
   uvec3 b = uvec3(4u, 5u, 6u);
-  uvec3 r = (a << b);
+  uvec3 r = (a << (b & uvec3(31u)));
 }
 
 layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
diff --git a/test/tint/expressions/binary/left-shift/vector-vector/u32.wgsl.expected.msl b/test/tint/expressions/binary/left-shift/vector-vector/u32.wgsl.expected.msl
index c80e57e..d7b8452 100644
--- a/test/tint/expressions/binary/left-shift/vector-vector/u32.wgsl.expected.msl
+++ b/test/tint/expressions/binary/left-shift/vector-vector/u32.wgsl.expected.msl
@@ -4,7 +4,7 @@
 kernel void f() {
   uint3 const a = uint3(1u, 2u, 3u);
   uint3 const b = uint3(4u, 5u, 6u);
-  uint3 const r = (a << b);
+  uint3 const r = (a << (b & uint3(31u)));
   return;
 }
 
diff --git a/test/tint/expressions/binary/left-shift/vector-vector/u32.wgsl.expected.spvasm b/test/tint/expressions/binary/left-shift/vector-vector/u32.wgsl.expected.spvasm
index 27379ec..376820e 100644
--- a/test/tint/expressions/binary/left-shift/vector-vector/u32.wgsl.expected.spvasm
+++ b/test/tint/expressions/binary/left-shift/vector-vector/u32.wgsl.expected.spvasm
@@ -1,7 +1,7 @@
 ; SPIR-V
 ; Version: 1.3
 ; Generator: Google Tint Compiler; 0
-; Bound: 16
+; Bound: 19
 ; Schema: 0
                OpCapability Shader
                OpMemoryModel Logical GLSL450
@@ -20,8 +20,11 @@
      %uint_5 = OpConstant %uint 5
      %uint_6 = OpConstant %uint 6
          %14 = OpConstantComposite %v3uint %uint_4 %uint_5 %uint_6
+    %uint_31 = OpConstant %uint 31
+         %16 = OpConstantComposite %v3uint %uint_31 %uint_31 %uint_31
           %f = OpFunction %void None %1
           %4 = OpLabel
-         %15 = OpShiftLeftLogical %v3uint %10 %14
+         %17 = OpBitwiseAnd %v3uint %14 %16
+         %18 = OpShiftLeftLogical %v3uint %10 %17
                OpReturn
                OpFunctionEnd
diff --git a/test/tint/expressions/binary/right-shift/scalar-scalar/i32.wgsl.expected.dxc.hlsl b/test/tint/expressions/binary/right-shift/scalar-scalar/i32.wgsl.expected.dxc.hlsl
index afd6087..c408e62 100644
--- a/test/tint/expressions/binary/right-shift/scalar-scalar/i32.wgsl.expected.dxc.hlsl
+++ b/test/tint/expressions/binary/right-shift/scalar-scalar/i32.wgsl.expected.dxc.hlsl
@@ -2,6 +2,6 @@
 void f() {
   const int a = 1;
   const uint b = 2u;
-  const int r = (a >> b);
+  const int r = (a >> (b & 31u));
   return;
 }
diff --git a/test/tint/expressions/binary/right-shift/scalar-scalar/i32.wgsl.expected.fxc.hlsl b/test/tint/expressions/binary/right-shift/scalar-scalar/i32.wgsl.expected.fxc.hlsl
index afd6087..c408e62 100644
--- a/test/tint/expressions/binary/right-shift/scalar-scalar/i32.wgsl.expected.fxc.hlsl
+++ b/test/tint/expressions/binary/right-shift/scalar-scalar/i32.wgsl.expected.fxc.hlsl
@@ -2,6 +2,6 @@
 void f() {
   const int a = 1;
   const uint b = 2u;
-  const int r = (a >> b);
+  const int r = (a >> (b & 31u));
   return;
 }
diff --git a/test/tint/expressions/binary/right-shift/scalar-scalar/i32.wgsl.expected.glsl b/test/tint/expressions/binary/right-shift/scalar-scalar/i32.wgsl.expected.glsl
index b51d4f3..66258ec 100644
--- a/test/tint/expressions/binary/right-shift/scalar-scalar/i32.wgsl.expected.glsl
+++ b/test/tint/expressions/binary/right-shift/scalar-scalar/i32.wgsl.expected.glsl
@@ -3,7 +3,7 @@
 void f() {
   int a = 1;
   uint b = 2u;
-  int r = (a >> b);
+  int r = (a >> (b & 31u));
 }
 
 layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
diff --git a/test/tint/expressions/binary/right-shift/scalar-scalar/i32.wgsl.expected.msl b/test/tint/expressions/binary/right-shift/scalar-scalar/i32.wgsl.expected.msl
index 9d9f749..1f3190c 100644
--- a/test/tint/expressions/binary/right-shift/scalar-scalar/i32.wgsl.expected.msl
+++ b/test/tint/expressions/binary/right-shift/scalar-scalar/i32.wgsl.expected.msl
@@ -4,7 +4,7 @@
 kernel void f() {
   int const a = 1;
   uint const b = 2u;
-  int const r = (a >> b);
+  int const r = (a >> (b & 31u));
   return;
 }
 
diff --git a/test/tint/expressions/binary/right-shift/scalar-scalar/i32.wgsl.expected.spvasm b/test/tint/expressions/binary/right-shift/scalar-scalar/i32.wgsl.expected.spvasm
index 10b58f2..e65b24f 100644
--- a/test/tint/expressions/binary/right-shift/scalar-scalar/i32.wgsl.expected.spvasm
+++ b/test/tint/expressions/binary/right-shift/scalar-scalar/i32.wgsl.expected.spvasm
@@ -1,7 +1,7 @@
 ; SPIR-V
 ; Version: 1.3
 ; Generator: Google Tint Compiler; 0
-; Bound: 10
+; Bound: 12
 ; Schema: 0
                OpCapability Shader
                OpMemoryModel Logical GLSL450
@@ -14,8 +14,10 @@
       %int_1 = OpConstant %int 1
        %uint = OpTypeInt 32 0
      %uint_2 = OpConstant %uint 2
+    %uint_31 = OpConstant %uint 31
           %f = OpFunction %void None %1
           %4 = OpLabel
-          %9 = OpShiftRightArithmetic %int %int_1 %uint_2
+         %10 = OpBitwiseAnd %uint %uint_2 %uint_31
+         %11 = OpShiftRightArithmetic %int %int_1 %10
                OpReturn
                OpFunctionEnd
diff --git a/test/tint/expressions/binary/right-shift/scalar-scalar/u32.wgsl.expected.dxc.hlsl b/test/tint/expressions/binary/right-shift/scalar-scalar/u32.wgsl.expected.dxc.hlsl
index b97620f..1cdc247 100644
--- a/test/tint/expressions/binary/right-shift/scalar-scalar/u32.wgsl.expected.dxc.hlsl
+++ b/test/tint/expressions/binary/right-shift/scalar-scalar/u32.wgsl.expected.dxc.hlsl
@@ -2,6 +2,6 @@
 void f() {
   const uint a = 1u;
   const uint b = 2u;
-  const uint r = (a >> b);
+  const uint r = (a >> (b & 31u));
   return;
 }
diff --git a/test/tint/expressions/binary/right-shift/scalar-scalar/u32.wgsl.expected.fxc.hlsl b/test/tint/expressions/binary/right-shift/scalar-scalar/u32.wgsl.expected.fxc.hlsl
index b97620f..1cdc247 100644
--- a/test/tint/expressions/binary/right-shift/scalar-scalar/u32.wgsl.expected.fxc.hlsl
+++ b/test/tint/expressions/binary/right-shift/scalar-scalar/u32.wgsl.expected.fxc.hlsl
@@ -2,6 +2,6 @@
 void f() {
   const uint a = 1u;
   const uint b = 2u;
-  const uint r = (a >> b);
+  const uint r = (a >> (b & 31u));
   return;
 }
diff --git a/test/tint/expressions/binary/right-shift/scalar-scalar/u32.wgsl.expected.glsl b/test/tint/expressions/binary/right-shift/scalar-scalar/u32.wgsl.expected.glsl
index d81e2de..bdd0e58 100644
--- a/test/tint/expressions/binary/right-shift/scalar-scalar/u32.wgsl.expected.glsl
+++ b/test/tint/expressions/binary/right-shift/scalar-scalar/u32.wgsl.expected.glsl
@@ -3,7 +3,7 @@
 void f() {
   uint a = 1u;
   uint b = 2u;
-  uint r = (a >> b);
+  uint r = (a >> (b & 31u));
 }
 
 layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
diff --git a/test/tint/expressions/binary/right-shift/scalar-scalar/u32.wgsl.expected.msl b/test/tint/expressions/binary/right-shift/scalar-scalar/u32.wgsl.expected.msl
index 68036b5..448647e 100644
--- a/test/tint/expressions/binary/right-shift/scalar-scalar/u32.wgsl.expected.msl
+++ b/test/tint/expressions/binary/right-shift/scalar-scalar/u32.wgsl.expected.msl
@@ -4,7 +4,7 @@
 kernel void f() {
   uint const a = 1u;
   uint const b = 2u;
-  uint const r = (a >> b);
+  uint const r = (a >> (b & 31u));
   return;
 }
 
diff --git a/test/tint/expressions/binary/right-shift/scalar-scalar/u32.wgsl.expected.spvasm b/test/tint/expressions/binary/right-shift/scalar-scalar/u32.wgsl.expected.spvasm
index 04adcf8..8517337 100644
--- a/test/tint/expressions/binary/right-shift/scalar-scalar/u32.wgsl.expected.spvasm
+++ b/test/tint/expressions/binary/right-shift/scalar-scalar/u32.wgsl.expected.spvasm
@@ -1,7 +1,7 @@
 ; SPIR-V
 ; Version: 1.3
 ; Generator: Google Tint Compiler; 0
-; Bound: 9
+; Bound: 11
 ; Schema: 0
                OpCapability Shader
                OpMemoryModel Logical GLSL450
@@ -13,8 +13,10 @@
        %uint = OpTypeInt 32 0
      %uint_1 = OpConstant %uint 1
      %uint_2 = OpConstant %uint 2
+    %uint_31 = OpConstant %uint 31
           %f = OpFunction %void None %1
           %4 = OpLabel
-          %8 = OpShiftRightLogical %uint %uint_1 %uint_2
+          %9 = OpBitwiseAnd %uint %uint_2 %uint_31
+         %10 = OpShiftRightLogical %uint %uint_1 %9
                OpReturn
                OpFunctionEnd
diff --git a/test/tint/expressions/binary/right-shift/vector-vector/i32.wgsl.expected.dxc.hlsl b/test/tint/expressions/binary/right-shift/vector-vector/i32.wgsl.expected.dxc.hlsl
index 2520c5e..0421b5f 100644
--- a/test/tint/expressions/binary/right-shift/vector-vector/i32.wgsl.expected.dxc.hlsl
+++ b/test/tint/expressions/binary/right-shift/vector-vector/i32.wgsl.expected.dxc.hlsl
@@ -2,6 +2,6 @@
 void f() {
   const int3 a = int3(1, 2, 3);
   const uint3 b = uint3(4u, 5u, 6u);
-  const int3 r = (a >> b);
+  const int3 r = (a >> (b & (31u).xxx));
   return;
 }
diff --git a/test/tint/expressions/binary/right-shift/vector-vector/i32.wgsl.expected.fxc.hlsl b/test/tint/expressions/binary/right-shift/vector-vector/i32.wgsl.expected.fxc.hlsl
index 2520c5e..0421b5f 100644
--- a/test/tint/expressions/binary/right-shift/vector-vector/i32.wgsl.expected.fxc.hlsl
+++ b/test/tint/expressions/binary/right-shift/vector-vector/i32.wgsl.expected.fxc.hlsl
@@ -2,6 +2,6 @@
 void f() {
   const int3 a = int3(1, 2, 3);
   const uint3 b = uint3(4u, 5u, 6u);
-  const int3 r = (a >> b);
+  const int3 r = (a >> (b & (31u).xxx));
   return;
 }
diff --git a/test/tint/expressions/binary/right-shift/vector-vector/i32.wgsl.expected.glsl b/test/tint/expressions/binary/right-shift/vector-vector/i32.wgsl.expected.glsl
index ca2842c..191817d 100644
--- a/test/tint/expressions/binary/right-shift/vector-vector/i32.wgsl.expected.glsl
+++ b/test/tint/expressions/binary/right-shift/vector-vector/i32.wgsl.expected.glsl
@@ -3,7 +3,7 @@
 void f() {
   ivec3 a = ivec3(1, 2, 3);
   uvec3 b = uvec3(4u, 5u, 6u);
-  ivec3 r = (a >> b);
+  ivec3 r = (a >> (b & uvec3(31u)));
 }
 
 layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
diff --git a/test/tint/expressions/binary/right-shift/vector-vector/i32.wgsl.expected.msl b/test/tint/expressions/binary/right-shift/vector-vector/i32.wgsl.expected.msl
index 5c3fd63..d691234 100644
--- a/test/tint/expressions/binary/right-shift/vector-vector/i32.wgsl.expected.msl
+++ b/test/tint/expressions/binary/right-shift/vector-vector/i32.wgsl.expected.msl
@@ -4,7 +4,7 @@
 kernel void f() {
   int3 const a = int3(1, 2, 3);
   uint3 const b = uint3(4u, 5u, 6u);
-  int3 const r = (a >> b);
+  int3 const r = (a >> (b & uint3(31u)));
   return;
 }
 
diff --git a/test/tint/expressions/binary/right-shift/vector-vector/i32.wgsl.expected.spvasm b/test/tint/expressions/binary/right-shift/vector-vector/i32.wgsl.expected.spvasm
index 227bd02..317ba4a 100644
--- a/test/tint/expressions/binary/right-shift/vector-vector/i32.wgsl.expected.spvasm
+++ b/test/tint/expressions/binary/right-shift/vector-vector/i32.wgsl.expected.spvasm
@@ -1,7 +1,7 @@
 ; SPIR-V
 ; Version: 1.3
 ; Generator: Google Tint Compiler; 0
-; Bound: 18
+; Bound: 21
 ; Schema: 0
                OpCapability Shader
                OpMemoryModel Logical GLSL450
@@ -22,8 +22,11 @@
      %uint_5 = OpConstant %uint 5
      %uint_6 = OpConstant %uint 6
          %16 = OpConstantComposite %v3uint %uint_4 %uint_5 %uint_6
+    %uint_31 = OpConstant %uint 31
+         %18 = OpConstantComposite %v3uint %uint_31 %uint_31 %uint_31
           %f = OpFunction %void None %1
           %4 = OpLabel
-         %17 = OpShiftRightArithmetic %v3int %10 %16
+         %19 = OpBitwiseAnd %v3uint %16 %18
+         %20 = OpShiftRightArithmetic %v3int %10 %19
                OpReturn
                OpFunctionEnd
diff --git a/test/tint/expressions/binary/right-shift/vector-vector/u32.wgsl.expected.dxc.hlsl b/test/tint/expressions/binary/right-shift/vector-vector/u32.wgsl.expected.dxc.hlsl
index 1c57ee7..8a9e6df 100644
--- a/test/tint/expressions/binary/right-shift/vector-vector/u32.wgsl.expected.dxc.hlsl
+++ b/test/tint/expressions/binary/right-shift/vector-vector/u32.wgsl.expected.dxc.hlsl
@@ -2,6 +2,6 @@
 void f() {
   const uint3 a = uint3(1u, 2u, 3u);
   const uint3 b = uint3(4u, 5u, 6u);
-  const uint3 r = (a >> b);
+  const uint3 r = (a >> (b & (31u).xxx));
   return;
 }
diff --git a/test/tint/expressions/binary/right-shift/vector-vector/u32.wgsl.expected.fxc.hlsl b/test/tint/expressions/binary/right-shift/vector-vector/u32.wgsl.expected.fxc.hlsl
index 1c57ee7..8a9e6df 100644
--- a/test/tint/expressions/binary/right-shift/vector-vector/u32.wgsl.expected.fxc.hlsl
+++ b/test/tint/expressions/binary/right-shift/vector-vector/u32.wgsl.expected.fxc.hlsl
@@ -2,6 +2,6 @@
 void f() {
   const uint3 a = uint3(1u, 2u, 3u);
   const uint3 b = uint3(4u, 5u, 6u);
-  const uint3 r = (a >> b);
+  const uint3 r = (a >> (b & (31u).xxx));
   return;
 }
diff --git a/test/tint/expressions/binary/right-shift/vector-vector/u32.wgsl.expected.glsl b/test/tint/expressions/binary/right-shift/vector-vector/u32.wgsl.expected.glsl
index a292377..1b4c41c 100644
--- a/test/tint/expressions/binary/right-shift/vector-vector/u32.wgsl.expected.glsl
+++ b/test/tint/expressions/binary/right-shift/vector-vector/u32.wgsl.expected.glsl
@@ -3,7 +3,7 @@
 void f() {
   uvec3 a = uvec3(1u, 2u, 3u);
   uvec3 b = uvec3(4u, 5u, 6u);
-  uvec3 r = (a >> b);
+  uvec3 r = (a >> (b & uvec3(31u)));
 }
 
 layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
diff --git a/test/tint/expressions/binary/right-shift/vector-vector/u32.wgsl.expected.msl b/test/tint/expressions/binary/right-shift/vector-vector/u32.wgsl.expected.msl
index b5aa9e9..fe0ab3d 100644
--- a/test/tint/expressions/binary/right-shift/vector-vector/u32.wgsl.expected.msl
+++ b/test/tint/expressions/binary/right-shift/vector-vector/u32.wgsl.expected.msl
@@ -4,7 +4,7 @@
 kernel void f() {
   uint3 const a = uint3(1u, 2u, 3u);
   uint3 const b = uint3(4u, 5u, 6u);
-  uint3 const r = (a >> b);
+  uint3 const r = (a >> (b & uint3(31u)));
   return;
 }
 
diff --git a/test/tint/expressions/binary/right-shift/vector-vector/u32.wgsl.expected.spvasm b/test/tint/expressions/binary/right-shift/vector-vector/u32.wgsl.expected.spvasm
index 17932f2..5aeebd1 100644
--- a/test/tint/expressions/binary/right-shift/vector-vector/u32.wgsl.expected.spvasm
+++ b/test/tint/expressions/binary/right-shift/vector-vector/u32.wgsl.expected.spvasm
@@ -1,7 +1,7 @@
 ; SPIR-V
 ; Version: 1.3
 ; Generator: Google Tint Compiler; 0
-; Bound: 16
+; Bound: 19
 ; Schema: 0
                OpCapability Shader
                OpMemoryModel Logical GLSL450
@@ -20,8 +20,11 @@
      %uint_5 = OpConstant %uint 5
      %uint_6 = OpConstant %uint 6
          %14 = OpConstantComposite %v3uint %uint_4 %uint_5 %uint_6
+    %uint_31 = OpConstant %uint 31
+         %16 = OpConstantComposite %v3uint %uint_31 %uint_31 %uint_31
           %f = OpFunction %void None %1
           %4 = OpLabel
-         %15 = OpShiftRightLogical %v3uint %10 %14
+         %17 = OpBitwiseAnd %v3uint %14 %16
+         %18 = OpShiftRightLogical %v3uint %10 %17
                OpReturn
                OpFunctionEnd