[ir] Add benchmark for IR validation

We don't run IR validation in release mode, which means that the E2E
benchmarks won't automatically catch IR validation performance
regressions on the waterfall. Having a dedicated benchmark also makes
it easier to investigate the performance of the IR validator in
isolation.

Change-Id: If452bef85ff50c75700d52d860972922a7babe24
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/204194
Reviewed-by: dan sinclair <dsinclair@chromium.org>
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
Commit-Queue: James Price <jrprice@google.com>
diff --git a/src/tint/cmd/bench/BUILD.bazel b/src/tint/cmd/bench/BUILD.bazel
index a44ed53..dd02bf7 100644
--- a/src/tint/cmd/bench/BUILD.bazel
+++ b/src/tint/cmd/bench/BUILD.bazel
@@ -132,6 +132,7 @@
   }) + select({
     ":tint_build_wgsl_reader": [
       "//src/tint/cmd/bench:bench",
+      "//src/tint/lang/core/ir:bench",
       "//src/tint/lang/wgsl/reader:bench",
     ],
     "//conditions:default": [],
diff --git a/src/tint/cmd/bench/BUILD.cmake b/src/tint/cmd/bench/BUILD.cmake
index 429ede3..98cbab9 100644
--- a/src/tint/cmd/bench/BUILD.cmake
+++ b/src/tint/cmd/bench/BUILD.cmake
@@ -101,6 +101,7 @@
 if(TINT_BUILD_WGSL_READER)
   tint_target_add_dependencies(tint_cmd_bench_bench_cmd bench_cmd
     tint_cmd_bench_bench
+    tint_lang_core_ir_bench
     tint_lang_wgsl_reader_bench
   )
 endif(TINT_BUILD_WGSL_READER)
diff --git a/src/tint/cmd/bench/BUILD.gn b/src/tint/cmd/bench/BUILD.gn
index a6c3564..40f4a8f 100644
--- a/src/tint/cmd/bench/BUILD.gn
+++ b/src/tint/cmd/bench/BUILD.gn
@@ -135,6 +135,7 @@
       if (tint_build_wgsl_reader) {
         deps += [
           "${tint_src_dir}/cmd/bench:bench",
+          "${tint_src_dir}/lang/core/ir:bench",
           "${tint_src_dir}/lang/wgsl/reader:bench",
         ]
       }
diff --git a/src/tint/lang/core/ir/BUILD.bazel b/src/tint/lang/core/ir/BUILD.bazel
index 81ee19e..7012c69 100644
--- a/src/tint/lang/core/ir/BUILD.bazel
+++ b/src/tint/lang/core/ir/BUILD.bazel
@@ -244,4 +244,51 @@
   copts = COPTS,
   visibility = ["//visibility:public"],
 )
+cc_library(
+  name = "bench",
+  alwayslink = True,
+  srcs = [
+    "validator_bench.cc",
+  ],
+  deps = [
+    "//src/tint/api/common",
+    "//src/tint/lang/core",
+    "//src/tint/lang/core/constant",
+    "//src/tint/lang/core/ir",
+    "//src/tint/lang/core/type",
+    "//src/tint/lang/wgsl",
+    "//src/tint/lang/wgsl/ast",
+    "//src/tint/lang/wgsl/common",
+    "//src/tint/lang/wgsl/features",
+    "//src/tint/lang/wgsl/program",
+    "//src/tint/lang/wgsl/sem",
+    "//src/tint/utils/containers",
+    "//src/tint/utils/diagnostic",
+    "//src/tint/utils/ice",
+    "//src/tint/utils/id",
+    "//src/tint/utils/macros",
+    "//src/tint/utils/math",
+    "//src/tint/utils/memory",
+    "//src/tint/utils/reflection",
+    "//src/tint/utils/result",
+    "//src/tint/utils/rtti",
+    "//src/tint/utils/symbol",
+    "//src/tint/utils/text",
+    "//src/tint/utils/traits",
+    "@benchmark",
+  ] + select({
+    ":tint_build_wgsl_reader": [
+      "//src/tint/cmd/bench:bench",
+      "//src/tint/lang/wgsl/reader",
+    ],
+    "//conditions:default": [],
+  }),
+  copts = COPTS,
+  visibility = ["//visibility:public"],
+)
+
+alias(
+  name = "tint_build_wgsl_reader",
+  actual = "//src/tint:tint_build_wgsl_reader_true",
+)
 
diff --git a/src/tint/lang/core/ir/BUILD.cfg b/src/tint/lang/core/ir/BUILD.cfg
new file mode 100644
index 0000000..4166f62
--- /dev/null
+++ b/src/tint/lang/core/ir/BUILD.cfg
@@ -0,0 +1,5 @@
+{
+    "bench": {
+        "Condition": "tint_build_wgsl_reader",
+    }
+}
diff --git a/src/tint/lang/core/ir/BUILD.cmake b/src/tint/lang/core/ir/BUILD.cmake
index e918b7c..4919831 100644
--- a/src/tint/lang/core/ir/BUILD.cmake
+++ b/src/tint/lang/core/ir/BUILD.cmake
@@ -246,3 +246,53 @@
 tint_target_add_external_dependencies(tint_lang_core_ir_test test
   "gtest"
 )
+
+if(TINT_BUILD_WGSL_READER)
+################################################################################
+# Target:    tint_lang_core_ir_bench
+# Kind:      bench
+# Condition: TINT_BUILD_WGSL_READER
+################################################################################
+tint_add_target(tint_lang_core_ir_bench bench
+  lang/core/ir/validator_bench.cc
+)
+
+tint_target_add_dependencies(tint_lang_core_ir_bench bench
+  tint_api_common
+  tint_lang_core
+  tint_lang_core_constant
+  tint_lang_core_ir
+  tint_lang_core_type
+  tint_lang_wgsl
+  tint_lang_wgsl_ast
+  tint_lang_wgsl_common
+  tint_lang_wgsl_features
+  tint_lang_wgsl_program
+  tint_lang_wgsl_sem
+  tint_utils_containers
+  tint_utils_diagnostic
+  tint_utils_ice
+  tint_utils_id
+  tint_utils_macros
+  tint_utils_math
+  tint_utils_memory
+  tint_utils_reflection
+  tint_utils_result
+  tint_utils_rtti
+  tint_utils_symbol
+  tint_utils_text
+  tint_utils_traits
+)
+
+tint_target_add_external_dependencies(tint_lang_core_ir_bench bench
+  "google-benchmark"
+)
+
+if(TINT_BUILD_WGSL_READER)
+  tint_target_add_dependencies(tint_lang_core_ir_bench bench
+    tint_cmd_bench_bench
+    tint_lang_wgsl_reader
+  )
+endif(TINT_BUILD_WGSL_READER)
+
+endif(TINT_BUILD_WGSL_READER)
\ No newline at end of file
diff --git a/src/tint/lang/core/ir/BUILD.gn b/src/tint/lang/core/ir/BUILD.gn
index 0d06577..85973b6 100644
--- a/src/tint/lang/core/ir/BUILD.gn
+++ b/src/tint/lang/core/ir/BUILD.gn
@@ -243,3 +243,44 @@
     ]
   }
 }
+if (tint_build_benchmarks) {
+  if (tint_build_wgsl_reader) {
+    tint_benchmarks_source_set("bench") {
+      sources = [ "validator_bench.cc" ]
+      deps = [
+        "${tint_src_dir}:google_benchmark",
+        "${tint_src_dir}/api/common",
+        "${tint_src_dir}/lang/core",
+        "${tint_src_dir}/lang/core/constant",
+        "${tint_src_dir}/lang/core/ir",
+        "${tint_src_dir}/lang/core/type",
+        "${tint_src_dir}/lang/wgsl",
+        "${tint_src_dir}/lang/wgsl/ast",
+        "${tint_src_dir}/lang/wgsl/common",
+        "${tint_src_dir}/lang/wgsl/features",
+        "${tint_src_dir}/lang/wgsl/program",
+        "${tint_src_dir}/lang/wgsl/sem",
+        "${tint_src_dir}/utils/containers",
+        "${tint_src_dir}/utils/diagnostic",
+        "${tint_src_dir}/utils/ice",
+        "${tint_src_dir}/utils/id",
+        "${tint_src_dir}/utils/macros",
+        "${tint_src_dir}/utils/math",
+        "${tint_src_dir}/utils/memory",
+        "${tint_src_dir}/utils/reflection",
+        "${tint_src_dir}/utils/result",
+        "${tint_src_dir}/utils/rtti",
+        "${tint_src_dir}/utils/symbol",
+        "${tint_src_dir}/utils/text",
+        "${tint_src_dir}/utils/traits",
+      ]
+
+      if (tint_build_wgsl_reader) {
+        deps += [
+          "${tint_src_dir}/cmd/bench:bench",
+          "${tint_src_dir}/lang/wgsl/reader",
+        ]
+      }
+    }
+  }
+}
diff --git a/src/tint/lang/core/ir/validator_bench.cc b/src/tint/lang/core/ir/validator_bench.cc
new file mode 100644
index 0000000..42449cb
--- /dev/null
+++ b/src/tint/lang/core/ir/validator_bench.cc
@@ -0,0 +1,72 @@
+// Copyright 2024 The Dawn & Tint Authors
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+//    list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation
+//    and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+//    contributors may be used to endorse or promote products derived from
+//    this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <string>
+
+#include "src/tint/cmd/bench/bench.h"
+#include "src/tint/lang/core/ir/validator.h"
+#include "src/tint/lang/wgsl/reader/reader.h"
+
+#if TINT_BUILD_IS_MSVC
+#if _MSC_VER > 1930 && _MSC_VER < 1939
+#define BUGGY_COMPILER  // MSVC can ICE
+#endif
+#endif
+
+#ifndef BUGGY_COMPILER
+
+namespace tint::core::ir {
+namespace {
+
+void ValidateIR(benchmark::State& state, std::string input_name) {
+    auto res = bench::GetWgslProgram(input_name);
+    if (res != Success) {
+        state.SkipWithError(res.Failure().reason.Str());
+        return;
+    }
+
+    // Convert the AST program to an IR module.
+    auto ir = tint::wgsl::reader::ProgramToLoweredIR(res->program);
+    if (ir != Success) {
+        state.SkipWithError(ir.Failure().reason.Str());
+        return;
+    }
+
+    for (auto _ : state) {
+        auto val_res = Validate(ir.Get(), {});
+        if (val_res != Success) {
+            state.SkipWithError(val_res.Failure().reason.Str());
+        }
+    }
+}
+
+TINT_BENCHMARK_PROGRAMS(ValidateIR);
+
+}  // namespace
+}  // namespace tint::core::ir
+
+#endif