Import Tint changes from Dawn

Changes:
  - 51cf5838e2f9654c3073e79fed3c84c23377af80 [tint][sem] Add more Declaration() methods by Ben Clayton <bclayton@google.com>
  - 5a6b701b2dbf73868275fb13bdc6073b7b8642d5 Add ClampFragDepth transform to GLSL writer. by Stephen White <senorblanco@chromium.org>
  - 1b00745fc9e02ba00ff169c73d8b4bd2340ae6e5 [tint][ir] Don't emit instructions with abstract types. by Ben Clayton <bclayton@google.com>
GitOrigin-RevId: 51cf5838e2f9654c3073e79fed3c84c23377af80
Change-Id: Ib38a806a1c2db501d4016f1870ce972953c9cba5
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/174641
Reviewed-by: Ben Clayton <bclayton@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/src/tint/cmd/tint/main.cc b/src/tint/cmd/tint/main.cc
index 4d18bb9..1601a76 100644
--- a/src/tint/cmd/tint/main.cc
+++ b/src/tint/cmd/tint/main.cc
@@ -1080,11 +1080,18 @@
         gen_options.texture_builtins_from_uniform = std::move(textureBuiltinsFromUniform);
 
         auto entry_point = inspector.GetEntryPoint(entry_point_name);
+        uint32_t offset = entry_point.push_constant_size;
 
         if (entry_point.instance_index_used) {
             // Place the first_instance push constant member after user-defined push constants (if
             // any).
-            gen_options.first_instance_offset = entry_point.push_constant_size;
+            gen_options.first_instance_offset = offset;
+            offset += 4;
+        }
+        if (entry_point.frag_depth_used) {
+            gen_options.min_depth_offset = offset + 0;
+            gen_options.max_depth_offset = offset + 4;
+            offset += 8;
         }
 
         auto result = tint::glsl::writer::Generate(prg, gen_options, entry_point_name);
diff --git a/src/tint/lang/glsl/writer/ast_printer/ast_printer.cc b/src/tint/lang/glsl/writer/ast_printer/ast_printer.cc
index 326c1a2..764b779 100644
--- a/src/tint/lang/glsl/writer/ast_printer/ast_printer.cc
+++ b/src/tint/lang/glsl/writer/ast_printer/ast_printer.cc
@@ -61,6 +61,7 @@
 #include "src/tint/lang/wgsl/ast/transform/binding_remapper.h"
 #include "src/tint/lang/wgsl/ast/transform/builtin_polyfill.h"
 #include "src/tint/lang/wgsl/ast/transform/canonicalize_entry_point_io.h"
+#include "src/tint/lang/wgsl/ast/transform/clamp_frag_depth.h"
 #include "src/tint/lang/wgsl/ast/transform/demote_to_helper.h"
 #include "src/tint/lang/wgsl/ast/transform/direct_variable_access.h"
 #include "src/tint/lang/wgsl/ast/transform/disable_uniformity_analysis.h"
@@ -206,6 +207,10 @@
 
     manager.Add<ast::transform::OffsetFirstIndex>();
 
+    // ClampFragDepth must come before CanonicalizeEntryPointIO, or the assignments to FragDepth are
+    // lost
+    manager.Add<ast::transform::ClampFragDepth>();
+
     // CanonicalizeEntryPointIO must come after Robustness
     manager.Add<ast::transform::CanonicalizeEntryPointIO>();
 
@@ -249,6 +254,8 @@
 
     data.Add<ast::transform::OffsetFirstIndex::Config>(std::nullopt, options.first_instance_offset);
 
+    data.Add<ast::transform::ClampFragDepth::Config>(options.min_depth_offset,
+                                                     options.max_depth_offset);
     SanitizedResult result;
     ast::transform::DataMap outputs;
     result.program = manager.Run(in, data, outputs);
diff --git a/src/tint/lang/glsl/writer/common/options.h b/src/tint/lang/glsl/writer/common/options.h
index 7545366..62cd80c 100644
--- a/src/tint/lang/glsl/writer/common/options.h
+++ b/src/tint/lang/glsl/writer/common/options.h
@@ -82,6 +82,12 @@
     /// Offset of the firstInstance push constant.
     std::optional<int32_t> first_instance_offset;
 
+    /// Offset of the minDepth push constant.
+    std::optional<uint32_t> min_depth_offset;
+
+    /// Offset of the maxDepth push constant.
+    std::optional<uint32_t> max_depth_offset;
+
     /// Options used to map WGSL textureNumLevels/textureNumSamples builtins to internal uniform
     /// buffer values. If not specified, emits corresponding GLSL builtins
     /// textureQueryLevels/textureSamples directly.
@@ -98,6 +104,8 @@
                  binding_remapper_options,
                  external_texture_options,
                  first_instance_offset,
+                 min_depth_offset,
+                 max_depth_offset,
                  texture_builtins_from_uniform);
 };
 
diff --git a/src/tint/lang/spirv/writer/ast_printer/ast_printer.cc b/src/tint/lang/spirv/writer/ast_printer/ast_printer.cc
index 4272172..c782db5 100644
--- a/src/tint/lang/spirv/writer/ast_printer/ast_printer.cc
+++ b/src/tint/lang/spirv/writer/ast_printer/ast_printer.cc
@@ -66,6 +66,7 @@
 
     if (options.clamp_frag_depth) {
         manager.Add<ast::transform::ClampFragDepth>();
+        data.Add<ast::transform::ClampFragDepth::Config>(0, 4);
     }
 
     manager.Add<ast::transform::DisableUniformityAnalysis>();
diff --git a/src/tint/lang/wgsl/ast/transform/clamp_frag_depth.cc b/src/tint/lang/wgsl/ast/transform/clamp_frag_depth.cc
index 59e6dd6..2b5e6a2 100644
--- a/src/tint/lang/wgsl/ast/transform/clamp_frag_depth.cc
+++ b/src/tint/lang/wgsl/ast/transform/clamp_frag_depth.cc
@@ -35,6 +35,7 @@
 #include "src/tint/lang/wgsl/ast/function.h"
 #include "src/tint/lang/wgsl/ast/module.h"
 #include "src/tint/lang/wgsl/ast/struct.h"
+#include "src/tint/lang/wgsl/ast/transform/push_constant_helper.h"
 #include "src/tint/lang/wgsl/program/clone_context.h"
 #include "src/tint/lang/wgsl/program/program_builder.h"
 #include "src/tint/lang/wgsl/resolver/resolve.h"
@@ -45,6 +46,7 @@
 #include "src/tint/utils/macros/scoped_assignment.h"
 
 TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::ClampFragDepth);
+TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::ClampFragDepth::Config);
 
 namespace tint::ast::transform {
 
@@ -63,49 +65,37 @@
 
     /// Runs the transform
     /// @returns the new program or SkipTransform if the transform is not required
-    Transform::ApplyResult Run() {
-        // Abort on any use of push constants in the module.
-        for (auto* global : src.AST().GlobalVariables()) {
-            if (auto* var = global->As<ast::Var>()) {
-                auto* v = src.Sem().Get(var);
-                if (TINT_UNLIKELY(v->AddressSpace() == core::AddressSpace::kPushConstant)) {
-                    TINT_ICE()
-                        << "ClampFragDepth doesn't know how to handle module that already use push "
-                           "constants";
-                    return resolver::Resolve(b);
-                }
-            }
-        }
-
-        if (!ShouldRun()) {
+    Transform::ApplyResult Run(const DataMap& inputs) {
+        const Config* cfg = inputs.Get<Config>();
+        if (!cfg || !cfg->min_depth_offset.has_value() || !cfg->max_depth_offset.has_value()) {
             return SkipTransform;
         }
 
+        PushConstantHelper push_constant_helper(ctx);
+
         // At least one entry-point needs clamping. Add the following to the module:
         //
         //   enable chromium_experimental_push_constant;
         //
-        //   struct FragDepthClampArgs {
-        //       min : f32,
-        //       max : f32,
+        //   struct PushConstants {
+        //       min_depth : f32,
+        //       max_depth : f32,
         //   }
-        //   var<push_constant> frag_depth_clamp_args : FragDepthClampArgs;
+        //   var<push_constant> push_constants : PushConstants;
         //
         //   fn clamp_frag_depth(v : f32) -> f32 {
-        //       return clamp(v, frag_depth_clamp_args.min, frag_depth_clamp_args.max);
+        //       return clamp(v, push_constants.min, push_constants.max_depth);
         //   }
-        b.Enable(wgsl::Extension::kChromiumExperimentalPushConstant);
 
-        b.Structure(b.Symbols().New("FragDepthClampArgs"),
-                    Vector{b.Member("min", b.ty.f32()), b.Member("max", b.ty.f32())});
+        push_constant_helper.InsertMember("min_depth", b.ty.f32(), *cfg->min_depth_offset);
+        push_constant_helper.InsertMember("max_depth", b.ty.f32(), *cfg->max_depth_offset);
 
-        auto args_sym = b.Symbols().New("frag_depth_clamp_args");
-        b.GlobalVar(args_sym, b.ty("FragDepthClampArgs"), core::AddressSpace::kPushConstant);
+        Symbol buffer_name = push_constant_helper.Run();
 
         auto base_fn_sym = b.Symbols().New("clamp_frag_depth");
         b.Func(base_fn_sym, Vector{b.Param("v", b.ty.f32())}, b.ty.f32(),
-               Vector{b.Return(b.Call("clamp", "v", b.MemberAccessor(args_sym, "min"),
-                                      b.MemberAccessor(args_sym, "max")))});
+               Vector{b.Return(b.Call("clamp", "v", b.MemberAccessor(buffer_name, "min_depth"),
+                                      b.MemberAccessor(buffer_name, "max_depth")))});
 
         // If true, the currently cloned function returns frag depth directly as a scalar
         bool returns_frag_depth_as_value = false;
@@ -185,17 +175,6 @@
     }
 
   private:
-    /// @returns true if the transform should run
-    bool ShouldRun() {
-        for (auto* fn : src.AST().Functions()) {
-            if (fn->PipelineStage() == ast::PipelineStage::kFragment &&
-                (ReturnsFragDepthAsValue(fn) || ReturnsFragDepthInStruct(fn))) {
-                return true;
-            }
-        }
-
-        return false;
-    }
     /// @param attrs the attributes to examine
     /// @returns true if @p attrs contains a `@builtin(frag_depth)` attribute
     bool ContainsFragDepth(VectorRef<const ast::Attribute*> attrs) {
@@ -237,9 +216,15 @@
 ClampFragDepth::~ClampFragDepth() = default;
 
 ast::transform::Transform::ApplyResult ClampFragDepth::Apply(const Program& src,
-                                                             const ast::transform::DataMap&,
+                                                             const ast::transform::DataMap& inputs,
                                                              ast::transform::DataMap&) const {
-    return State{src}.Run();
+    return State{src}.Run(inputs);
 }
 
+ClampFragDepth::Config::Config(std::optional<uint32_t> min_depth_off,
+                               std::optional<uint32_t> max_depth_off)
+    : min_depth_offset(min_depth_off), max_depth_offset(max_depth_off) {}
+
+ClampFragDepth::Config::~Config() = default;
+
 }  // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/clamp_frag_depth.h b/src/tint/lang/wgsl/ast/transform/clamp_frag_depth.h
index 5bcd3ea..599b91c 100644
--- a/src/tint/lang/wgsl/ast/transform/clamp_frag_depth.h
+++ b/src/tint/lang/wgsl/ast/transform/clamp_frag_depth.h
@@ -69,6 +69,23 @@
     /// Destructor
     ~ClampFragDepth() override;
 
+    /// Transform configuration options
+    struct Config final : public Castable<Config, ast::transform::Data> {
+        /// Constructor
+        /// @param min_depth_off Offset of the minDepth push constant
+        /// @param max_depth_off Offset of the maxDepth push constant
+        Config(std::optional<uint32_t> min_depth_off, std::optional<uint32_t> max_depth_off);
+
+        /// Destructor
+        ~Config() override;
+
+        /// Offset of the min_depth push constant
+        std::optional<uint32_t> min_depth_offset;
+
+        /// Offset of the min_depth push constant
+        std::optional<uint32_t> max_depth_offset;
+    };
+
     /// @copydoc ast::transform::Transform::Apply
     ApplyResult Apply(const Program& program,
                       const ast::transform::DataMap& inputs,
diff --git a/src/tint/lang/wgsl/ast/transform/clamp_frag_depth_test.cc b/src/tint/lang/wgsl/ast/transform/clamp_frag_depth_test.cc
index cc09c6f..97f1f31 100644
--- a/src/tint/lang/wgsl/ast/transform/clamp_frag_depth_test.cc
+++ b/src/tint/lang/wgsl/ast/transform/clamp_frag_depth_test.cc
@@ -40,48 +40,102 @@
     EXPECT_FALSE(ShouldRun<ClampFragDepth>(src));
 }
 
-TEST_F(ClampFragDepthTest, ShouldRunNoFragmentShader) {
+TEST_F(ClampFragDepthTest, ShouldRunNoConfig) {
     auto* src = R"(
-        fn f() -> f32 {
+        @fragment fn main() -> @builtin(frag_depth) f32 {
             return 0.0;
         }
-
-        @compute @workgroup_size(1) fn cs() {
-        }
-
-        @vertex fn vs() -> @builtin(position) vec4<f32> {
-            return vec4<f32>();
-        }
     )";
 
     EXPECT_FALSE(ShouldRun<ClampFragDepth>(src));
 }
 
-TEST_F(ClampFragDepthTest, ShouldRunFragmentShaderNoReturnType) {
+TEST_F(ClampFragDepthTest, ShouldRunNoMin) {
     auto* src = R"(
-        @fragment fn main() {
-        }
-    )";
-
-    EXPECT_FALSE(ShouldRun<ClampFragDepth>(src));
-}
-
-TEST_F(ClampFragDepthTest, ShouldRunFragmentShaderNoFragDepth) {
-    auto* src = R"(
-        @fragment fn main() -> @location(0) f32 {
+        @fragment fn main() -> @builtin(frag_depth) f32 {
             return 0.0;
         }
+    )";
 
-        struct S {
-            @location(0) a : f32,
-            @builtin(sample_mask) b : u32,
-        }
-        @fragment fn main2() -> S {
-            return S();
+    DataMap config;
+    config.Add<ClampFragDepth::Config>(std::nullopt, 4);
+
+    EXPECT_FALSE(ShouldRun<ClampFragDepth>(src, config));
+}
+
+TEST_F(ClampFragDepthTest, ShouldRunNoMinNoMax) {
+    auto* src = R"(
+        @fragment fn main() -> @builtin(frag_depth) f32 {
+            return 0.0;
         }
     )";
 
-    EXPECT_FALSE(ShouldRun<ClampFragDepth>(src));
+    DataMap config;
+    config.Add<ClampFragDepth::Config>(0, std::nullopt);
+
+    EXPECT_FALSE(ShouldRun<ClampFragDepth>(src, config));
+}
+
+TEST_F(ClampFragDepthTest, ShouldRun) {
+    auto* src = R"(
+        @fragment fn main() -> @builtin(frag_depth) f32 {
+            return 0.0;
+        }
+    )";
+
+    DataMap config;
+    config.Add<ClampFragDepth::Config>(0, 4);
+
+    EXPECT_TRUE(ShouldRun<ClampFragDepth>(src, config));
+}
+
+TEST_F(ClampFragDepthTest, ExistingPushConstant) {
+    auto* src = R"(
+        enable chromium_experimental_push_constant;
+
+        struct PushConstants {
+          a : f32,
+        }
+
+        var<push_constant> push_constants : PushConstants;
+        @fragment fn main() -> @builtin(frag_depth) f32 {
+            return push_constants.a;
+        }
+
+    )";
+
+    auto* expect = R"(
+enable chromium_experimental_push_constant;
+
+struct PushConstants_1 {
+  a : f32,
+  /* @offset(4) */
+  min_depth : f32,
+  /* @offset(8) */
+  max_depth : f32,
+}
+
+fn clamp_frag_depth(v : f32) -> f32 {
+  return clamp(v, push_constants.min_depth, push_constants.max_depth);
+}
+
+struct PushConstants {
+  a : f32,
+}
+
+var<push_constant> push_constants : PushConstants_1;
+
+@fragment
+fn main() -> @builtin(frag_depth) f32 {
+  return clamp_frag_depth(push_constants.a);
+}
+)";
+
+    DataMap config;
+    config.Add<ClampFragDepth::Config>(4, 8);
+
+    auto got = Run<ClampFragDepth>(src, config);
+    EXPECT_EQ(expect, str(got));
 }
 
 TEST_F(ClampFragDepthTest, ShouldRunFragDepthAsDirectReturn) {
@@ -91,7 +145,10 @@
         }
     )";
 
-    EXPECT_TRUE(ShouldRun<ClampFragDepth>(src));
+    DataMap config;
+    config.Add<ClampFragDepth::Config>(0, 4);
+
+    EXPECT_TRUE(ShouldRun<ClampFragDepth>(src, config));
 }
 
 TEST_F(ClampFragDepthTest, ShouldRunFragDepthInStruct) {
@@ -106,7 +163,10 @@
         }
     )";
 
-    EXPECT_TRUE(ShouldRun<ClampFragDepth>(src));
+    DataMap config;
+    config.Add<ClampFragDepth::Config>(0, 4);
+
+    EXPECT_TRUE(ShouldRun<ClampFragDepth>(src, config));
 }
 
 TEST_F(ClampFragDepthTest, SingleReturnOfFragDepth) {
@@ -119,15 +179,17 @@
     auto* expect = R"(
 enable chromium_experimental_push_constant;
 
-struct FragDepthClampArgs {
-  min : f32,
-  max : f32,
+struct PushConstants {
+  /* @offset(0) */
+  min_depth : f32,
+  /* @offset(4) */
+  max_depth : f32,
 }
 
-var<push_constant> frag_depth_clamp_args : FragDepthClampArgs;
+var<push_constant> push_constants : PushConstants;
 
 fn clamp_frag_depth(v : f32) -> f32 {
-  return clamp(v, frag_depth_clamp_args.min, frag_depth_clamp_args.max);
+  return clamp(v, push_constants.min_depth, push_constants.max_depth);
 }
 
 @fragment
@@ -136,7 +198,9 @@
 }
 )";
 
-    auto got = Run<ClampFragDepth>(src);
+    DataMap config;
+    config.Add<ClampFragDepth::Config>(0, 4);
+    auto got = Run<ClampFragDepth>(src, config);
     EXPECT_EQ(expect, str(got));
 }
 
@@ -153,15 +217,17 @@
     auto* expect = R"(
 enable chromium_experimental_push_constant;
 
-struct FragDepthClampArgs {
-  min : f32,
-  max : f32,
+struct PushConstants {
+  /* @offset(0) */
+  min_depth : f32,
+  /* @offset(4) */
+  max_depth : f32,
 }
 
-var<push_constant> frag_depth_clamp_args : FragDepthClampArgs;
+var<push_constant> push_constants : PushConstants;
 
 fn clamp_frag_depth(v : f32) -> f32 {
-  return clamp(v, frag_depth_clamp_args.min, frag_depth_clamp_args.max);
+  return clamp(v, push_constants.min_depth, push_constants.max_depth);
 }
 
 @fragment
@@ -173,7 +239,9 @@
 }
 )";
 
-    auto got = Run<ClampFragDepth>(src);
+    DataMap config;
+    config.Add<ClampFragDepth::Config>(0, 4);
+    auto got = Run<ClampFragDepth>(src, config);
     EXPECT_EQ(expect, str(got));
 }
 
@@ -190,15 +258,17 @@
     auto* expect = R"(
 enable chromium_experimental_push_constant;
 
-struct FragDepthClampArgs {
-  min : f32,
-  max : f32,
+struct PushConstants {
+  /* @offset(0) */
+  min_depth : f32,
+  /* @offset(4) */
+  max_depth : f32,
 }
 
-var<push_constant> frag_depth_clamp_args : FragDepthClampArgs;
+var<push_constant> push_constants : PushConstants;
 
 fn clamp_frag_depth(v : f32) -> f32 {
-  return clamp(v, frag_depth_clamp_args.min, frag_depth_clamp_args.max);
+  return clamp(v, push_constants.min_depth, push_constants.max_depth);
 }
 
 @fragment
@@ -212,7 +282,9 @@
 }
 )";
 
-    auto got = Run<ClampFragDepth>(src);
+    DataMap config;
+    config.Add<ClampFragDepth::Config>(0, 4);
+    auto got = Run<ClampFragDepth>(src, config);
     EXPECT_EQ(expect, str(got));
 }
 
@@ -230,15 +302,17 @@
     auto* expect = R"(
 enable chromium_experimental_push_constant;
 
-struct FragDepthClampArgs {
-  min : f32,
-  max : f32,
+struct PushConstants {
+  /* @offset(0) */
+  min_depth : f32,
+  /* @offset(4) */
+  max_depth : f32,
 }
 
-var<push_constant> frag_depth_clamp_args : FragDepthClampArgs;
+var<push_constant> push_constants : PushConstants;
 
 fn clamp_frag_depth(v : f32) -> f32 {
-  return clamp(v, frag_depth_clamp_args.min, frag_depth_clamp_args.max);
+  return clamp(v, push_constants.min_depth, push_constants.max_depth);
 }
 
 struct S {
@@ -256,7 +330,9 @@
 }
 )";
 
-    auto got = Run<ClampFragDepth>(src);
+    DataMap config;
+    config.Add<ClampFragDepth::Config>(0, 4);
+    auto got = Run<ClampFragDepth>(src, config);
     EXPECT_EQ(expect, str(got));
 }
 
@@ -285,15 +361,17 @@
     auto* expect = R"(
 enable chromium_experimental_push_constant;
 
-struct FragDepthClampArgs {
-  min : f32,
-  max : f32,
+struct PushConstants {
+  /* @offset(0) */
+  min_depth : f32,
+  /* @offset(4) */
+  max_depth : f32,
 }
 
-var<push_constant> frag_depth_clamp_args : FragDepthClampArgs;
+var<push_constant> push_constants : PushConstants;
 
 fn clamp_frag_depth(v : f32) -> f32 {
-  return clamp(v, frag_depth_clamp_args.min, frag_depth_clamp_args.max);
+  return clamp(v, push_constants.min_depth, push_constants.max_depth);
 }
 
 struct S {
@@ -330,7 +408,9 @@
 }
 )";
 
-    auto got = Run<ClampFragDepth>(src);
+    DataMap config;
+    config.Add<ClampFragDepth::Config>(0, 4);
+    auto got = Run<ClampFragDepth>(src, config);
     EXPECT_EQ(expect, str(got));
 }
 
@@ -352,15 +432,17 @@
     auto* expect = R"(
 enable chromium_experimental_push_constant;
 
-struct FragDepthClampArgs {
-  min : f32,
-  max : f32,
+struct PushConstants {
+  /* @offset(0) */
+  min_depth : f32,
+  /* @offset(4) */
+  max_depth : f32,
 }
 
-var<push_constant> frag_depth_clamp_args : FragDepthClampArgs;
+var<push_constant> push_constants : PushConstants;
 
 fn clamp_frag_depth(v : f32) -> f32 {
-  return clamp(v, frag_depth_clamp_args.min, frag_depth_clamp_args.max);
+  return clamp(v, push_constants.min_depth, push_constants.max_depth);
 }
 
 struct S {
@@ -386,7 +468,9 @@
 }
 )";
 
-    auto got = Run<ClampFragDepth>(src);
+    DataMap config;
+    config.Add<ClampFragDepth::Config>(0, 4);
+    auto got = Run<ClampFragDepth>(src, config);
     EXPECT_EQ(expect, str(got));
 }
 
diff --git a/src/tint/lang/wgsl/reader/program_to_ir/program_to_ir.cc b/src/tint/lang/wgsl/reader/program_to_ir/program_to_ir.cc
index 05505d1..6d4621a 100644
--- a/src/tint/lang/wgsl/reader/program_to_ir/program_to_ir.cc
+++ b/src/tint/lang/wgsl/reader/program_to_ir/program_to_ir.cc
@@ -450,13 +450,24 @@
     }
 
     void EmitAssignment(const ast::AssignmentStatement* stmt) {
-        // If assigning to a phony, just generate the RHS and we're done. Note that, because
-        // this isn't used, a subsequent transform could remove it due to it being dead code.
-        // This could then change the interface for the program (i.e. a global var no longer
-        // used). If that happens we have to either fix this to store to a phony value, or make
-        // sure we pull the interface before doing the dead code elimination.
+        // If assigning to a phony, and the expression evaluation stage is runtime or override, just
+        // generate the RHS and we're done. Note that, because this isn't used, a subsequent
+        // transform could remove it due to it being dead code. This could then change the interface
+        // for the program (i.e. a global var no longer used). If that happens we have to either fix
+        // this to store to a phony value, or make sure we pull the interface before doing the dead
+        // code elimination.
         if (stmt->lhs->Is<ast::PhonyExpression>()) {
-            (void)EmitValueExpression(stmt->rhs);
+            const auto* sem = program_.Sem().GetVal(stmt->rhs);
+            switch (sem->Stage()) {
+                case core::EvaluationStage::kRuntime:
+                case core::EvaluationStage::kOverride:
+                    EmitValueExpression(stmt->rhs);
+                    break;
+                case core::EvaluationStage::kNotEvaluated:
+                case core::EvaluationStage::kConstant:
+                    // Don't emit.
+                    break;
+            }
             return;
         }
 
diff --git a/src/tint/lang/wgsl/reader/program_to_ir/program_to_ir_test.cc b/src/tint/lang/wgsl/reader/program_to_ir/program_to_ir_test.cc
index b47ee3a..9867cb4 100644
--- a/src/tint/lang/wgsl/reader/program_to_ir/program_to_ir_test.cc
+++ b/src/tint/lang/wgsl/reader/program_to_ir/program_to_ir_test.cc
@@ -26,6 +26,7 @@
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include "gmock/gmock.h"
+#include "src/tint/lang/core/builtin_fn.h"
 #include "src/tint/lang/core/constant/scalar.h"
 #include "src/tint/lang/core/fluent_types.h"
 #include "src/tint/lang/core/ir/block.h"
@@ -1164,5 +1165,27 @@
 )");
 }
 
+////////////////////////////////////////////////////////////////////////////////
+// Bugs
+////////////////////////////////////////////////////////////////////////////////
+TEST_F(IR_FromProgramTest, BugChromium324466107) {
+    Func("f", Empty, ty.void_(),
+         Vector{
+             // Abstract type on the RHS - cannot be emitted.
+             Assign(Phony(), Call(core::BuiltinFn::kFrexp, Call(ty.vec2<Infer>(), 2.0_a))),
+         });
+
+    auto m = Build();
+    ASSERT_EQ(m, Success);
+
+    EXPECT_EQ(Disassemble(m.Get()),
+              R"(%f = func():void -> %b1 {
+  %b1 = block {
+    ret
+  }
+}
+)");
+}
+
 }  // namespace
 }  // namespace tint::wgsl::reader
diff --git a/src/tint/lang/wgsl/sem/function_expression.cc b/src/tint/lang/wgsl/sem/function_expression.cc
index d20ab00..d033140 100644
--- a/src/tint/lang/wgsl/sem/function_expression.cc
+++ b/src/tint/lang/wgsl/sem/function_expression.cc
@@ -26,16 +26,21 @@
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include "src/tint/lang/wgsl/sem/function_expression.h"
+#include "src/tint/lang/wgsl/ast/identifier_expression.h"
 
 TINT_INSTANTIATE_TYPEINFO(tint::sem::FunctionExpression);
 
 namespace tint::sem {
 
-FunctionExpression::FunctionExpression(const ast::Expression* declaration,
+FunctionExpression::FunctionExpression(const ast::IdentifierExpression* declaration,
                                        const Statement* statement,
                                        const sem::Function* function)
     : Base(declaration, statement), function_(function) {}
 
 FunctionExpression::~FunctionExpression() = default;
 
+const ast::IdentifierExpression* FunctionExpression::Declaration() const {
+    return static_cast<const ast::IdentifierExpression*>(Base::Declaration());
+}
+
 }  // namespace tint::sem
diff --git a/src/tint/lang/wgsl/sem/function_expression.h b/src/tint/lang/wgsl/sem/function_expression.h
index 5b0625f..8e2c4de 100644
--- a/src/tint/lang/wgsl/sem/function_expression.h
+++ b/src/tint/lang/wgsl/sem/function_expression.h
@@ -31,6 +31,9 @@
 #include "src/tint/lang/wgsl/sem/expression.h"
 
 // Forward declarations
+namespace tint::ast {
+class IdentifierExpression;
+}  // namespace tint::ast
 namespace tint::sem {
 class Function;
 }  // namespace tint::sem
@@ -45,13 +48,16 @@
     /// @param declaration the AST node
     /// @param statement the statement that owns this expression
     /// @param function the function that the expression resolved to
-    FunctionExpression(const ast::Expression* declaration,
+    FunctionExpression(const ast::IdentifierExpression* declaration,
                        const Statement* statement,
                        const sem::Function* function);
 
     /// Destructor
     ~FunctionExpression() override;
 
+    /// @returns the AST node
+    const ast::IdentifierExpression* Declaration() const;
+
     /// @return the function that the expression resolved to
     const sem::Function* Function() const { return function_; }
 
diff --git a/src/tint/lang/wgsl/sem/member_accessor_expression.cc b/src/tint/lang/wgsl/sem/member_accessor_expression.cc
index 68bcd7a..8a10ac1 100644
--- a/src/tint/lang/wgsl/sem/member_accessor_expression.cc
+++ b/src/tint/lang/wgsl/sem/member_accessor_expression.cc
@@ -48,6 +48,10 @@
 
 MemberAccessorExpression::~MemberAccessorExpression() = default;
 
+const ast::MemberAccessorExpression* MemberAccessorExpression::Declaration() const {
+    return static_cast<const ast::MemberAccessorExpression*>(declaration_);
+}
+
 StructMemberAccess::StructMemberAccess(const ast::MemberAccessorExpression* declaration,
                                        const core::type::Type* type,
                                        const Statement* statement,
diff --git a/src/tint/lang/wgsl/sem/member_accessor_expression.h b/src/tint/lang/wgsl/sem/member_accessor_expression.h
index 67dcf08..bb6f388 100644
--- a/src/tint/lang/wgsl/sem/member_accessor_expression.h
+++ b/src/tint/lang/wgsl/sem/member_accessor_expression.h
@@ -48,6 +48,9 @@
     /// Destructor
     ~MemberAccessorExpression() override;
 
+    /// @returns the AST node
+    const ast::MemberAccessorExpression* Declaration() const;
+
   protected:
     /// Constructor
     /// @param declaration the AST node
diff --git a/src/tint/lang/wgsl/sem/variable.cc b/src/tint/lang/wgsl/sem/variable.cc
index 9d5e1b2..e056c77 100644
--- a/src/tint/lang/wgsl/sem/variable.cc
+++ b/src/tint/lang/wgsl/sem/variable.cc
@@ -94,4 +94,8 @@
 
 VariableUser::~VariableUser() = default;
 
+const ast::IdentifierExpression* VariableUser::Declaration() const {
+    return static_cast<const ast::IdentifierExpression*>(Base::Declaration());
+}
+
 }  // namespace tint::sem
diff --git a/src/tint/lang/wgsl/sem/variable.h b/src/tint/lang/wgsl/sem/variable.h
index 9809fcb..15ca981 100644
--- a/src/tint/lang/wgsl/sem/variable.h
+++ b/src/tint/lang/wgsl/sem/variable.h
@@ -292,6 +292,9 @@
                  sem::Variable* variable);
     ~VariableUser() override;
 
+    /// @returns the AST node
+    const ast::IdentifierExpression* Declaration() const;
+
     /// @returns the variable that this expression refers to
     const sem::Variable* Variable() const { return variable_; }