Import Tint changes from Dawn

Changes:
  - 884f95258d50845d4ea28e901599750099040c8c tint/utils: Add TINT_LIKELY / TINT_UNLIKELY macros by Ben Clayton <bclayton@google.com>
  - 91d39a7639030d8664fa66dfda79ea159d3801dc tint/resolver: Reduce Hashmap fixed sizes for UA by Ben Clayton <bclayton@google.com>
  - 42363a5b18b0174f7565f7c2acd6e4410e5ed0ee tint/transform: Skip SimplifyPointers if possible by Ben Clayton <bclayton@google.com>
  - 3d6c263446502ff72cf11bcd281529d7f2a512f7 tint/utils: Add Log2 and NextPowerOfTwo by Ben Clayton <bclayton@google.com>
  - 1f9e50edd4d4536d227dd37063b3cf77749c3eef Add another const to arrays of strings in renamer.cc by Brandon Jones <bajones@chromium.org>
  - 7a41bf1e2f9b24325430d924f2e8c539c1987f79 tint/transform: Skip Unshadow if possible by Ben Clayton <bclayton@google.com>
GitOrigin-RevId: 884f95258d50845d4ea28e901599750099040c8c
Change-Id: Ie89235b05e7892b40c7279b437f747f704f4ca3f
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/116856
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/src/tint/ast/module.cc b/src/tint/ast/module.cc
index 3faab6c..5003996 100644
--- a/src/tint/ast/module.cc
+++ b/src/tint/ast/module.cc
@@ -134,7 +134,7 @@
     enables_.Clear();
 
     for (auto* decl : global_declarations_) {
-        if (!decl) {
+        if (TINT_UNLIKELY(!decl)) {
             TINT_ICE(AST, ctx->dst->Diagnostics()) << "src global declaration was nullptr";
             continue;
         }
diff --git a/src/tint/ast/traverse_expressions.h b/src/tint/ast/traverse_expressions.h
index bcf0bfd..3b3b9ba 100644
--- a/src/tint/ast/traverse_expressions.h
+++ b/src/tint/ast/traverse_expressions.h
@@ -25,6 +25,7 @@
 #include "src/tint/ast/member_accessor_expression.h"
 #include "src/tint/ast/phony_expression.h"
 #include "src/tint/ast/unary_op_expression.h"
+#include "src/tint/utils/compiler_macros.h"
 #include "src/tint/utils/reverse.h"
 #include "src/tint/utils/vector.h"
 
@@ -147,7 +148,8 @@
                 return true;
             },
             [&](Default) {
-                if (expr->IsAnyOf<LiteralExpression, IdentifierExpression, PhonyExpression>()) {
+                if (TINT_LIKELY((expr->IsAnyOf<LiteralExpression, IdentifierExpression,
+                                               PhonyExpression>()))) {
                     return true;  // Leaf expression
                 }
                 TINT_ICE(AST, diags) << "unhandled expression type: " << expr->TypeInfo().name;
diff --git a/src/tint/clone_context.h b/src/tint/clone_context.h
index 508932a..1ec3422 100644
--- a/src/tint/clone_context.h
+++ b/src/tint/clone_context.h
@@ -26,6 +26,7 @@
 #include "src/tint/program_id.h"
 #include "src/tint/symbol.h"
 #include "src/tint/traits.h"
+#include "src/tint/utils/compiler_macros.h"
 #include "src/tint/utils/hashmap.h"
 #include "src/tint/utils/hashset.h"
 #include "src/tint/utils/vector.h"
@@ -300,8 +301,9 @@
         using TPtr = traits::ParameterType<F, 0>;
         using T = typename std::remove_pointer<TPtr>::type;
         for (auto& transform : transforms_) {
-            if (transform.typeinfo->Is(&TypeInfo::Of<T>()) ||
-                TypeInfo::Of<T>().Is(transform.typeinfo)) {
+            bool already_registered = transform.typeinfo->Is(&TypeInfo::Of<T>()) ||
+                                      TypeInfo::Of<T>().Is(transform.typeinfo);
+            if (TINT_UNLIKELY(already_registered)) {
                 TINT_ICE(Clone, Diagnostics())
                     << "ReplaceAll() called with a handler for type " << TypeInfo::Of<T>().name
                     << " that is already handled by a handler for type "
@@ -326,7 +328,7 @@
     /// register a SymbolTransform more than once will result in an ICE.
     /// @returns this CloneContext so calls can be chained
     CloneContext& ReplaceAll(const SymbolTransform& replacer) {
-        if (symbol_transform_) {
+        if (TINT_UNLIKELY(symbol_transform_)) {
             TINT_ICE(Clone, Diagnostics()) << "ReplaceAll(const SymbolTransform&) called "
                                               "multiple times on the same CloneContext";
             return *this;
@@ -383,7 +385,7 @@
     template <typename T, size_t N, typename OBJECT>
     CloneContext& Remove(const utils::Vector<T, N>& vector, OBJECT* object) {
         TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(Clone, src, object);
-        if (std::find(vector.begin(), vector.end(), object) == vector.end()) {
+        if (TINT_UNLIKELY((std::find(vector.begin(), vector.end(), object) == vector.end()))) {
             TINT_ICE(Clone, Diagnostics())
                 << "CloneContext::Remove() vector does not contain object";
             return *this;
@@ -450,7 +452,7 @@
                                const OBJECT* object) {
         TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(Clone, src, before);
         TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(Clone, dst, object);
-        if (std::find(vector.begin(), vector.end(), before) == vector.end()) {
+        if (TINT_UNLIKELY((std::find(vector.begin(), vector.end(), before) == vector.end()))) {
             TINT_ICE(Clone, Diagnostics())
                 << "CloneContext::InsertBefore() vector does not contain before";
             return *this;
@@ -492,7 +494,7 @@
                               const OBJECT* object) {
         TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(Clone, src, after);
         TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(Clone, dst, object);
-        if (std::find(vector.begin(), vector.end(), after) == vector.end()) {
+        if (TINT_UNLIKELY((std::find(vector.begin(), vector.end(), after) == vector.end()))) {
             TINT_ICE(Clone, Diagnostics())
                 << "CloneContext::InsertAfter() vector does not contain after";
             return *this;
@@ -583,7 +585,8 @@
         if (obj == nullptr) {
             return nullptr;
         }
-        if (const TO* cast = obj->template As<TO>()) {
+        const TO* cast = obj->template As<TO>();
+        if (TINT_LIKELY(cast)) {
             return cast;
         }
         CheckedCastFailure(obj, TypeInfo::Of<TO>());
diff --git a/src/tint/inspector/inspector.cc b/src/tint/inspector/inspector.cc
index 75c4863..e69c3b5 100644
--- a/src/tint/inspector/inspector.cc
+++ b/src/tint/inspector/inspector.cc
@@ -887,7 +887,7 @@
 
 template <size_t N, typename F>
 void Inspector::GetOriginatingResources(std::array<const ast::Expression*, N> exprs, F&& callback) {
-    if (!program_->IsValid()) {
+    if (TINT_UNLIKELY(!program_->IsValid())) {
         TINT_ICE(Inspector, diagnostics_)
             << "attempting to get originating resources in invalid program";
         return;
diff --git a/src/tint/program_builder.cc b/src/tint/program_builder.cc
index a219de3..9c2019e 100644
--- a/src/tint/program_builder.cc
+++ b/src/tint/program_builder.cc
@@ -21,6 +21,7 @@
 #include "src/tint/demangler.h"
 #include "src/tint/sem/expression.h"
 #include "src/tint/sem/variable.h"
+#include "src/tint/utils/compiler_macros.h"
 
 using namespace tint::number_suffixes;  // NOLINT
 
@@ -89,7 +90,7 @@
 }
 
 void ProgramBuilder::AssertNotMoved() const {
-    if (moved_) {
+    if (TINT_UNLIKELY(moved_)) {
         TINT_ICE(ProgramBuilder, const_cast<ProgramBuilder*>(this)->diagnostics_)
             << "Attempting to use ProgramBuilder after it has been moved";
     }
diff --git a/src/tint/reader/wgsl/parser_impl.cc b/src/tint/reader/wgsl/parser_impl.cc
index 3e03bab..d35a87e 100644
--- a/src/tint/reader/wgsl/parser_impl.cc
+++ b/src/tint/reader/wgsl/parser_impl.cc
@@ -296,15 +296,15 @@
 }
 
 void ParserImpl::split_token(Token::Type lhs, Token::Type rhs) {
-    if (next_token_idx_ == 0) {
+    if (TINT_UNLIKELY(next_token_idx_ == 0)) {
         TINT_ICE(Reader, builder_.Diagnostics())
             << "attempt to update placeholder at beginning of tokens";
     }
-    if (next_token_idx_ >= tokens_.size()) {
+    if (TINT_UNLIKELY(next_token_idx_ >= tokens_.size())) {
         TINT_ICE(Reader, builder_.Diagnostics())
             << "attempt to update placeholder past end of tokens";
     }
-    if (!tokens_[next_token_idx_].IsPlaceholder()) {
+    if (TINT_UNLIKELY(!tokens_[next_token_idx_].IsPlaceholder())) {
         TINT_ICE(Reader, builder_.Diagnostics()) << "attempt to update non-placeholder token";
     }
     tokens_[next_token_idx_ - 1].SetType(lhs);
@@ -3852,7 +3852,7 @@
     auto result = body();
     --parse_depth_;
 
-    if (sync_tokens_.back() != tok) {
+    if (TINT_UNLIKELY(sync_tokens_.back() != tok)) {
         TINT_ICE(Reader, builder_.Diagnostics()) << "sync_tokens is out of sync";
     }
     sync_tokens_.pop_back();
diff --git a/src/tint/resolver/const_eval.cc b/src/tint/resolver/const_eval.cc
index b52f4aa..7fa435c 100644
--- a/src/tint/resolver/const_eval.cc
+++ b/src/tint/resolver/const_eval.cc
@@ -332,7 +332,7 @@
 
     std::function<const type::Type*(size_t idx)> target_el_ty;
     if (auto* str = target_ty->As<type::Struct>()) {
-        if (str->Members().Length() != composite->elements.Length()) {
+        if (TINT_UNLIKELY(str->Members().Length() != composite->elements.Length())) {
             TINT_ICE(Resolver, builder.Diagnostics())
                 << "const-eval conversion of structure has mismatched element counts";
             return utils::Failure;
@@ -1837,7 +1837,7 @@
         return Dispatch_ia_iu32(create, c0, c1);
     };
 
-    if (!type::Type::DeepestElementOf(args[1]->Type())->Is<type::U32>()) {
+    if (TINT_UNLIKELY(!type::Type::DeepestElementOf(args[1]->Type())->Is<type::U32>())) {
         TINT_ICE(Resolver, builder.Diagnostics())
             << "Element type of rhs of ShiftLeft must be a u32";
         return utils::Failure;
@@ -1901,7 +1901,7 @@
         return Dispatch_ia_iu32(create, c0, c1);
     };
 
-    if (!type::Type::DeepestElementOf(args[1]->Type())->Is<type::U32>()) {
+    if (TINT_UNLIKELY(!type::Type::DeepestElementOf(args[1]->Type())->Is<type::U32>())) {
         TINT_ICE(Resolver, builder.Diagnostics())
             << "Element type of rhs of ShiftLeft must be a u32";
         return utils::Failure;
diff --git a/src/tint/resolver/dependency_graph.cc b/src/tint/resolver/dependency_graph.cc
index 9d2e0da..a380305 100644
--- a/src/tint/resolver/dependency_graph.cc
+++ b/src/tint/resolver/dependency_graph.cc
@@ -71,6 +71,7 @@
 #include "src/tint/symbol_table.h"
 #include "src/tint/type/short_name.h"
 #include "src/tint/utils/block_allocator.h"
+#include "src/tint/utils/compiler_macros.h"
 #include "src/tint/utils/defer.h"
 #include "src/tint/utils/map.h"
 #include "src/tint/utils/scoped_assignment.h"
@@ -319,8 +320,8 @@
             },
             [&](const ast::StaticAssert* assertion) { TraverseExpression(assertion->condition); },
             [&](Default) {
-                if (!stmt->IsAnyOf<ast::BreakStatement, ast::ContinueStatement,
-                                   ast::DiscardStatement>()) {
+                if (TINT_UNLIKELY((!stmt->IsAnyOf<ast::BreakStatement, ast::ContinueStatement,
+                                                  ast::DiscardStatement>()))) {
                     UnhandledNode(diagnostics_, stmt);
                 }
             });
@@ -697,7 +698,7 @@
 
             sorted_.Add(global->node);
 
-            if (!stack.IsEmpty()) {
+            if (TINT_UNLIKELY(!stack.IsEmpty())) {
                 // Each stack.push() must have a corresponding stack.pop_back().
                 TINT_ICE(Resolver, diagnostics_)
                     << "stack not empty after returning from TraverseDependencies()";
@@ -709,7 +710,8 @@
     /// of global `from` depending on `to`.
     /// @note will raise an ICE if the edge is not found.
     DependencyInfo DepInfoFor(const Global* from, const Global* to) const {
-        if (auto info = dependency_edges_.Find(DependencyEdge{from, to})) {
+        auto info = dependency_edges_.Find(DependencyEdge{from, to});
+        if (TINT_LIKELY(info)) {
             return *info;
         }
         TINT_ICE(Resolver, diagnostics_)
diff --git a/src/tint/resolver/intrinsic_table.cc b/src/tint/resolver/intrinsic_table.cc
index c311597..4b507b8 100644
--- a/src/tint/resolver/intrinsic_table.cc
+++ b/src/tint/resolver/intrinsic_table.cc
@@ -1546,7 +1546,7 @@
         Any any;
         return_type =
             Match(match.templates, match.overload, indices, earliest_eval_stage).Type(&any);
-        if (!return_type) {
+        if (TINT_UNLIKELY(!return_type)) {
             TINT_ICE(Resolver, builder.Diagnostics()) << "MatchState.Match() returned null";
             return {};
         }
diff --git a/src/tint/resolver/resolver.cc b/src/tint/resolver/resolver.cc
index 19b870e..48c310f 100644
--- a/src/tint/resolver/resolver.cc
+++ b/src/tint/resolver/resolver.cc
@@ -83,6 +83,7 @@
 #include "src/tint/type/sampler.h"
 #include "src/tint/type/short_name.h"
 #include "src/tint/type/storage_texture.h"
+#include "src/tint/utils/compiler_macros.h"
 #include "src/tint/utils/defer.h"
 #include "src/tint/utils/math.h"
 #include "src/tint/utils/reverse.h"
@@ -130,7 +131,7 @@
 
     bool result = ResolveInternal();
 
-    if (!result && !diagnostics_.contains_errors()) {
+    if (TINT_UNLIKELY(!result && !diagnostics_.contains_errors())) {
         TINT_ICE(Resolver, diagnostics_) << "resolving failed, but no error was raised";
         return false;
     }
@@ -188,7 +189,7 @@
 
     bool result = true;
     for (auto* node : builder_->ASTNodes().Objects()) {
-        if (!marked_[node->node_id.value]) {
+        if (TINT_UNLIKELY(!marked_[node->node_id.value])) {
             TINT_ICE(Resolver, diagnostics_)
                 << "AST node '" << node->TypeInfo().name << "' was not reached by the resolver\n"
                 << "At: " << node->source << "\n"
@@ -884,7 +885,7 @@
 void Resolver::SetShadows() {
     for (auto it : dependencies_.shadows) {
         CastableBase* b = sem_.Get(it.value);
-        if (!b) {
+        if (TINT_UNLIKELY(!b)) {
             TINT_ICE(Resolver, builder_->Diagnostics())
                 << "AST node '" << it.value->TypeInfo().name << "' had no semantic info\n"
                 << "At: " << it.value->source << "\n"
@@ -1073,7 +1074,7 @@
 
     if (decl->body) {
         Mark(decl->body);
-        if (current_compound_statement_) {
+        if (TINT_UNLIKELY(current_compound_statement_)) {
             TINT_ICE(Resolver, diagnostics_)
                 << "Resolver::Function() called with a current compound statement";
             return nullptr;
@@ -1804,7 +1805,7 @@
     const constant::Value* materialized_val = nullptr;
     if (!skip_const_eval_.Contains(decl)) {
         auto expr_val = expr->ConstantValue();
-        if (!expr_val) {
+        if (TINT_UNLIKELY(!expr_val)) {
             TINT_ICE(Resolver, builder_->Diagnostics())
                 << decl->source << "Materialize(" << decl->TypeInfo().name
                 << ") called on expression with no constant value";
@@ -1817,7 +1818,7 @@
             return nullptr;
         }
         materialized_val = val.Get();
-        if (!materialized_val) {
+        if (TINT_UNLIKELY(!materialized_val)) {
             TINT_ICE(Resolver, builder_->Diagnostics())
                 << decl->source << "ConvertValue(" << builder_->FriendlyName(expr_val->Type())
                 << " -> " << builder_->FriendlyName(concrete_ty) << ") returned invalid value";
@@ -2488,7 +2489,7 @@
     // Collect a texture/sampler pair for this builtin.
     const auto& signature = builtin->Signature();
     int texture_index = signature.IndexOf(sem::ParameterUsage::kTexture);
-    if (texture_index == -1) {
+    if (TINT_UNLIKELY(texture_index == -1)) {
         TINT_ICE(Resolver, diagnostics_) << "texture builtin without texture parameter";
     }
     if (auto* user =
@@ -3445,7 +3446,7 @@
         AddError(msg.str(), str->source);
         return nullptr;
     }
-    if (struct_align > std::numeric_limits<uint32_t>::max()) {
+    if (TINT_UNLIKELY(struct_align > std::numeric_limits<uint32_t>::max())) {
         TINT_ICE(Resolver, diagnostics_) << "calculated struct stride exceeds uint32";
         return nullptr;
     }
@@ -3842,12 +3843,12 @@
 }
 
 bool Resolver::Mark(const ast::Node* node) {
-    if (node == nullptr) {
+    if (TINT_UNLIKELY(node == nullptr)) {
         TINT_ICE(Resolver, diagnostics_) << "Resolver::Mark() called with nullptr";
         return false;
     }
     auto marked_bit_ref = marked_[node->node_id.value];
-    if (!marked_bit_ref) {
+    if (TINT_LIKELY(!marked_bit_ref)) {
         marked_bit_ref = true;
         return true;
     }
diff --git a/src/tint/resolver/sem_helper.h b/src/tint/resolver/sem_helper.h
index 8e4a5b6..d6a5130 100644
--- a/src/tint/resolver/sem_helper.h
+++ b/src/tint/resolver/sem_helper.h
@@ -40,7 +40,7 @@
     auto* Get(const AST_OR_TYPE* ast) const {
         using T = sem::Info::GetResultType<SEM, AST_OR_TYPE>;
         auto* sem = builder_->Sem().Get(ast);
-        if (!sem) {
+        if (TINT_UNLIKELY(!sem)) {
             TINT_ICE(Resolver, builder_->Diagnostics())
                 << "AST node '" << ast->TypeInfo().name << "' had no semantic info\n"
                 << "At: " << ast->source << "\n"
diff --git a/src/tint/resolver/uniformity.cc b/src/tint/resolver/uniformity.cc
index 762aba5..a5aa37d 100644
--- a/src/tint/resolver/uniformity.cc
+++ b/src/tint/resolver/uniformity.cc
@@ -240,9 +240,9 @@
         /// The type of this control flow construct.
         std::string type;
         /// The input values for local variables at the start of this construct.
-        utils::Hashmap<const sem::Variable*, Node*, 8> var_in_nodes;
+        utils::Hashmap<const sem::Variable*, Node*, 4> var_in_nodes;
         /// The exit values for local variables at the end of this construct.
-        utils::Hashmap<const sem::Variable*, Node*, 8> var_exit_nodes;
+        utils::Hashmap<const sem::Variable*, Node*, 4> var_exit_nodes;
     };
 
     /// @returns a LoopSwitchInfo for the given statement, allocating the LoopSwitchInfo if this is
@@ -497,7 +497,7 @@
             },
 
             [&](const ast::BlockStatement* b) {
-                utils::Hashmap<const sem::Variable*, Node*, 8> scoped_assignments;
+                utils::Hashmap<const sem::Variable*, Node*, 4> scoped_assignments;
                 {
                     // Push a new scope for variable assignments in the block.
                     current_function_->variables.Push();
@@ -819,15 +819,15 @@
                 v->affects_control_flow = true;
                 v->AddEdge(v_cond);
 
-                utils::Hashmap<const sem::Variable*, Node*, 8> true_vars;
-                utils::Hashmap<const sem::Variable*, Node*, 8> false_vars;
+                utils::Hashmap<const sem::Variable*, Node*, 4> true_vars;
+                utils::Hashmap<const sem::Variable*, Node*, 4> false_vars;
 
                 // Helper to process a statement with a new scope for variable assignments.
                 // Populates `assigned_vars` with new nodes for any variables that are assigned in
                 // this statement.
                 auto process_in_scope =
                     [&](Node* cf_in, const ast::Statement* s,
-                        utils::Hashmap<const sem::Variable*, Node*, 8>& assigned_vars) {
+                        utils::Hashmap<const sem::Variable*, Node*, 4>& assigned_vars) {
                         // Push a new scope for variable assignments.
                         current_function_->variables.Push();
 
diff --git a/src/tint/resolver/validator.cc b/src/tint/resolver/validator.cc
index b199eb0..6c05df2 100644
--- a/src/tint/resolver/validator.cc
+++ b/src/tint/resolver/validator.cc
@@ -1007,8 +1007,8 @@
                 AddError("missing return at end of function", decl->source);
                 return false;
             }
-        } else if (IsValidationEnabled(decl->attributes,
-                                       ast::DisabledValidation::kFunctionHasNoBody)) {
+        } else if (TINT_UNLIKELY(IsValidationEnabled(
+                       decl->attributes, ast::DisabledValidation::kFunctionHasNoBody))) {
             TINT_ICE(Resolver, diagnostics_)
                 << "Function " << symbols_.NameFor(decl->symbol) << " has no body";
         }
@@ -1040,7 +1040,8 @@
 
     // https://www.w3.org/TR/WGSL/#behaviors-rules
     // a function behavior is always one of {}, or {Next}.
-    if (func->Behaviors() != sem::Behaviors{} && func->Behaviors() != sem::Behavior::kNext) {
+    if (TINT_UNLIKELY(func->Behaviors() != sem::Behaviors{} &&
+                      func->Behaviors() != sem::Behavior::kNext)) {
         auto name = symbols_.NameFor(decl->symbol);
         TINT_ICE(Resolver, diagnostics_)
             << "function '" << name << "' behaviors are: " << func->Behaviors();
@@ -1111,7 +1112,7 @@
 
                 bool is_input = param_or_ret == ParamOrRetType::kParameter;
 
-                if (!location.has_value()) {
+                if (TINT_UNLIKELY(!location.has_value())) {
                     TINT_ICE(Resolver, diagnostics_) << "Location has no value";
                     return false;
                 }
@@ -1810,7 +1811,7 @@
         return false;
     }
 
-    if (!c->Is<type::ConstantArrayCount>()) {
+    if (TINT_UNLIKELY(!c->Is<type::ConstantArrayCount>())) {
         TINT_ICE(Resolver, diagnostics_) << "Invalid ArrayCount found";
         return false;
     }
diff --git a/src/tint/scope_stack.h b/src/tint/scope_stack.h
index 75c50b4..4a1af1e 100644
--- a/src/tint/scope_stack.h
+++ b/src/tint/scope_stack.h
@@ -67,7 +67,7 @@
 
     /// Return the top scope of the stack.
     /// @returns the top scope of the stack
-    const utils::Hashmap<K, V, 8>& Top() const { return stack_.Back(); }
+    const utils::Hashmap<K, V, 4>& Top() const { return stack_.Back(); }
 
     /// Clear the scope stack.
     void Clear() {
@@ -76,7 +76,7 @@
     }
 
   private:
-    utils::Vector<utils::Hashmap<K, V, 8>, 8> stack_ = {{}};
+    utils::Vector<utils::Hashmap<K, V, 4>, 8> stack_ = {{}};
 };
 
 }  // namespace tint
diff --git a/src/tint/transform/array_length_from_uniform.cc b/src/tint/transform/array_length_from_uniform.cc
index fc59021..3f470fa 100644
--- a/src/tint/transform/array_length_from_uniform.cc
+++ b/src/tint/transform/array_length_from_uniform.cc
@@ -218,7 +218,7 @@
             //   arrayLength(&struct_var.array_member)
             //   arrayLength(&array_var)
             auto* param = call_expr->args[0]->As<ast::UnaryOpExpression>();
-            if (!param || param->op != ast::UnaryOp::kAddressOf) {
+            if (TINT_UNLIKELY(!param || param->op != ast::UnaryOp::kAddressOf)) {
                 TINT_ICE(Transform, b.Diagnostics())
                     << "expected form of arrayLength argument to be &array_var or "
                        "&struct_var.array_member";
@@ -229,7 +229,7 @@
                 storage_buffer_expr = accessor->structure;
             }
             auto* storage_buffer_sem = sem.Get<sem::VariableUser>(storage_buffer_expr);
-            if (!storage_buffer_sem) {
+            if (TINT_UNLIKELY(!storage_buffer_sem)) {
                 TINT_ICE(Transform, b.Diagnostics())
                     << "expected form of arrayLength argument to be &array_var or "
                        "&struct_var.array_member";
@@ -238,7 +238,7 @@
 
             // Get the index to use for the buffer size array.
             auto* var = tint::As<sem::GlobalVariable>(storage_buffer_sem->Variable());
-            if (!var) {
+            if (TINT_UNLIKELY(!var)) {
                 TINT_ICE(Transform, b.Diagnostics()) << "storage buffer is not a global variable";
                 break;
             }
diff --git a/src/tint/transform/builtin_polyfill.cc b/src/tint/transform/builtin_polyfill.cc
index 58c3925..2bab400 100644
--- a/src/tint/transform/builtin_polyfill.cc
+++ b/src/tint/transform/builtin_polyfill.cc
@@ -480,7 +480,7 @@
         uint32_t width = WidthOf(ty);
 
         // Currently in WGSL parameters of insertBits must be i32, u32, vecN<i32> or vecN<u32>
-        if (!type::Type::DeepestElementOf(ty)->IsAnyOf<type::I32, type::U32>()) {
+        if (TINT_UNLIKELY(((!type::Type::DeepestElementOf(ty)->IsAnyOf<type::I32, type::U32>())))) {
             TINT_ICE(Transform, b.Diagnostics())
                 << "insertBits polyfill only support i32, u32, and vector of i32 or u32, got "
                 << b.FriendlyName(ty);
diff --git a/src/tint/transform/calculate_array_length.cc b/src/tint/transform/calculate_array_length.cc
index 65821c9..0469dc9 100644
--- a/src/tint/transform/calculate_array_length.cc
+++ b/src/tint/transform/calculate_array_length.cc
@@ -152,7 +152,7 @@
                     //   arrayLength(&array_var)
                     auto* arg = call_expr->args[0];
                     auto* address_of = arg->As<ast::UnaryOpExpression>();
-                    if (!address_of || address_of->op != ast::UnaryOp::kAddressOf) {
+                    if (TINT_UNLIKELY(!address_of || address_of->op != ast::UnaryOp::kAddressOf)) {
                         TINT_ICE(Transform, b.Diagnostics())
                             << "arrayLength() expected address-of, got " << arg->TypeInfo().name;
                     }
@@ -161,7 +161,7 @@
                         storage_buffer_expr = accessor->structure;
                     }
                     auto* storage_buffer_sem = sem.Get<sem::VariableUser>(storage_buffer_expr);
-                    if (!storage_buffer_sem) {
+                    if (TINT_UNLIKELY(!storage_buffer_sem)) {
                         TINT_ICE(Transform, b.Diagnostics())
                             << "expected form of arrayLength argument to be &array_var or "
                                "&struct_var.array_member";
@@ -213,7 +213,7 @@
                                 },
                                 [&](const type::Array* arr) { return arr; });
 
-                            if (!array_type) {
+                            if (TINT_UNLIKELY(!array_type)) {
                                 TINT_ICE(Transform, b.Diagnostics())
                                     << "expected form of arrayLength argument to be "
                                        "&array_var or &struct_var.array_member";
diff --git a/src/tint/transform/canonicalize_entry_point_io.cc b/src/tint/transform/canonicalize_entry_point_io.cc
index 48e2948..1afad12 100644
--- a/src/tint/transform/canonicalize_entry_point_io.cc
+++ b/src/tint/transform/canonicalize_entry_point_io.cc
@@ -354,7 +354,7 @@
         // list to pass them through to the inner function.
         utils::Vector<const ast::Expression*, 8> inner_struct_values;
         for (auto* member : str->Members()) {
-            if (member->Type()->Is<sem::Struct>()) {
+            if (TINT_UNLIKELY(member->Type()->Is<sem::Struct>())) {
                 TINT_ICE(Transform, ctx.dst->Diagnostics()) << "nested IO struct";
                 continue;
             }
@@ -383,7 +383,7 @@
         bool do_interpolate = func_ast->PipelineStage() != ast::PipelineStage::kFragment;
         if (auto* str = inner_ret_type->As<sem::Struct>()) {
             for (auto* member : str->Members()) {
-                if (member->Type()->Is<sem::Struct>()) {
+                if (TINT_UNLIKELY(member->Type()->Is<sem::Struct>())) {
                     TINT_ICE(Transform, ctx.dst->Diagnostics()) << "nested IO struct";
                     continue;
                 }
diff --git a/src/tint/transform/clamp_frag_depth.cc b/src/tint/transform/clamp_frag_depth.cc
index 4551925..94deee5 100644
--- a/src/tint/transform/clamp_frag_depth.cc
+++ b/src/tint/transform/clamp_frag_depth.cc
@@ -89,7 +89,7 @@
     // Abort on any use of push constants in the module.
     for (auto* global : src->AST().GlobalVariables()) {
         if (auto* var = global->As<ast::Var>()) {
-            if (var->declared_address_space == ast::AddressSpace::kPushConstant) {
+            if (TINT_UNLIKELY(var->declared_address_space == ast::AddressSpace::kPushConstant)) {
                 TINT_ICE(Transform, b.Diagnostics())
                     << "ClampFragDepth doesn't know how to handle module that already use push "
                        "constants.";
diff --git a/src/tint/transform/decompose_memory_access.cc b/src/tint/transform/decompose_memory_access.cc
index 7c96875..e760bd2 100644
--- a/src/tint/transform/decompose_memory_access.cc
+++ b/src/tint/transform/decompose_memory_access.cc
@@ -503,7 +503,7 @@
                     auto* i = b.Var(b.Symbols().New("i"), b.Expr(0_u));
                     auto* for_init = b.Decl(i);
                     auto arr_cnt = arr_ty->ConstantCount();
-                    if (!arr_cnt) {
+                    if (TINT_UNLIKELY(!arr_cnt)) {
                         // Non-constant counts should not be possible:
                         // * Override-expression counts can only be applied to workgroup arrays, and
                         //   this method only handles storage and uniform.
@@ -607,7 +607,7 @@
                             auto* i = b.Var(b.Symbols().New("i"), b.Expr(0_u));
                             auto* for_init = b.Decl(i);
                             auto arr_cnt = arr_ty->ConstantCount();
-                            if (!arr_cnt) {
+                            if (TINT_UNLIKELY(!arr_cnt)) {
                                 // Non-constant counts should not be possible:
                                 // * Override-expression counts can only be applied to workgroup
                                 //   arrays, and this method only handles storage and uniform.
@@ -700,7 +700,7 @@
             }
 
             auto* atomic = IntrinsicAtomicFor(ctx.dst, op, el_ty);
-            if (atomic == nullptr) {
+            if (TINT_UNLIKELY(!atomic)) {
                 TINT_ICE(Transform, b.Diagnostics())
                     << "IntrinsicAtomicFor() returned nullptr for op " << op << " and type "
                     << el_ty->TypeInfo().name;
diff --git a/src/tint/transform/direct_variable_access.cc b/src/tint/transform/direct_variable_access.cc
index 1dbb51e..70479be 100644
--- a/src/tint/transform/direct_variable_access.cc
+++ b/src/tint/transform/direct_variable_access.cc
@@ -1097,7 +1097,8 @@
                 continue;
             }
 
-            if (auto* member = std::get_if<Symbol>(&op)) {
+            auto* member = std::get_if<Symbol>(&op);
+            if (TINT_LIKELY(member)) {
                 ss << sym.NameFor(*member);
                 continue;
             }
@@ -1145,7 +1146,8 @@
             return b.IndexAccessor(expr, idx);
         }
 
-        if (auto* member = std::get_if<Symbol>(&access)) {
+        auto* member = std::get_if<Symbol>(&access);
+        if (TINT_LIKELY(member)) {
             /// The access is a member access.
             return b.MemberAccessor(expr, ctx.Clone(*member));
         }
diff --git a/src/tint/transform/localize_struct_array_assignment.cc b/src/tint/transform/localize_struct_array_assignment.cc
index 3bf1a41..b389b61 100644
--- a/src/tint/transform/localize_struct_array_assignment.cc
+++ b/src/tint/transform/localize_struct_array_assignment.cc
@@ -187,24 +187,27 @@
     std::pair<const type::Type*, ast::AddressSpace> GetOriginatingTypeAndAddressSpace(
         const ast::AssignmentStatement* assign_stmt) {
         auto* root_ident = src->Sem().Get(assign_stmt->lhs)->RootIdentifier();
-        if (!root_ident) {
+        if (TINT_UNLIKELY(!root_ident)) {
             TINT_ICE(Transform, b.Diagnostics())
                 << "Unable to determine originating variable for lhs of assignment "
                    "statement";
             return {};
         }
 
-        auto* type = root_ident->Type();
-        if (auto* ref = type->As<type::Reference>()) {
-            return {ref->StoreType(), ref->AddressSpace()};
-        } else if (auto* ptr = type->As<type::Pointer>()) {
-            return {ptr->StoreType(), ptr->AddressSpace()};
-        }
-
-        TINT_ICE(Transform, b.Diagnostics())
-            << "Expecting to find variable of type pointer or reference on lhs "
-               "of assignment statement";
-        return {};
+        return Switch(
+            root_ident->Type(),  //
+            [&](const type::Reference* ref) {
+                return std::make_pair(ref->StoreType(), ref->AddressSpace());
+            },
+            [&](const type::Pointer* ptr) {
+                return std::make_pair(ptr->StoreType(), ptr->AddressSpace());
+            },
+            [&](Default) {
+                TINT_ICE(Transform, b.Diagnostics())
+                    << "Expecting to find variable of type pointer or reference on lhs "
+                       "of assignment statement";
+                return std::pair<const type::Type*, ast::AddressSpace>{};
+            });
     }
 };
 
diff --git a/src/tint/transform/multiplanar_external_texture.cc b/src/tint/transform/multiplanar_external_texture.cc
index f5770fb..d81d186 100644
--- a/src/tint/transform/multiplanar_external_texture.cc
+++ b/src/tint/transform/multiplanar_external_texture.cc
@@ -402,7 +402,7 @@
                                                                   NewBindingSymbols syms) {
         const ast::Expression* plane_0_binding_param = ctx.Clone(expr->args[0]);
 
-        if (expr->args.Length() != 3) {
+        if (TINT_UNLIKELY(expr->args.Length() != 3)) {
             TINT_ICE(Transform, b.Diagnostics())
                 << "expected textureSampleBaseClampToEdge call with a "
                    "texture_external to have 3 parameters, found "
@@ -447,7 +447,7 @@
     /// @param syms the expanded symbols to be used in the new call
     /// @returns a call expression to textureLoadExternal
     const ast::CallExpression* createTextureLoad(const sem::Call* call, NewBindingSymbols syms) {
-        if (call->Arguments().Length() != 2) {
+        if (TINT_UNLIKELY(call->Arguments().Length() != 2)) {
             TINT_ICE(Transform, b.Diagnostics())
                 << "expected textureLoad call with a texture_external to have 2 arguments, found "
                 << call->Arguments().Length() << " arguments";
diff --git a/src/tint/transform/renamer.cc b/src/tint/transform/renamer.cc
index 8d9872d..a467806 100644
--- a/src/tint/transform/renamer.cc
+++ b/src/tint/transform/renamer.cc
@@ -35,7 +35,7 @@
 namespace {
 
 // This list is used for a binary search and must be kept in sorted order.
-const char* kReservedKeywordsGLSL[] = {
+const char* const kReservedKeywordsGLSL[] = {
     "abs",
     "acos",
     "acosh",
@@ -395,7 +395,7 @@
 };
 
 // This list is used for a binary search and must be kept in sorted order.
-const char* kReservedKeywordsHLSL[] = {
+const char* const kReservedKeywordsHLSL[] = {
     "AddressU",
     "AddressV",
     "AddressW",
@@ -969,7 +969,7 @@
 };
 
 // This list is used for a binary search and must be kept in sorted order.
-const char* kReservedKeywordsMSL[] = {
+const char* const kReservedKeywordsMSL[] = {
     "HUGE_VALF",
     "HUGE_VALH",
     "INFINITY",
diff --git a/src/tint/transform/simplify_pointers.cc b/src/tint/transform/simplify_pointers.cc
index 5d36c1d..9757ae6 100644
--- a/src/tint/transform/simplify_pointers.cc
+++ b/src/tint/transform/simplify_pointers.cc
@@ -130,6 +130,86 @@
         // A map of saved expressions to their saved variable name
         utils::Hashmap<const ast::Expression*, Symbol, 8> saved_vars;
 
+        bool needs_transform = false;
+
+        // Find all the pointer-typed `let` declarations.
+        // Note that these must be function-scoped, as module-scoped `let`s are not
+        // permitted.
+        for (auto* node : ctx.src->ASTNodes().Objects()) {
+            Switch(
+                node,  //
+                [&](const ast::VariableDeclStatement* let) {
+                    if (!let->variable->Is<ast::Let>()) {
+                        return;  // Not a `let` declaration. Ignore.
+                    }
+
+                    auto* var = ctx.src->Sem().Get(let->variable);
+                    if (!var->Type()->Is<type::Pointer>()) {
+                        return;  // Not a pointer type. Ignore.
+                    }
+
+                    // We're dealing with a pointer-typed `let` declaration.
+
+                    // Scan the initializer expression for array index expressions that need
+                    // to be hoist to temporary "saved" variables.
+                    utils::Vector<const ast::VariableDeclStatement*, 8> saved;
+                    CollectSavedArrayIndices(
+                        var->Declaration()->initializer, [&](const ast::Expression* idx_expr) {
+                            // We have a sub-expression that needs to be saved.
+                            // Create a new variable
+                            auto saved_name = ctx.dst->Symbols().New(
+                                ctx.src->Symbols().NameFor(var->Declaration()->symbol) + "_save");
+                            auto* decl =
+                                ctx.dst->Decl(ctx.dst->Let(saved_name, ctx.Clone(idx_expr)));
+                            saved.Push(decl);
+                            // Record the substitution of `idx_expr` to the saved variable
+                            // with the symbol `saved_name`. This will be used by the
+                            // ReplaceAll() handler above.
+                            saved_vars.Add(idx_expr, saved_name);
+                        });
+
+                    // Find the place to insert the saved declarations.
+                    // Special care needs to be made for lets declared as the initializer
+                    // part of for-loops. In this case the block will hold the for-loop
+                    // statement, not the let.
+                    if (!saved.IsEmpty()) {
+                        auto* stmt = ctx.src->Sem().Get(let);
+                        auto* block = stmt->Block();
+                        // Find the statement owned by the block (either the let decl or a
+                        // for-loop)
+                        while (block != stmt->Parent()) {
+                            stmt = stmt->Parent();
+                        }
+                        // Declare the stored variables just before stmt. Order here is
+                        // important as order-of-operations needs to be preserved.
+                        // CollectSavedArrayIndices() visits the LHS of an index accessor
+                        // before the index expression.
+                        for (auto* decl : saved) {
+                            // Note that repeated calls to InsertBefore() with the same `before`
+                            // argument will result in nodes to inserted in the order the
+                            // calls are made (last call is inserted last).
+                            ctx.InsertBefore(block->Declaration()->statements, stmt->Declaration(),
+                                             decl);
+                        }
+                    }
+
+                    // As the original `let` declaration will be fully inlined, there's no
+                    // need for the original declaration to exist. Remove it.
+                    RemoveStatement(ctx, let);
+                },
+                [&](const ast::UnaryOpExpression* op) {
+                    if (op->op == ast::UnaryOp::kAddressOf) {
+                        // Transform can be skipped if no address-of operator is used, as there
+                        // will be no pointers that can be inlined.
+                        needs_transform = true;
+                    }
+                });
+        }
+
+        if (!needs_transform) {
+            return SkipTransform;
+        }
+
         // Register the ast::Expression transform handler.
         // This performs two different transformations:
         // * Identifiers that resolve to the pointer-typed `let` declarations are
@@ -160,70 +240,6 @@
             return expr;
         });
 
-        // Find all the pointer-typed `let` declarations.
-        // Note that these must be function-scoped, as module-scoped `let`s are not
-        // permitted.
-        for (auto* node : ctx.src->ASTNodes().Objects()) {
-            if (auto* let = node->As<ast::VariableDeclStatement>()) {
-                if (!let->variable->Is<ast::Let>()) {
-                    continue;  // Not a `let` declaration. Ignore.
-                }
-
-                auto* var = ctx.src->Sem().Get(let->variable);
-                if (!var->Type()->Is<type::Pointer>()) {
-                    continue;  // Not a pointer type. Ignore.
-                }
-
-                // We're dealing with a pointer-typed `let` declaration.
-
-                // Scan the initializer expression for array index expressions that need
-                // to be hoist to temporary "saved" variables.
-                utils::Vector<const ast::VariableDeclStatement*, 8> saved;
-                CollectSavedArrayIndices(
-                    var->Declaration()->initializer, [&](const ast::Expression* idx_expr) {
-                        // We have a sub-expression that needs to be saved.
-                        // Create a new variable
-                        auto saved_name = ctx.dst->Symbols().New(
-                            ctx.src->Symbols().NameFor(var->Declaration()->symbol) + "_save");
-                        auto* decl = ctx.dst->Decl(ctx.dst->Let(saved_name, ctx.Clone(idx_expr)));
-                        saved.Push(decl);
-                        // Record the substitution of `idx_expr` to the saved variable
-                        // with the symbol `saved_name`. This will be used by the
-                        // ReplaceAll() handler above.
-                        saved_vars.Add(idx_expr, saved_name);
-                    });
-
-                // Find the place to insert the saved declarations.
-                // Special care needs to be made for lets declared as the initializer
-                // part of for-loops. In this case the block will hold the for-loop
-                // statement, not the let.
-                if (!saved.IsEmpty()) {
-                    auto* stmt = ctx.src->Sem().Get(let);
-                    auto* block = stmt->Block();
-                    // Find the statement owned by the block (either the let decl or a
-                    // for-loop)
-                    while (block != stmt->Parent()) {
-                        stmt = stmt->Parent();
-                    }
-                    // Declare the stored variables just before stmt. Order here is
-                    // important as order-of-operations needs to be preserved.
-                    // CollectSavedArrayIndices() visits the LHS of an index accessor
-                    // before the index expression.
-                    for (auto* decl : saved) {
-                        // Note that repeated calls to InsertBefore() with the same `before`
-                        // argument will result in nodes to inserted in the order the
-                        // calls are made (last call is inserted last).
-                        ctx.InsertBefore(block->Declaration()->statements, stmt->Declaration(),
-                                         decl);
-                    }
-                }
-
-                // As the original `let` declaration will be fully inlined, there's no
-                // need for the original declaration to exist. Remove it.
-                RemoveStatement(ctx, let);
-            }
-        }
-
         ctx.Clone();
         return Program(std::move(b));
     }
diff --git a/src/tint/transform/simplify_pointers_test.cc b/src/tint/transform/simplify_pointers_test.cc
index 9848ff3..b05dfc4 100644
--- a/src/tint/transform/simplify_pointers_test.cc
+++ b/src/tint/transform/simplify_pointers_test.cc
@@ -24,11 +24,18 @@
 
 TEST_F(SimplifyPointersTest, EmptyModule) {
     auto* src = "";
-    auto* expect = "";
 
-    auto got = Run<Unshadow, SimplifyPointers>(src);
+    EXPECT_FALSE(ShouldRun<SimplifyPointers>(src));
+}
 
-    EXPECT_EQ(expect, str(got));
+TEST_F(SimplifyPointersTest, NoAddressOf) {
+    auto* src = R"(
+fn f(p : ptr<function, i32>) {
+  var v : i32;
+}
+)";
+
+    EXPECT_FALSE(ShouldRun<SimplifyPointers>(src));
 }
 
 TEST_F(SimplifyPointersTest, FoldPointer) {
diff --git a/src/tint/transform/std140.cc b/src/tint/transform/std140.cc
index 0b8665a..d8f1610 100644
--- a/src/tint/transform/std140.cc
+++ b/src/tint/transform/std140.cc
@@ -25,6 +25,7 @@
 #include "src/tint/sem/module.h"
 #include "src/tint/sem/struct.h"
 #include "src/tint/sem/variable.h"
+#include "src/tint/utils/compiler_macros.h"
 #include "src/tint/utils/hashmap.h"
 #include "src/tint/utils/transform.h"
 
@@ -433,7 +434,7 @@
                         attrs.Push(b.create<ast::StrideAttribute>(arr->Stride()));
                     }
                     auto count = arr->ConstantCount();
-                    if (!count) {
+                    if (TINT_UNLIKELY(!count)) {
                         // Non-constant counts should not be possible:
                         // * Override-expression counts can only be applied to workgroup arrays, and
                         //   this method only handles types transitively used as uniform buffers.
@@ -518,7 +519,7 @@
                         access.indices.Push(UniformVariable{});
                         return Action::kStop;
                     }
-                    if (user->Variable()->Type()->Is<type::Pointer>()) {
+                    if (TINT_LIKELY(user->Variable()->Type()->Is<type::Pointer>())) {
                         // Found a pointer. As the root identifier is a uniform buffer variable,
                         // this must be a pointer-let. Continue traversing from the let
                         // initializer.
@@ -633,7 +634,7 @@
             [&](const sem::Struct* str) { return sym.NameFor(str->Name()); },
             [&](const type::Array* arr) {
                 auto count = arr->ConstantCount();
-                if (!count) {
+                if (TINT_UNLIKELY(!count)) {
                     // Non-constant counts should not be possible:
                     // * Override-expression counts can only be applied to workgroup arrays, and
                     //   this method only handles types transitively used as uniform buffers.
@@ -717,7 +718,8 @@
                 },  //
                 [&](const type::Matrix* mat) {
                     // Reassemble a std140 matrix from the structure of column vector members.
-                    if (auto std140_mat = std140_mats.Get(mat)) {
+                    auto std140_mat = std140_mats.Get(mat);
+                    if (TINT_LIKELY(std140_mat)) {
                         utils::Vector<const ast::Expression*, 8> args;
                         // std140 decomposed matrix. Reassemble.
                         auto* mat_ty = CreateASTTypeFor(ctx, mat);
@@ -739,7 +741,7 @@
                     auto* dst_el = b.IndexAccessor(var, i);
                     auto* src_el = Convert(arr->ElemType(), b.IndexAccessor(param, i));
                     auto count = arr->ConstantCount();
-                    if (!count) {
+                    if (TINT_UNLIKELY(!count)) {
                         // Non-constant counts should not be possible:
                         // * Override-expression counts can only be applied to workgroup arrays, and
                         //   this method only handles types transitively used as uniform buffers.
diff --git a/src/tint/transform/transform.cc b/src/tint/transform/transform.cc
index 55bb715..b0c7596 100644
--- a/src/tint/transform/transform.cc
+++ b/src/tint/transform/transform.cc
@@ -65,7 +65,7 @@
         ctx.Remove(block->Declaration()->statements, stmt);
         return;
     }
-    if (tint::Is<sem::ForLoopStatement>(sem->Parent())) {
+    if (TINT_LIKELY(tint::Is<sem::ForLoopStatement>(sem->Parent()))) {
         ctx.Replace(stmt, static_cast<ast::Expression*>(nullptr));
         return;
     }
@@ -130,11 +130,12 @@
             auto* count = ctx.Clone(override->expr->Declaration());
             return ctx.dst->ty.array(el, count, std::move(attrs));
         }
-        if (auto count = a->ConstantCount()) {
-            return ctx.dst->ty.array(el, u32(count.value()), std::move(attrs));
+        auto count = a->ConstantCount();
+        if (TINT_UNLIKELY(!count)) {
+            TINT_ICE(Transform, ctx.dst->Diagnostics()) << type::Array::kErrExpectedConstantCount;
+            return ctx.dst->ty.array(el, u32(1), std::move(attrs));
         }
-        TINT_ICE(Transform, ctx.dst->Diagnostics()) << type::Array::kErrExpectedConstantCount;
-        return ctx.dst->ty.array(el, u32(1), std::move(attrs));
+        return ctx.dst->ty.array(el, u32(count.value()), std::move(attrs));
     }
     if (auto* s = ty->As<sem::Struct>()) {
         return ctx.dst->create<ast::TypeName>(ctx.Clone(s->Declaration()->name));
diff --git a/src/tint/transform/truncate_interstage_variables.cc b/src/tint/transform/truncate_interstage_variables.cc
index bbb27fd..b2d7ff1 100644
--- a/src/tint/transform/truncate_interstage_variables.cc
+++ b/src/tint/transform/truncate_interstage_variables.cc
@@ -83,7 +83,7 @@
         auto* func_sem = sem.Get(func_ast);
         auto* str = func_sem->ReturnType()->As<sem::Struct>();
 
-        if (!str) {
+        if (TINT_UNLIKELY(!str)) {
             TINT_ICE(Transform, ctx.dst->Diagnostics())
                 << "Entrypoint function return type is non-struct.\n"
                 << "TruncateInterstageVariables transform needs to run after "
diff --git a/src/tint/transform/unshadow.cc b/src/tint/transform/unshadow.cc
index 3ec5f30..7c9f75c 100644
--- a/src/tint/transform/unshadow.cc
+++ b/src/tint/transform/unshadow.cc
@@ -81,19 +81,29 @@
                 });
         };
 
-        ctx.ReplaceAll([&](const ast::Variable* v) -> const ast::Variable* {
-            if (auto* local = sem.Get<sem::LocalVariable>(v)) {
-                if (local->Shadows()) {
-                    return rename(local);
-                }
-            }
-            if (auto* param = sem.Get<sem::Parameter>(v)) {
-                if (param->Shadows()) {
-                    return rename(param);
-                }
-            }
-            return nullptr;
-        });
+        bool made_changes = false;
+
+        for (auto* node : ctx.src->SemNodes().Objects()) {
+            Switch(
+                node,  //
+                [&](const sem::LocalVariable* local) {
+                    if (local->Shadows()) {
+                        ctx.Replace(local->Declaration(), [&, local] { return rename(local); });
+                        made_changes = true;
+                    }
+                },
+                [&](const sem::Parameter* param) {
+                    if (param->Shadows()) {
+                        ctx.Replace(param->Declaration(), [&, param] { return rename(param); });
+                        made_changes = true;
+                    }
+                });
+        }
+
+        if (!made_changes) {
+            return SkipTransform;
+        }
+
         ctx.ReplaceAll(
             [&](const ast::IdentifierExpression* ident) -> const tint::ast::IdentifierExpression* {
                 if (auto* sem_ident = sem.Get(ident)) {
diff --git a/src/tint/transform/unshadow_test.cc b/src/tint/transform/unshadow_test.cc
index f9e976b..d2ee8a5 100644
--- a/src/tint/transform/unshadow_test.cc
+++ b/src/tint/transform/unshadow_test.cc
@@ -23,11 +23,8 @@
 
 TEST_F(UnshadowTest, EmptyModule) {
     auto* src = "";
-    auto* expect = "";
 
-    auto got = Run<Unshadow>(src);
-
-    EXPECT_EQ(expect, str(got));
+    EXPECT_FALSE(ShouldRun<Unshadow>(src));
 }
 
 TEST_F(UnshadowTest, Noop) {
@@ -48,11 +45,7 @@
 }
 )";
 
-    auto* expect = src;
-
-    auto got = Run<Unshadow>(src);
-
-    EXPECT_EQ(expect, str(got));
+    EXPECT_FALSE(ShouldRun<Unshadow>(src));
 }
 
 TEST_F(UnshadowTest, LocalShadowsAlias) {
diff --git a/src/tint/transform/utils/hoist_to_decl_before.cc b/src/tint/transform/utils/hoist_to_decl_before.cc
index fa18934..5852573 100644
--- a/src/tint/transform/utils/hoist_to_decl_before.cc
+++ b/src/tint/transform/utils/hoist_to_decl_before.cc
@@ -336,7 +336,8 @@
             return true;
         }
 
-        if (auto* fl = parent->As<sem::ForLoopStatement>()) {
+        auto* fl = parent->As<sem::ForLoopStatement>();
+        if (TINT_LIKELY(fl)) {
             // Insertion point is a for-loop initializer or continuing statement.
             // These require special care.
             if (fl->Declaration()->initializer == ip) {
@@ -349,7 +350,7 @@
                 return true;
             }
 
-            if (fl->Declaration()->continuing == ip) {
+            if (TINT_LIKELY(fl->Declaration()->continuing == ip)) {
                 // Insertion point is a for-loop continuing statement.
                 // For-loop needs to be decomposed to a loop.
 
diff --git a/src/tint/transform/vectorize_matrix_conversions.cc b/src/tint/transform/vectorize_matrix_conversions.cc
index 714e61e..26f27c3 100644
--- a/src/tint/transform/vectorize_matrix_conversions.cc
+++ b/src/tint/transform/vectorize_matrix_conversions.cc
@@ -93,7 +93,8 @@
         }
 
         // The source and destination type of a matrix conversion must have a same shape.
-        if (!(src_type->rows() == dst_type->rows() && src_type->columns() == dst_type->columns())) {
+        if (TINT_UNLIKELY(!(src_type->rows() == dst_type->rows() &&
+                            src_type->columns() == dst_type->columns()))) {
             TINT_ICE(Transform, b.Diagnostics())
                 << "source and destination matrix has different shape in matrix conversion";
             return nullptr;
diff --git a/src/tint/transform/vectorize_scalar_matrix_initializers.cc b/src/tint/transform/vectorize_scalar_matrix_initializers.cc
index 2a3a37c..9e989bc 100644
--- a/src/tint/transform/vectorize_scalar_matrix_initializers.cc
+++ b/src/tint/transform/vectorize_scalar_matrix_initializers.cc
@@ -128,7 +128,7 @@
             return b.Call(fn, ctx.Clone(args[0]->Declaration()));
         }
 
-        if (args.Length() == mat_type->columns() * mat_type->rows()) {
+        if (TINT_LIKELY(args.Length() == mat_type->columns() * mat_type->rows())) {
             return build_mat([&](uint32_t c, uint32_t r) {
                 return ctx.Clone(args[c * mat_type->rows() + r]->Declaration());
             });
diff --git a/src/tint/transform/vertex_pulling.cc b/src/tint/transform/vertex_pulling.cc
index a5cb94a..42dbbe3 100644
--- a/src/tint/transform/vertex_pulling.cc
+++ b/src/tint/transform/vertex_pulling.cc
@@ -22,6 +22,7 @@
 #include "src/tint/ast/variable_decl_statement.h"
 #include "src/tint/program_builder.h"
 #include "src/tint/sem/variable.h"
+#include "src/tint/utils/compiler_macros.h"
 #include "src/tint/utils/map.h"
 #include "src/tint/utils/math.h"
 
@@ -766,12 +767,17 @@
             auto* sem = src->Sem().Get<sem::Parameter>(param);
             info.type = sem->Type();
 
-            if (!sem->Location().has_value()) {
+            if (TINT_UNLIKELY(!sem->Location().has_value())) {
                 TINT_ICE(Transform, b.Diagnostics()) << "Location missing value";
                 return;
             }
             location_info[sem->Location().value()] = info;
-        } else if (auto* builtin = ast::GetAttribute<ast::BuiltinAttribute>(param->attributes)) {
+        } else {
+            auto* builtin = ast::GetAttribute<ast::BuiltinAttribute>(param->attributes);
+            if (TINT_UNLIKELY(!builtin)) {
+                TINT_ICE(Transform, b.Diagnostics()) << "Invalid entry point parameter";
+                return;
+            }
             // Check for existing vertex_index and instance_index builtins.
             if (builtin->builtin == ast::BuiltinValue::kVertexIndex) {
                 vertex_index_expr = [this, param]() { return b.Expr(ctx.Clone(param->symbol)); };
@@ -779,8 +785,6 @@
                 instance_index_expr = [this, param]() { return b.Expr(ctx.Clone(param->symbol)); };
             }
             new_function_parameters.Push(ctx.Clone(param));
-        } else {
-            TINT_ICE(Transform, b.Diagnostics()) << "Invalid entry point parameter";
         }
     }
 
@@ -817,8 +821,12 @@
                 TINT_ASSERT(Transform, sem->Location().has_value());
                 location_info[sem->Location().value()] = info;
                 has_locations = true;
-            } else if (auto* builtin =
-                           ast::GetAttribute<ast::BuiltinAttribute>(member->attributes)) {
+            } else {
+                auto* builtin = ast::GetAttribute<ast::BuiltinAttribute>(member->attributes);
+                if (TINT_UNLIKELY(!builtin)) {
+                    TINT_ICE(Transform, b.Diagnostics()) << "Invalid entry point parameter";
+                    return;
+                }
                 // Check for existing vertex_index and instance_index builtins.
                 if (builtin->builtin == ast::BuiltinValue::kVertexIndex) {
                     vertex_index_expr = member_expr;
@@ -826,8 +834,6 @@
                     instance_index_expr = member_expr;
                 }
                 members_to_clone.Push(member);
-            } else {
-                TINT_ICE(Transform, b.Diagnostics()) << "Invalid entry point parameter";
             }
         }
 
diff --git a/src/tint/utils/compiler_macros.h b/src/tint/utils/compiler_macros.h
index 1a04042..087a02f 100644
--- a/src/tint/utils/compiler_macros.h
+++ b/src/tint/utils/compiler_macros.h
@@ -41,6 +41,9 @@
     __pragma(warning(pop))                   \
     TINT_REQUIRE_SEMICOLON
 // clang-format on
+
+#define TINT_UNLIKELY(x) x /* currently no-op */
+#define TINT_LIKELY(x) x   /* currently no-op */
 #elif defined(__clang__)
 ////////////////////////////////////////////////////////////////////////////////
 // Clang
@@ -64,6 +67,9 @@
     _Pragma("clang diagnostic pop")          \
     TINT_REQUIRE_SEMICOLON
 // clang-format on
+
+#define TINT_UNLIKELY(x) __builtin_expect(!!(x), false)
+#define TINT_LIKELY(x) __builtin_expect(!!(x), true)
 #elif defined(__GNUC__)
 ////////////////////////////////////////////////////////////////////////////////
 // GCC
@@ -87,12 +93,18 @@
     _Pragma("GCC diagnostic pop")            \
     TINT_REQUIRE_SEMICOLON
 // clang-format on
+
+#define TINT_UNLIKELY(x) __builtin_expect(!!(x), false)
+#define TINT_LIKELY(x) __builtin_expect(!!(x), true)
 #else
 ////////////////////////////////////////////////////////////////////////////////
 // Other
 ////////////////////////////////////////////////////////////////////////////////
 #define TINT_BEGIN_DISABLE_WARNING(name) TINT_REQUIRE_SEMICOLON
 #define TINT_END_DISABLE_WARNING(name) TINT_REQUIRE_SEMICOLON
+#define TINT_UNLIKELY(x) x
+#define TINT_LIKELY(x) x
+
 #endif
 
 #endif  // SRC_TINT_UTILS_COMPILER_MACROS_H_
diff --git a/src/tint/utils/math.h b/src/tint/utils/math.h
index 3d8874a..27dd232 100644
--- a/src/tint/utils/math.h
+++ b/src/tint/utils/math.h
@@ -26,7 +26,7 @@
 /// @return `value` rounded to the next multiple of `alignment`
 /// @note `alignment` must be positive. An alignment of zero will cause a DBZ.
 template <typename T>
-inline T RoundUp(T alignment, T value) {
+inline constexpr T RoundUp(T alignment, T value) {
     return ((value + alignment - 1) / alignment) * alignment;
 }
 
@@ -34,11 +34,58 @@
 /// @returns true if `value` is a power-of-two
 /// @note `value` must be positive if `T` is signed
 template <typename T>
-inline bool IsPowerOfTwo(T value) {
+inline constexpr bool IsPowerOfTwo(T value) {
     return (value & (value - 1)) == 0;
 }
 
 /// @param value the input value
+/// @returns the base-2 logarithm of @p value
+inline constexpr uint32_t Log2(uint64_t value) {
+#if defined(__clang__) || defined(__GNUC__)
+    return 63 - static_cast<uint32_t>(__builtin_clzll(value));
+#elif defined(_MSC_VER) && !defined(__clang__) && __cplusplus >= 202002L  // MSVC and C++20+
+    // note: std::is_constant_evaluated() added in C++20
+    //       required here as _BitScanReverse64 is not constexpr
+    if constexpr (!std::is_constant_evaluated()) {
+        // NOLINTNEXTLINE(runtime/int)
+        if constexpr (sizeof(unsigned long) == 8) {  // 64-bit
+            // NOLINTNEXTLINE(runtime/int)
+            unsigned long first_bit_index = 0;
+            _BitScanReverse64(&first_bit_index, value);
+            return first_bit_index;
+        } else {  // 32-bit
+            // NOLINTNEXTLINE(runtime/int)
+            unsigned long first_bit_index = 0;
+            if (_BitScanReverse(&first_bit_index, value >> 32)) {
+                return first_bit_index + 32;
+            }
+            _BitScanReverse(&first_bit_index, value & 0xffffffff);
+            return first_bit_index;
+        }
+    }
+#endif
+
+    // Non intrinsic (slow) path. Supports constexpr evaluation.
+    for (size_t clz = 0; clz < 64; clz++) {
+        size_t bit = 63 - clz;
+        if (value & (static_cast<size_t>(1u) << bit)) {
+            return bit;
+        }
+    }
+    return 64;
+}
+
+/// @param value the input value
+/// @returns the next power of two number greater or equal to @p value
+inline constexpr uint64_t NextPowerOfTwo(uint64_t value) {
+    if (value <= 1) {
+        return 1;
+    } else {
+        return static_cast<uint64_t>(1) << (Log2(value - 1) + 1);
+    }
+}
+
+/// @param value the input value
 /// @returns the largest power of two that `value` is a multiple of
 template <typename T>
 inline std::enable_if_t<std::is_unsigned<T>::value, T> MaxAlignOf(T value) {
diff --git a/src/tint/utils/math_test.cc b/src/tint/utils/math_test.cc
index 515c718..e8db5f7 100644
--- a/src/tint/utils/math_test.cc
+++ b/src/tint/utils/math_test.cc
@@ -57,6 +57,70 @@
     EXPECT_EQ(IsPowerOfTwo(9), false);
 }
 
+TEST(MathTests, Log2) {
+    EXPECT_EQ(Log2(1), 0u);
+    EXPECT_EQ(Log2(2), 1u);
+    EXPECT_EQ(Log2(3), 1u);
+    EXPECT_EQ(Log2(4), 2u);
+    EXPECT_EQ(Log2(5), 2u);
+    EXPECT_EQ(Log2(6), 2u);
+    EXPECT_EQ(Log2(7), 2u);
+    EXPECT_EQ(Log2(8), 3u);
+    EXPECT_EQ(Log2(9), 3u);
+    EXPECT_EQ(Log2(0x7fffffffu), 30u);
+    EXPECT_EQ(Log2(0x80000000u), 31u);
+    EXPECT_EQ(Log2(0x80000001u), 31u);
+    EXPECT_EQ(Log2(0x7fffffffffffffffu), 62u);
+    EXPECT_EQ(Log2(0x8000000000000000u), 63u);
+
+    static_assert(Log2(1) == 0u);
+    static_assert(Log2(2) == 1u);
+    static_assert(Log2(3) == 1u);
+    static_assert(Log2(4) == 2u);
+    static_assert(Log2(5) == 2u);
+    static_assert(Log2(6) == 2u);
+    static_assert(Log2(7) == 2u);
+    static_assert(Log2(8) == 3u);
+    static_assert(Log2(9) == 3u);
+    static_assert(Log2(0x7fffffffu) == 30u);
+    static_assert(Log2(0x80000000u) == 31u);
+    static_assert(Log2(0x80000001u) == 31u);
+    static_assert(Log2(0x7fffffffffffffffu) == 62u);
+    static_assert(Log2(0x8000000000000000u) == 63u);
+}
+
+TEST(MathTests, NextPowerOfTwo) {
+    EXPECT_EQ(NextPowerOfTwo(0), 1u);
+    EXPECT_EQ(NextPowerOfTwo(1), 1u);
+    EXPECT_EQ(NextPowerOfTwo(2), 2u);
+    EXPECT_EQ(NextPowerOfTwo(3), 4u);
+    EXPECT_EQ(NextPowerOfTwo(4), 4u);
+    EXPECT_EQ(NextPowerOfTwo(5), 8u);
+    EXPECT_EQ(NextPowerOfTwo(6), 8u);
+    EXPECT_EQ(NextPowerOfTwo(7), 8u);
+    EXPECT_EQ(NextPowerOfTwo(8), 8u);
+    EXPECT_EQ(NextPowerOfTwo(9), 16u);
+    EXPECT_EQ(NextPowerOfTwo(0x7fffffffu), 0x80000000u);
+    EXPECT_EQ(NextPowerOfTwo(0x80000000u), 0x80000000u);
+    EXPECT_EQ(NextPowerOfTwo(0x80000001u), 0x100000000u);
+    EXPECT_EQ(NextPowerOfTwo(0x7fffffffffffffffu), 0x8000000000000000u);
+
+    static_assert(NextPowerOfTwo(0) == 1u);
+    static_assert(NextPowerOfTwo(1) == 1u);
+    static_assert(NextPowerOfTwo(2) == 2u);
+    static_assert(NextPowerOfTwo(3) == 4u);
+    static_assert(NextPowerOfTwo(4) == 4u);
+    static_assert(NextPowerOfTwo(5) == 8u);
+    static_assert(NextPowerOfTwo(6) == 8u);
+    static_assert(NextPowerOfTwo(7) == 8u);
+    static_assert(NextPowerOfTwo(8) == 8u);
+    static_assert(NextPowerOfTwo(9) == 16u);
+    static_assert(NextPowerOfTwo(0x7fffffffu) == 0x80000000u);
+    static_assert(NextPowerOfTwo(0x80000000u) == 0x80000000u);
+    static_assert(NextPowerOfTwo(0x80000001u) == 0x100000000u);
+    static_assert(NextPowerOfTwo(0x7fffffffffffffffu) == 0x8000000000000000u);
+}
+
 TEST(MathTests, MaxAlignOf) {
     EXPECT_EQ(MaxAlignOf(0u), 1u);
     EXPECT_EQ(MaxAlignOf(1u), 1u);
diff --git a/src/tint/writer/glsl/generator_impl.cc b/src/tint/writer/glsl/generator_impl.cc
index bc52f15..b5d897d 100644
--- a/src/tint/writer/glsl/generator_impl.cc
+++ b/src/tint/writer/glsl/generator_impl.cc
@@ -292,41 +292,41 @@
             continue;  // These are not emitted.
         }
 
-        if (auto* global = decl->As<ast::Variable>()) {
-            if (!EmitGlobalVariable(global)) {
+        bool ok = Switch(
+            decl,  //
+            [&](const ast::Variable* global) { return EmitGlobalVariable(global); },
+            [&](const ast::Struct* str) {
+                auto* sem = builder_.Sem().Get(str);
+                bool has_rt_arr = false;
+                if (auto* arr = sem->Members().Back()->Type()->As<type::Array>()) {
+                    has_rt_arr = arr->Count()->Is<type::RuntimeArrayCount>();
+                }
+                bool is_block = ast::HasAttribute<transform::AddBlockAttribute::BlockAttribute>(
+                    str->attributes);
+                if (!has_rt_arr && !is_block) {
+                    if (!EmitStructType(current_buffer_, sem)) {
+                        return false;
+                    }
+                }
+                return true;
+            },
+            [&](const ast::Function* func) {
+                if (func->IsEntryPoint()) {
+                    return EmitEntryPointFunction(func);
+                }
+                return EmitFunction(func);
+            },
+            [&](const ast::Enable* enable) {
+                // Record the required extension for generating extension directive later
+                return RecordExtension(enable);
+            },
+            [&](Default) {
+                TINT_ICE(Writer, diagnostics_)
+                    << "unhandled module-scope declaration: " << decl->TypeInfo().name;
                 return false;
-            }
-        } else if (auto* str = decl->As<ast::Struct>()) {
-            auto* sem = builder_.Sem().Get(str);
-            bool has_rt_arr = false;
-            if (auto* arr = sem->Members().Back()->Type()->As<type::Array>()) {
-                has_rt_arr = arr->Count()->Is<type::RuntimeArrayCount>();
-            }
-            bool is_block =
-                ast::HasAttribute<transform::AddBlockAttribute::BlockAttribute>(str->attributes);
-            if (!has_rt_arr && !is_block) {
-                if (!EmitStructType(current_buffer_, sem)) {
-                    return false;
-                }
-            }
-        } else if (auto* func = decl->As<ast::Function>()) {
-            if (func->IsEntryPoint()) {
-                if (!EmitEntryPointFunction(func)) {
-                    return false;
-                }
-            } else {
-                if (!EmitFunction(func)) {
-                    return false;
-                }
-            }
-        } else if (auto* ext = decl->As<ast::Enable>()) {
-            // Record the required extension for generating extension directive later
-            if (!RecordExtension(ext)) {
-                return false;
-            }
-        } else {
-            TINT_ICE(Writer, diagnostics_)
-                << "unhandled module-scope declaration: " << decl->TypeInfo().name;
+            });
+
+        if (TINT_UNLIKELY(!ok)) {
             return false;
         }
     }
@@ -493,7 +493,7 @@
     // Emit operator.
     if (expr->op == ast::BinaryOp::kAnd) {
         out << " & ";
-    } else if (expr->op == ast::BinaryOp::kOr) {
+    } else if (TINT_LIKELY(expr->op == ast::BinaryOp::kOr)) {
         out << " | ";
     } else {
         TINT_ICE(Writer, diagnostics_) << "unexpected binary op: " << FriendlyName(expr->op);
@@ -742,22 +742,17 @@
 
 bool GeneratorImpl::EmitCall(std::ostream& out, const ast::CallExpression* expr) {
     auto* call = builder_.Sem().Get<sem::Call>(expr);
-    auto* target = call->Target();
-
-    if (target->Is<sem::Function>()) {
-        return EmitFunctionCall(out, call);
-    }
-    if (auto* builtin = target->As<sem::Builtin>()) {
-        return EmitBuiltinCall(out, call, builtin);
-    }
-    if (auto* cast = target->As<sem::TypeConversion>()) {
-        return EmitTypeConversion(out, call, cast);
-    }
-    if (auto* ctor = target->As<sem::TypeInitializer>()) {
-        return EmitTypeInitializer(out, call, ctor);
-    }
-    TINT_ICE(Writer, diagnostics_) << "unhandled call target: " << target->TypeInfo().name;
-    return false;
+    return Switch(
+        call->Target(),  //
+        [&](const sem::Function*) { return EmitFunctionCall(out, call); },
+        [&](const sem::Builtin* builtin) { return EmitBuiltinCall(out, call, builtin); },
+        [&](const sem::TypeConversion* conv) { return EmitTypeConversion(out, call, conv); },
+        [&](const sem::TypeInitializer* init) { return EmitTypeInitializer(out, call, init); },
+        [&](Default) {
+            TINT_ICE(Writer, diagnostics_)
+                << "unhandled call target: " << call->Target()->TypeInfo().name;
+            return false;
+        });
 }
 
 bool GeneratorImpl::EmitFunctionCall(std::ostream& out, const sem::Call* call) {
@@ -1379,7 +1374,7 @@
     };
 
     auto* texture = arg(Usage::kTexture);
-    if (!texture) {
+    if (TINT_UNLIKELY(!texture)) {
         TINT_ICE(Writer, diagnostics_) << "missing texture argument";
         return false;
     }
@@ -1579,7 +1574,7 @@
     out << ", ";
 
     auto* param_coords = arg(Usage::kCoords);
-    if (!param_coords) {
+    if (TINT_UNLIKELY(!param_coords)) {
         TINT_ICE(Writer, diagnostics_) << "missing coords argument";
         return false;
     }
@@ -1678,7 +1673,7 @@
             out << "xyz"[i];
         }
     }
-    if (wgsl_ret_width > glsl_ret_width) {
+    if (TINT_UNLIKELY(wgsl_ret_width > glsl_ret_width)) {
         TINT_ICE(Writer, diagnostics_)
             << "WGSL return width (" << wgsl_ret_width << ") is wider than GLSL return width ("
             << glsl_ret_width << ") for " << builtin->Type();
@@ -2028,7 +2023,7 @@
 bool GeneratorImpl::EmitUniformVariable(const ast::Var* var, const sem::Variable* sem) {
     auto* type = sem->Type()->UnwrapRef();
     auto* str = type->As<sem::Struct>();
-    if (!str) {
+    if (TINT_UNLIKELY(!str)) {
         TINT_ICE(Writer, builder_.Diagnostics()) << "storage variable must be of struct type";
         return false;
     }
@@ -2049,7 +2044,7 @@
 bool GeneratorImpl::EmitStorageVariable(const ast::Var* var, const sem::Variable* sem) {
     auto* type = sem->Type()->UnwrapRef();
     auto* str = type->As<sem::Struct>();
-    if (!str) {
+    if (TINT_UNLIKELY(!str)) {
         TINT_ICE(Writer, builder_.Diagnostics()) << "storage variable must be of struct type";
         return false;
     }
@@ -2254,7 +2249,7 @@
         for (auto* var : func->params) {
             auto* sem = builder_.Sem().Get(var);
             auto* type = sem->Type();
-            if (!type->Is<sem::Struct>()) {
+            if (TINT_UNLIKELY(!type->Is<sem::Struct>())) {
                 // ICE likely indicates that the CanonicalizeEntryPointIO transform was
                 // not run, or a builtin parameter was added after it was run.
                 TINT_ICE(Writer, diagnostics_) << "Unsupported non-struct entry point parameter";
@@ -2893,7 +2888,7 @@
         if (mat->rows() != mat->columns()) {
             out << "x" << mat->rows();
         }
-    } else if (type->Is<type::Pointer>()) {
+    } else if (TINT_UNLIKELY(type->Is<type::Pointer>())) {
         TINT_ICE(Writer, diagnostics_)
             << "Attempting to emit pointer type. These should have been removed "
                "with the InlinePointerLets transform";
@@ -2903,7 +2898,7 @@
     } else if (auto* str = type->As<sem::Struct>()) {
         out << StructName(str);
     } else if (auto* tex = type->As<type::Texture>()) {
-        if (tex->Is<type::ExternalTexture>()) {
+        if (TINT_UNLIKELY(tex->Is<type::ExternalTexture>())) {
             TINT_ICE(Writer, diagnostics_) << "Multiplanar external texture transform was not run.";
             return false;
         }
@@ -2925,7 +2920,7 @@
         if (!subtype || subtype->Is<type::F32>()) {
         } else if (subtype->Is<type::I32>()) {
             out << "i";
-        } else if (subtype->Is<type::U32>()) {
+        } else if (TINT_LIKELY(subtype->Is<type::U32>())) {
             out << "u";
         } else {
             TINT_ICE(Writer, diagnostics_) << "Unsupported texture type";
diff --git a/src/tint/writer/hlsl/generator_impl.cc b/src/tint/writer/hlsl/generator_impl.cc
index fe10b2b..d9afe1d 100644
--- a/src/tint/writer/hlsl/generator_impl.cc
+++ b/src/tint/writer/hlsl/generator_impl.cc
@@ -69,6 +69,7 @@
 #include "src/tint/type/multisampled_texture.h"
 #include "src/tint/type/sampled_texture.h"
 #include "src/tint/type/storage_texture.h"
+#include "src/tint/utils/compiler_macros.h"
 #include "src/tint/utils/defer.h"
 #include "src/tint/utils/map.h"
 #include "src/tint/utils/scoped_assignment.h"
@@ -2289,7 +2290,7 @@
     };
 
     auto* texture = arg(Usage::kTexture);
-    if (!texture) {
+    if (TINT_UNLIKELY(!texture)) {
         TINT_ICE(Writer, diagnostics_) << "missing texture argument";
         return false;
     }
@@ -2412,7 +2413,7 @@
                 }
             }
 
-            if (num_dimensions > 4) {
+            if (TINT_UNLIKELY(num_dimensions > 4)) {
                 TINT_ICE(Writer, diagnostics_) << "Texture query builtin temporary vector has "
                                                << num_dimensions << " dimensions";
                 return false;
@@ -2446,7 +2447,7 @@
                     pre << dims;
                 } else {
                     static constexpr char xyzw[] = {'x', 'y', 'z', 'w'};
-                    if (num_dimensions < 0 || num_dimensions > 4) {
+                    if (TINT_UNLIKELY(num_dimensions < 0 || num_dimensions > 4)) {
                         TINT_ICE(Writer, diagnostics_)
                             << "vector dimensions are " << num_dimensions;
                         return false;
@@ -2553,7 +2554,7 @@
     }
 
     auto* param_coords = arg(Usage::kCoords);
-    if (!param_coords) {
+    if (TINT_UNLIKELY(!param_coords)) {
         TINT_ICE(Writer, diagnostics_) << "missing coords argument";
         return false;
     }
@@ -2636,7 +2637,7 @@
                 out << "xyz"[i];
             }
         }
-        if (wgsl_ret_width > hlsl_ret_width) {
+        if (TINT_UNLIKELY(wgsl_ret_width > hlsl_ret_width)) {
             TINT_ICE(Writer, diagnostics_)
                 << "WGSL return width (" << wgsl_ret_width << ") is wider than HLSL return width ("
                 << hlsl_ret_width << ") for " << builtin->Type();
@@ -3241,7 +3242,7 @@
         for (auto* var : func->params) {
             auto* sem = builder_.Sem().Get(var);
             auto* type = sem->Type();
-            if (!type->Is<sem::Struct>()) {
+            if (TINT_UNLIKELY(!type->Is<sem::Struct>())) {
                 // ICE likely indicates that the CanonicalizeEntryPointIO transform was
                 // not run, or a builtin parameter was added after it was run.
                 TINT_ICE(Writer, diagnostics_) << "Unsupported non-struct entry point parameter";
@@ -3944,7 +3945,7 @@
             const type::Type* base_type = ary;
             std::vector<uint32_t> sizes;
             while (auto* arr = base_type->As<type::Array>()) {
-                if (arr->Count()->Is<type::RuntimeArrayCount>()) {
+                if (TINT_UNLIKELY(arr->Count()->Is<type::RuntimeArrayCount>())) {
                     TINT_ICE(Writer, diagnostics_)
                         << "runtime arrays may only exist in storage buffers, which should have "
                            "been transformed into a ByteAddressBuffer";
@@ -4032,7 +4033,7 @@
             return true;
         },
         [&](const type::Texture* tex) {
-            if (tex->Is<type::ExternalTexture>()) {
+            if (TINT_UNLIKELY(tex->Is<type::ExternalTexture>())) {
                 TINT_ICE(Writer, diagnostics_)
                     << "Multiplanar external texture transform was not run.";
                 return false;
@@ -4075,7 +4076,7 @@
 
             if (storage) {
                 auto* component = image_format_to_rwtexture_type(storage->texel_format());
-                if (component == nullptr) {
+                if (TINT_UNLIKELY(!component)) {
                     TINT_ICE(Writer, diagnostics_) << "Unsupported StorageTexture TexelFormat: "
                                                    << static_cast<int>(storage->texel_format());
                     return false;
@@ -4090,7 +4091,7 @@
                     out << "float4";
                 } else if (subtype->Is<type::I32>()) {
                     out << "int4";
-                } else if (subtype->Is<type::U32>()) {
+                } else if (TINT_LIKELY(subtype->Is<type::U32>())) {
                     out << "uint4";
                 } else {
                     TINT_ICE(Writer, diagnostics_) << "Unsupported multisampled texture type";
@@ -4170,7 +4171,7 @@
                 for (auto* attr : decl->attributes) {
                     if (attr->Is<ast::LocationAttribute>()) {
                         auto& pipeline_stage_uses = str->PipelineStageUses();
-                        if (pipeline_stage_uses.size() != 1) {
+                        if (TINT_UNLIKELY(pipeline_stage_uses.size() != 1)) {
                             TINT_ICE(Writer, diagnostics_) << "invalid entry point IO struct uses";
                         }
 
@@ -4183,8 +4184,8 @@
                         } else if (pipeline_stage_uses.count(
                                        type::PipelineStageUsage::kFragmentInput)) {
                             post += " : TEXCOORD" + std::to_string(loc);
-                        } else if (pipeline_stage_uses.count(
-                                       type::PipelineStageUsage::kFragmentOutput)) {
+                        } else if (TINT_LIKELY(pipeline_stage_uses.count(
+                                       type::PipelineStageUsage::kFragmentOutput))) {
                             post += " : SV_Target" + std::to_string(loc);
                         } else {
                             TINT_ICE(Writer, diagnostics_) << "invalid use of location attribute";
@@ -4211,9 +4212,9 @@
                         // stricter and therefore provides the necessary guarantees.
                         // See discussion here: https://github.com/gpuweb/gpuweb/issues/893
                         pre += "precise ";
-                    } else if (!attr->IsAnyOf<ast::StructMemberAlignAttribute,
-                                              ast::StructMemberOffsetAttribute,
-                                              ast::StructMemberSizeAttribute>()) {
+                    } else if (TINT_UNLIKELY((!attr->IsAnyOf<ast::StructMemberAlignAttribute,
+                                                             ast::StructMemberOffsetAttribute,
+                                                             ast::StructMemberSizeAttribute>()))) {
                         TINT_ICE(Writer, diagnostics_)
                             << "unhandled struct member attribute: " << attr->Name();
                         return false;
diff --git a/src/tint/writer/msl/generator_impl.cc b/src/tint/writer/msl/generator_impl.cc
index fae6682..4889d7d 100644
--- a/src/tint/writer/msl/generator_impl.cc
+++ b/src/tint/writer/msl/generator_impl.cc
@@ -993,7 +993,7 @@
     };
 
     auto* texture = arg(Usage::kTexture)->Declaration();
-    if (!texture) {
+    if (TINT_UNLIKELY(!texture)) {
         TINT_ICE(Writer, diagnostics_) << "missing texture arg";
         return false;
     }
@@ -1976,14 +1976,14 @@
     // attribute have a value of zero.
     const uint32_t kInvalidBindingIndex = std::numeric_limits<uint32_t>::max();
     auto get_binding_index = [&](const ast::Parameter* param) -> uint32_t {
-        if (!param->HasBindingPoint()) {
+        if (TINT_UNLIKELY(!param->HasBindingPoint())) {
             TINT_ICE(Writer, diagnostics_)
                 << "missing binding attributes for entry point parameter";
             return kInvalidBindingIndex;
         }
         auto* param_sem = program_->Sem().Get<sem::Parameter>(param);
         auto bp = param_sem->BindingPoint();
-        if (bp.group != 0) {
+        if (TINT_UNLIKELY(bp.group != 0)) {
             TINT_ICE(Writer, diagnostics_) << "encountered non-zero resource group index (use "
                                               "BindingRemapper to fix)";
             return kInvalidBindingIndex;
@@ -2026,7 +2026,7 @@
                 }
                 if (param->type->Is<ast::Sampler>()) {
                     out << " [[sampler(" << binding << ")]]";
-                } else if (param->type->Is<ast::Texture>()) {
+                } else if (TINT_LIKELY(param->type->Is<ast::Texture>())) {
                     out << " [[texture(" << binding << ")]]";
                 } else {
                     TINT_ICE(Writer, diagnostics_) << "invalid handle type entry point parameter";
@@ -2038,7 +2038,8 @@
                     auto& allocations = workgroup_allocations_[func_name];
                     out << " [[threadgroup(" << allocations.size() << ")]]";
                     allocations.push_back(program_->Sem().Get(ptr->type)->Size());
-                } else if (sc == ast::AddressSpace::kStorage || sc == ast::AddressSpace::kUniform) {
+                } else if (TINT_LIKELY(sc == ast::AddressSpace::kStorage ||
+                                       sc == ast::AddressSpace::kUniform)) {
                     uint32_t binding = get_binding_index(param);
                     if (binding == kInvalidBindingIndex) {
                         return false;
@@ -2067,7 +2068,7 @@
                     }
                     out << " [[" << name << "]]";
                 }
-                if (!builtin_found) {
+                if (TINT_UNLIKELY(!builtin_found)) {
                     TINT_ICE(Writer, diagnostics_) << "Unsupported entry point parameter";
                 }
             }
@@ -2530,7 +2531,7 @@
                 out << "atomic_int";
                 return true;
             }
-            if (atomic->Type()->Is<type::U32>()) {
+            if (TINT_LIKELY(atomic->Type()->Is<type::U32>())) {
                 out << "atomic_uint";
                 return true;
             }
@@ -2610,7 +2611,7 @@
             return true;
         },
         [&](const type::Texture* tex) {
-            if (tex->Is<type::ExternalTexture>()) {
+            if (TINT_UNLIKELY(tex->Is<type::ExternalTexture>())) {
                 TINT_ICE(Writer, diagnostics_)
                     << "Multiplanar external texture transform was not run.";
                 return false;
@@ -2793,7 +2794,7 @@
         auto wgsl_offset = mem->Offset();
 
         if (is_host_shareable) {
-            if (wgsl_offset < msl_offset) {
+            if (TINT_UNLIKELY(wgsl_offset < msl_offset)) {
                 // Unimplementable layout
                 TINT_ICE(Writer, diagnostics_) << "Structure member WGSL offset (" << wgsl_offset
                                                << ") is behind MSL offset (" << msl_offset << ")";
@@ -2837,7 +2838,7 @@
                     },
                     [&](const ast::LocationAttribute*) {
                         auto& pipeline_stage_uses = str->PipelineStageUses();
-                        if (pipeline_stage_uses.size() != 1) {
+                        if (TINT_UNLIKELY(pipeline_stage_uses.size() != 1)) {
                             TINT_ICE(Writer, diagnostics_) << "invalid entry point IO struct uses";
                             return false;
                         }
@@ -2851,8 +2852,8 @@
                         } else if (pipeline_stage_uses.count(
                                        type::PipelineStageUsage::kFragmentInput)) {
                             out << " [[user(locn" + std::to_string(loc) + ")]]";
-                        } else if (pipeline_stage_uses.count(
-                                       type::PipelineStageUsage::kFragmentOutput)) {
+                        } else if (TINT_LIKELY(pipeline_stage_uses.count(
+                                       type::PipelineStageUsage::kFragmentOutput))) {
                             out << " [[color(" + std::to_string(loc) + ")]]";
                         } else {
                             TINT_ICE(Writer, diagnostics_) << "invalid use of location decoration";
@@ -2898,7 +2899,7 @@
         if (is_host_shareable) {
             // Calculate new MSL offset
             auto size_align = MslPackedTypeSizeAndAlign(ty);
-            if (msl_offset % size_align.align) {
+            if (TINT_UNLIKELY(msl_offset % size_align.align)) {
                 TINT_ICE(Writer, diagnostics_)
                     << "Misaligned MSL structure member " << ty->FriendlyName(program_->Symbols())
                     << " " << mem_name;
@@ -3167,7 +3168,7 @@
         },
 
         [&](const type::Array* arr) {
-            if (!arr->IsStrideImplicit()) {
+            if (TINT_UNLIKELY(!arr->IsStrideImplicit())) {
                 TINT_ICE(Writer, diagnostics_)
                     << "arrays with explicit strides should not exist past the SPIR-V reader";
                 return SizeAndAlign{};
diff --git a/src/tint/writer/spirv/builder.cc b/src/tint/writer/spirv/builder.cc
index 9d1092e..c252f8c 100644
--- a/src/tint/writer/spirv/builder.cc
+++ b/src/tint/writer/spirv/builder.cc
@@ -45,6 +45,7 @@
 #include "src/tint/type/reference.h"
 #include "src/tint/type/sampled_texture.h"
 #include "src/tint/type/vector.h"
+#include "src/tint/utils/compiler_macros.h"
 #include "src/tint/utils/defer.h"
 #include "src/tint/utils/map.h"
 #include "src/tint/writer/append_vector.h"
@@ -756,7 +757,7 @@
     }
 
     auto* sem = builder_.Sem().Get<sem::GlobalVariable>(v);
-    if (!sem) {
+    if (TINT_UNLIKELY(!sem)) {
         TINT_ICE(Writer, builder_.Diagnostics())
             << "attempted to generate a global from a non-global variable";
         return false;
@@ -927,7 +928,7 @@
     }
 
     // If the source is a vector, we use OpVectorExtractDynamic.
-    if (info->source_type->Is<type::Vector>()) {
+    if (TINT_LIKELY(info->source_type->Is<type::Vector>())) {
         if (!push_function_inst(
                 spv::Op::OpVectorExtractDynamic,
                 {Operand(result_type_id), extract, Operand(info->source_id), Operand(idx_id)})) {
@@ -949,125 +950,128 @@
     auto* expr_sem = builder_.Sem().Get(expr)->UnwrapLoad();
     auto* expr_type = expr_sem->Type();
 
-    if (auto* access = expr_sem->As<sem::StructMemberAccess>()) {
-        uint32_t idx = access->Member()->Index();
+    return Switch(
+        expr_sem,  //
+        [&](const sem::StructMemberAccess* access) {
+            uint32_t idx = access->Member()->Index();
 
-        if (info->source_type->Is<type::Reference>()) {
-            auto idx_id = GenerateConstantIfNeeded(ScalarConstant::U32(idx));
-            if (idx_id == 0) {
-                return 0;
-            }
-            info->access_chain_indices.push_back(idx_id);
-            info->source_type = expr_type;
-        } else {
-            auto result_type_id = GenerateTypeIfNeeded(expr_type);
-            if (result_type_id == 0) {
-                return false;
-            }
-
-            auto extract = result_op();
-            auto extract_id = std::get<uint32_t>(extract);
-            if (!push_function_inst(
-                    spv::Op::OpCompositeExtract,
-                    {Operand(result_type_id), extract, Operand(info->source_id), Operand(idx)})) {
-                return false;
-            }
-
-            info->source_id = extract_id;
-            info->source_type = expr_type;
-        }
-
-        return true;
-    }
-
-    if (auto* swizzle = expr_sem->As<sem::Swizzle>()) {
-        // Single element swizzle is either an access chain or a composite extract
-        auto& indices = swizzle->Indices();
-        if (indices.Length() == 1) {
             if (info->source_type->Is<type::Reference>()) {
-                auto idx_id = GenerateConstantIfNeeded(ScalarConstant::U32(indices[0]));
-                if (idx_id == 0) {
-                    return 0;
+                auto idx_id = GenerateConstantIfNeeded(ScalarConstant::U32(idx));
+                if (TINT_UNLIKELY(idx_id == 0)) {
+                    return false;
                 }
                 info->access_chain_indices.push_back(idx_id);
+                info->source_type = expr_type;
             } else {
                 auto result_type_id = GenerateTypeIfNeeded(expr_type);
-                if (result_type_id == 0) {
-                    return 0;
+                if (TINT_UNLIKELY(result_type_id == 0)) {
+                    return false;
                 }
 
                 auto extract = result_op();
                 auto extract_id = std::get<uint32_t>(extract);
                 if (!push_function_inst(spv::Op::OpCompositeExtract,
                                         {Operand(result_type_id), extract, Operand(info->source_id),
-                                         Operand(indices[0])})) {
+                                         Operand(idx)})) {
                     return false;
                 }
 
                 info->source_id = extract_id;
                 info->source_type = expr_type;
             }
+
             return true;
-        }
+        },
+        [&](const sem::Swizzle* swizzle) {
+            // Single element swizzle is either an access chain or a composite extract
+            auto& indices = swizzle->Indices();
+            if (indices.Length() == 1) {
+                if (info->source_type->Is<type::Reference>()) {
+                    auto idx_id = GenerateConstantIfNeeded(ScalarConstant::U32(indices[0]));
+                    if (TINT_UNLIKELY(idx_id == 0)) {
+                        return false;
+                    }
+                    info->access_chain_indices.push_back(idx_id);
+                } else {
+                    auto result_type_id = GenerateTypeIfNeeded(expr_type);
+                    if (TINT_UNLIKELY(result_type_id == 0)) {
+                        return false;
+                    }
 
-        // Store the type away as it may change if we run the access chain
-        auto* incoming_type = info->source_type;
+                    auto extract = result_op();
+                    auto extract_id = std::get<uint32_t>(extract);
+                    if (!push_function_inst(spv::Op::OpCompositeExtract,
+                                            {Operand(result_type_id), extract,
+                                             Operand(info->source_id), Operand(indices[0])})) {
+                        return false;
+                    }
 
-        // Multi-item extract is a VectorShuffle. We have to emit any existing
-        // access chain data, then load the access chain and shuffle that.
-        if (!info->access_chain_indices.empty()) {
-            auto result_type_id = GenerateTypeIfNeeded(info->source_type);
-            if (result_type_id == 0) {
-                return 0;
-            }
-            auto extract = result_op();
-            auto extract_id = std::get<uint32_t>(extract);
-
-            OperandList ops = {Operand(result_type_id), extract, Operand(info->source_id)};
-            for (auto id : info->access_chain_indices) {
-                ops.push_back(Operand(id));
+                    info->source_id = extract_id;
+                    info->source_type = expr_type;
+                }
+                return true;
             }
 
-            if (!push_function_inst(spv::Op::OpAccessChain, ops)) {
+            // Store the type away as it may change if we run the access chain
+            auto* incoming_type = info->source_type;
+
+            // Multi-item extract is a VectorShuffle. We have to emit any existing
+            // access chain data, then load the access chain and shuffle that.
+            if (!info->access_chain_indices.empty()) {
+                auto result_type_id = GenerateTypeIfNeeded(info->source_type);
+                if (TINT_UNLIKELY(result_type_id == 0)) {
+                    return false;
+                }
+                auto extract = result_op();
+                auto extract_id = std::get<uint32_t>(extract);
+
+                OperandList ops = {Operand(result_type_id), extract, Operand(info->source_id)};
+                for (auto id : info->access_chain_indices) {
+                    ops.push_back(Operand(id));
+                }
+
+                if (!push_function_inst(spv::Op::OpAccessChain, ops)) {
+                    return false;
+                }
+
+                info->source_id = GenerateLoadIfNeeded(expr_type, extract_id);
+                info->source_type = expr_type->UnwrapRef();
+                info->access_chain_indices.clear();
+            }
+
+            auto result_type_id = GenerateTypeIfNeeded(expr_type);
+            if (TINT_UNLIKELY(result_type_id == 0)) {
                 return false;
             }
 
-            info->source_id = GenerateLoadIfNeeded(expr_type, extract_id);
-            info->source_type = expr_type->UnwrapRef();
-            info->access_chain_indices.clear();
-        }
+            auto vec_id = GenerateLoadIfNeeded(incoming_type, info->source_id);
 
-        auto result_type_id = GenerateTypeIfNeeded(expr_type);
-        if (result_type_id == 0) {
+            auto result = result_op();
+            auto result_id = std::get<uint32_t>(result);
+
+            OperandList ops = {Operand(result_type_id), result, Operand(vec_id), Operand(vec_id)};
+
+            for (auto idx : indices) {
+                ops.push_back(Operand(idx));
+            }
+
+            if (!push_function_inst(spv::Op::OpVectorShuffle, ops)) {
+                return false;
+            }
+            info->source_id = result_id;
+            info->source_type = expr_type;
+            return true;
+        },
+        [&](Default) {
+            TINT_ICE(Writer, builder_.Diagnostics())
+                << "unhandled member index type: " << expr_sem->TypeInfo().name;
             return false;
-        }
-
-        auto vec_id = GenerateLoadIfNeeded(incoming_type, info->source_id);
-
-        auto result = result_op();
-        auto result_id = std::get<uint32_t>(result);
-
-        OperandList ops = {Operand(result_type_id), result, Operand(vec_id), Operand(vec_id)};
-
-        for (auto idx : indices) {
-            ops.push_back(Operand(idx));
-        }
-
-        if (!push_function_inst(spv::Op::OpVectorShuffle, ops)) {
-            return false;
-        }
-        info->source_id = result_id;
-        info->source_type = expr_type;
-        return true;
-    }
-
-    TINT_ICE(Writer, builder_.Diagnostics())
-        << "unhandled member index type: " << expr_sem->TypeInfo().name;
-    return false;
+        });
 }
 
 uint32_t Builder::GenerateAccessorExpression(const ast::Expression* expr) {
-    if (!expr->IsAnyOf<ast::IndexAccessorExpression, ast::MemberAccessorExpression>()) {
+    if (TINT_UNLIKELY(
+            (!expr->IsAnyOf<ast::IndexAccessorExpression, ast::MemberAccessorExpression>()))) {
         TINT_ICE(Writer, builder_.Diagnostics()) << "expression is not an accessor";
         return 0;
     }
@@ -1448,7 +1452,7 @@
                                                   bool is_global_init) {
     // This should not happen as we rely on constant folding to obviate
     // casts/conversions for module-scope variables
-    if (is_global_init) {
+    if (TINT_UNLIKELY(is_global_init)) {
         TINT_ICE(Writer, builder_.Diagnostics())
             << "Module-level conversions are not supported. Conversions should "
                "have already been constant-folded by the FoldConstants transform.";
@@ -1565,13 +1569,13 @@
         }
 
         return result_id;
-    } else if (from_type->Is<type::Matrix>() && to_type->Is<type::Matrix>()) {
+    } else if (TINT_LIKELY(from_type->Is<type::Matrix>() && to_type->Is<type::Matrix>())) {
         // SPIRV does not support matrix conversion, the only valid case is matrix identity
         // initializer. Matrix conversion between f32 and f16 should be transformed into vector
         // conversions for each column vectors by VectorizeMatrixConversions.
         auto* from_mat = from_type->As<type::Matrix>();
         auto* to_mat = to_type->As<type::Matrix>();
-        if (from_mat == to_mat) {
+        if (TINT_LIKELY(from_mat == to_mat)) {
             return val_id;
         }
         TINT_ICE(Writer, builder_.Diagnostics())
@@ -2631,7 +2635,7 @@
     // Generates the argument with the given usage, returning the operand ID
     auto gen_arg = [&](Usage usage) {
         auto* argument = arg(usage);
-        if (!argument) {
+        if (TINT_UNLIKELY(!argument)) {
             TINT_ICE(Writer, builder_.Diagnostics())
                 << "missing argument " << static_cast<int>(usage);
         }
@@ -2639,7 +2643,7 @@
     };
 
     auto* texture = arg(Usage::kTexture);
-    if (!texture) {
+    if (TINT_UNLIKELY(!texture)) {
         TINT_ICE(Writer, builder_.Diagnostics()) << "missing texture argument";
     }
 
@@ -3752,7 +3756,7 @@
 }
 
 bool Builder::GenerateTextureType(const type::Texture* texture, const Operand& result) {
-    if (texture->Is<type::ExternalTexture>()) {
+    if (TINT_UNLIKELY(texture->Is<type::ExternalTexture>())) {
         TINT_ICE(Writer, builder_.Diagnostics())
             << "Multiplanar external texture transform was not run.";
         return false;
@@ -4008,7 +4012,7 @@
         case ast::BuiltinValue::kPosition:
             if (storage == ast::AddressSpace::kIn) {
                 return SpvBuiltInFragCoord;
-            } else if (storage == ast::AddressSpace::kOut) {
+            } else if (TINT_LIKELY(storage == ast::AddressSpace::kOut)) {
                 return SpvBuiltInPosition;
             } else {
                 TINT_ICE(Writer, builder_.Diagnostics()) << "invalid address space for builtin";
diff --git a/src/tint/writer/spirv/builder.h b/src/tint/writer/spirv/builder.h
index 6ac749e..0704074 100644
--- a/src/tint/writer/spirv/builder.h
+++ b/src/tint/writer/spirv/builder.h
@@ -194,7 +194,7 @@
     /// Pushes a variable to the current function
     /// @param operands the variable operands
     void push_function_var(const OperandList& operands) {
-        if (functions_.empty()) {
+        if (TINT_UNLIKELY(functions_.empty())) {
             TINT_ICE(Writer, builder_.Diagnostics())
                 << "push_function_var() called without a function";
         }
diff --git a/src/tint/writer/text_generator.cc b/src/tint/writer/text_generator.cc
index 3dabb5c..6d29844 100644
--- a/src/tint/writer/text_generator.cc
+++ b/src/tint/writer/text_generator.cc
@@ -77,7 +77,7 @@
 }
 
 void TextGenerator::TextBuffer::Insert(const std::string& line, size_t before, uint32_t indent) {
-    if (before >= lines.size()) {
+    if (TINT_UNLIKELY(before >= lines.size())) {
         diag::List d;
         TINT_ICE(Writer, d) << "TextBuffer::Insert() called with before >= lines.size()\n"
                             << "  before:" << before << "\n"
@@ -96,7 +96,7 @@
 }
 
 void TextGenerator::TextBuffer::Insert(const TextBuffer& tb, size_t before, uint32_t indent) {
-    if (before >= lines.size()) {
+    if (TINT_UNLIKELY(before >= lines.size())) {
         diag::List d;
         TINT_ICE(Writer, d) << "TextBuffer::Insert() called with before >= lines.size()\n"
                             << "  before:" << before << "\n"
diff --git a/src/tint/writer/wgsl/generator_impl.cc b/src/tint/writer/wgsl/generator_impl.cc
index cfd512b..f02d96c 100644
--- a/src/tint/writer/wgsl/generator_impl.cc
+++ b/src/tint/writer/wgsl/generator_impl.cc
@@ -224,7 +224,7 @@
         if (!EmitExpression(out, expr->target.name)) {
             return false;
         }
-    } else if (expr->target.type) {
+    } else if (TINT_LIKELY(expr->target.type)) {
         if (!EmitType(out, expr->target.type)) {
             return false;
         }