tint/utils: Add TINT_LIKELY / TINT_UNLIKELY macros

Emits branch prediction hints.
Give unlikely hints about where we call TINT_ICE.

Change-Id: Ied5bc3d7c8b3a838e96e5a0a64156048f90411c6
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/116875
Commit-Queue: Ben Clayton <bclayton@google.com>
Auto-Submit: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Dan Sinclair <dsinclair@chromium.org>
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 3bf1a410..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/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/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";
             }
         }