From 98b5cc23b64b8476d7c7059957449fadf1ef0bb3 Mon Sep 17 00:00:00 2001 From: Yulong Wang <7679871+fs-eire@users.noreply.github.com> Date: Mon, 30 Mar 2026 14:44:35 -0700 Subject: [PATCH 1/7] [webgpu] house keeping after ORT switch to c++20 --- cmake/onnxruntime_providers_webgpu.cmake | 1 - onnxruntime/core/providers/webgpu/program.h | 171 ++++-------------- .../core/providers/webgpu/program_test.cc | 109 +++++++++++ .../core/providers/webgpu/shader_variable.cc | 9 +- 4 files changed, 154 insertions(+), 136 deletions(-) create mode 100644 onnxruntime/core/providers/webgpu/program_test.cc diff --git a/cmake/onnxruntime_providers_webgpu.cmake b/cmake/onnxruntime_providers_webgpu.cmake index cd29e4dad0a17..921b20e4bab32 100644 --- a/cmake/onnxruntime_providers_webgpu.cmake +++ b/cmake/onnxruntime_providers_webgpu.cmake @@ -248,7 +248,6 @@ endif() endif() - target_compile_features(onnxruntime_providers_webgpu PRIVATE cxx_std_20) add_dependencies(onnxruntime_providers_webgpu onnx ${onnxruntime_EXTERNAL_DEPENDENCIES}) if (onnxruntime_WGSL_TEMPLATE) diff --git a/onnxruntime/core/providers/webgpu/program.h b/onnxruntime/core/providers/webgpu/program.h index fa6ca1a8f6ba5..26a7824b2990c 100644 --- a/onnxruntime/core/providers/webgpu/program.h +++ b/onnxruntime/core/providers/webgpu/program.h @@ -3,6 +3,7 @@ #pragma once +#include #include #include #include @@ -417,133 +418,55 @@ class ProgramWrapper : public ProgramBase { ProgramWrapper(Args&&... args) : ProgramBase{std::forward(args)...} {} }; -#if defined(ORT_WEBGPU_REGISTER_DERIVED_PROGRAM_CLASS_TYPE_CHECK) -#error "macro ORT_WEBGPU_REGISTER_DERIVED_PROGRAM_CLASS_TYPE_CHECK is already defined" -#endif - -#define ORT_WEBGPU_REGISTER_DERIVED_PROGRAM_CLASS_TYPE_CHECK(identifier, element_type) \ - private: \ - template \ - static auto test_has_##identifier(int) -> decltype(U::identifier, std::true_type{}); /* checks if member exists */ \ - template \ - static auto test_has_##identifier(...) -> std::false_type; \ - \ - template ::value && /* - is a const std::array */ \ - std::is_const_v && /* - has "const" modifier */ \ - !std::is_member_pointer_v>> /* - is static */ \ - static auto test_has_##identifier##_with_correct_type(int) -> std::true_type; \ - template \ - static auto test_has_##identifier##_with_correct_type(...) -> std::false_type; \ - \ - public: \ - static constexpr bool has_##identifier = decltype(test_has_##identifier(0))::value; \ - static constexpr bool has_##identifier##_with_correct_type = decltype(test_has_##identifier##_with_correct_type(0))::value - // the following template class checks whether the type is a const std::array template struct is_const_std_array : std::false_type {}; template struct is_const_std_array> : std::true_type {}; -// the following template class checks whether certain static members exist in the derived class (SFINAE) -template -class DerivedProgramClassTypeCheck { - ORT_WEBGPU_REGISTER_DERIVED_PROGRAM_CLASS_TYPE_CHECK(constants, ProgramConstant); - ORT_WEBGPU_REGISTER_DERIVED_PROGRAM_CLASS_TYPE_CHECK(overridable_constants, ProgramOverridableConstantDefinition); - ORT_WEBGPU_REGISTER_DERIVED_PROGRAM_CLASS_TYPE_CHECK(uniform_variables, ProgramUniformVariableDefinition); -}; - -// compile-time tests for the type check -// -// TODO: move this to test folder -namespace test { +// The following variable templates check whether certain static members exist in the derived class. +// Uses std::void_t for SFINAE-based member detection, working with both static and non-static members. +template +inline constexpr bool has_member_constants = false; template -class TestTypeCheck { - ORT_WEBGPU_REGISTER_DERIVED_PROGRAM_CLASS_TYPE_CHECK(a, int); -}; - -struct TestClass_Empty {}; -static_assert(!TestTypeCheck::has_a); -static_assert(!TestTypeCheck::has_a_with_correct_type); - -struct TestClass_NotArray_0 { - int b; -}; -static_assert(!TestTypeCheck::has_a); -static_assert(!TestTypeCheck::has_a_with_correct_type); - -struct TestClass_NotArray_1 { - int a; -}; -static_assert(TestTypeCheck::has_a); -static_assert(!TestTypeCheck::has_a_with_correct_type); - -struct TestClass_NotArray_2 { - const int a; -}; -static_assert(TestTypeCheck::has_a); -static_assert(!TestTypeCheck::has_a_with_correct_type); - -struct TestClass_NotStdArray_0 { - const int a[2]; -}; -static_assert(TestTypeCheck::has_a); -static_assert(!TestTypeCheck::has_a_with_correct_type); - -struct TestClass_NotStdArray_1 { - static constexpr int a[] = {0}; -}; -static_assert(TestTypeCheck::has_a); -static_assert(!TestTypeCheck::has_a_with_correct_type); +inline constexpr bool has_member_constants> = true; -struct TestClass_NotStdArray_2 { - static int a[]; -}; -static_assert(TestTypeCheck::has_a); -static_assert(!TestTypeCheck::has_a_with_correct_type); - -struct TestClass_NotStdArray_3 { - static const int a[]; -}; -static_assert(TestTypeCheck::has_a); -static_assert(!TestTypeCheck::has_a_with_correct_type); +template +inline constexpr bool has_member_overridable_constants = false; +template +inline constexpr bool has_member_overridable_constants> = true; -struct TestClass_StdArray_0 { - std::array a = {1}; -}; -static_assert(TestTypeCheck::has_a); -static_assert(!TestTypeCheck::has_a_with_correct_type); +template +inline constexpr bool has_member_uniform_variables = false; +template +inline constexpr bool has_member_uniform_variables> = true; -struct TestClass_StdArray_1 { - static constexpr std::array a = {1, 2}; -}; -static_assert(TestTypeCheck::has_a); -static_assert(TestTypeCheck::has_a_with_correct_type); +// C++20 concepts for checking whether the member has the correct type (static const std::array). -struct TestClass_StdArray_2 { - static const std::array a; +template +concept has_constants_correct_type = requires { + T::constants; + requires is_const_std_array::value; + requires std::is_const_v; + requires !std::is_member_pointer_v; }; -static_assert(TestTypeCheck::has_a); -static_assert(TestTypeCheck::has_a_with_correct_type); -struct TestClass_StdArray_3 { - static constexpr const std::array a = {1, 2, 3, 4}; +template +concept has_overridable_constants_correct_type = requires { + T::overridable_constants; + requires is_const_std_array::value; + requires std::is_const_v; + requires !std::is_member_pointer_v; }; -static_assert(TestTypeCheck::has_a); -static_assert(TestTypeCheck::has_a_with_correct_type); -struct TestClass_StdArray_4 { - static std::array a; +template +concept has_uniform_variables_correct_type = requires { + T::uniform_variables; + requires is_const_std_array::value; + requires std::is_const_v; + requires !std::is_member_pointer_v; }; -static_assert(TestTypeCheck::has_a); -static_assert(!TestTypeCheck::has_a_with_correct_type); - -} // namespace test - -#undef ORT_WEBGPU_REGISTER_DERIVED_PROGRAM_CLASS_TYPE_CHECK } // namespace details @@ -555,11 +478,11 @@ class Program : public details::ProgramWrapper { static ProgramMetadata GetMetadata() { ProgramMetadata metadata; - if constexpr (details::DerivedProgramClassTypeCheck::has_constants) { + if constexpr (details::has_member_constants) { constexpr const ProgramConstant* ptr = T::constants.data(); constexpr size_t len = T::constants.size(); - static_assert(details::DerivedProgramClassTypeCheck::has_constants_with_correct_type, + static_assert(details::has_constants_correct_type, "Derived class of \"Program\" has member \"constants\" but its type is incorrect. " "Please use macro WEBGPU_PROGRAM_DEFINE_CONSTANTS() or WEBGPU_PROGRAM_EXTEND_CONSTANTS() to declare constants."); @@ -568,11 +491,11 @@ class Program : public details::ProgramWrapper { metadata.constants = {}; } - if constexpr (details::DerivedProgramClassTypeCheck::has_overridable_constants) { + if constexpr (details::has_member_overridable_constants) { constexpr const ProgramOverridableConstantDefinition* ptr = T::overridable_constants.data(); constexpr size_t len = T::overridable_constants.size(); - static_assert(details::DerivedProgramClassTypeCheck::has_overridable_constants_with_correct_type, + static_assert(details::has_overridable_constants_correct_type, "Derived class of \"Program\" has member \"overridable_constants\" but its type is incorrect. " "Please use macro WEBGPU_PROGRAM_DEFINE_OVERRIDABLE_CONSTANTS() or WEBGPU_PROGRAM_EXTEND_OVERRIDABLE_CONSTANTS() to declare overridable constants."); @@ -581,11 +504,11 @@ class Program : public details::ProgramWrapper { metadata.overridable_constants = {}; } - if constexpr (details::DerivedProgramClassTypeCheck::has_uniform_variables) { + if constexpr (details::has_member_uniform_variables) { constexpr const ProgramUniformVariableDefinition* ptr = T::uniform_variables.data(); constexpr size_t len = T::uniform_variables.size(); - static_assert(details::DerivedProgramClassTypeCheck::has_uniform_variables_with_correct_type, + static_assert(details::has_uniform_variables_correct_type, "Derived class of \"Program\" has member \"uniform_variables\" but its type is incorrect. " "Please use macro WEBGPU_PROGRAM_DEFINE_UNIFORM_VARIABLES() or WEBGPU_PROGRAM_EXTEND_UNIFORM_VARIABLES() to declare uniform variables."); @@ -599,20 +522,6 @@ class Program : public details::ProgramWrapper { }; namespace details { -// helper function to convert a C-style array to std::array -// -// This is basically the same as std::to_array in C++20. -// -template -constexpr auto _to_std_array_impl(T (&arr)[N], std::index_sequence) -> std::array, N> { - return {{arr[Idx]...}}; -} - -template -constexpr auto _to_std_array(T (&arr)[N]) -> std::array, N> { - return _to_std_array_impl(arr, std::make_index_sequence{}); -} - // helper function to concatenate a std::array and a C-style array to a std::array // template @@ -632,7 +541,7 @@ constexpr std::array, L + R> _concat2(const std::array #define WEBGPU_PROGRAM_DEFINE_(identifier, T, ...) \ static constexpr const T identifier##_own[] = {__VA_ARGS__}; \ static constexpr const auto identifier = \ - onnxruntime::webgpu::details::_to_std_array(identifier##_own) + std::to_array(identifier##_own) #define WEBGPU_PROGRAM_EXTEND_(identifier, T, BASE, ...) \ static constexpr const T identifier##_own[] = {__VA_ARGS__}; \ diff --git a/onnxruntime/core/providers/webgpu/program_test.cc b/onnxruntime/core/providers/webgpu/program_test.cc new file mode 100644 index 0000000000000..b541aa34ab925 --- /dev/null +++ b/onnxruntime/core/providers/webgpu/program_test.cc @@ -0,0 +1,109 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// Compile-time tests for the type detection utilities in program.h. +// These static_asserts verify that the has_member_* variable templates +// and has_*_correct_type concepts correctly detect static const std::array members. + +#include "core/providers/webgpu/program.h" + +namespace onnxruntime { +namespace webgpu { +namespace details { +namespace test { + +// Test-specific member detection for a member named 'a' +template +inline constexpr bool test_has_a = false; +template +inline constexpr bool test_has_a> = true; + +// Test-specific type correctness concept for a member named 'a' +template +concept test_has_a_correct_type = requires { + T::a; + requires is_const_std_array::value; + requires std::is_const_v; + requires !std::is_member_pointer_v; +}; + +struct TestClass_Empty {}; +static_assert(!test_has_a); +static_assert(!test_has_a_correct_type); + +struct TestClass_NotArray_0 { + int b; +}; +static_assert(!test_has_a); +static_assert(!test_has_a_correct_type); + +struct TestClass_NotArray_1 { + int a; +}; +static_assert(test_has_a); +static_assert(!test_has_a_correct_type); + +struct TestClass_NotArray_2 { + const int a; +}; +static_assert(test_has_a); +static_assert(!test_has_a_correct_type); + +struct TestClass_NotStdArray_0 { + const int a[2]; +}; +static_assert(test_has_a); +static_assert(!test_has_a_correct_type); + +struct TestClass_NotStdArray_1 { + static constexpr int a[] = {0}; +}; +static_assert(test_has_a); +static_assert(!test_has_a_correct_type); + +struct TestClass_NotStdArray_2 { + static int a[]; +}; +static_assert(test_has_a); +static_assert(!test_has_a_correct_type); + +struct TestClass_NotStdArray_3 { + static const int a[]; +}; +static_assert(test_has_a); +static_assert(!test_has_a_correct_type); + +struct TestClass_StdArray_0 { + std::array a = {1}; +}; +static_assert(test_has_a); +static_assert(!test_has_a_correct_type); + +struct TestClass_StdArray_1 { + static constexpr std::array a = {1, 2}; +}; +static_assert(test_has_a); +static_assert(test_has_a_correct_type); + +struct TestClass_StdArray_2 { + static const std::array a; +}; +static_assert(test_has_a); +static_assert(test_has_a_correct_type); + +struct TestClass_StdArray_3 { + static constexpr const std::array a = {1, 2, 3, 4}; +}; +static_assert(test_has_a); +static_assert(test_has_a_correct_type); + +struct TestClass_StdArray_4 { + static std::array a; +}; +static_assert(test_has_a); +static_assert(!test_has_a_correct_type); + +} // namespace test +} // namespace details +} // namespace webgpu +} // namespace onnxruntime diff --git a/onnxruntime/core/providers/webgpu/shader_variable.cc b/onnxruntime/core/providers/webgpu/shader_variable.cc index 611a75065d509..57a90c972b228 100644 --- a/onnxruntime/core/providers/webgpu/shader_variable.cc +++ b/onnxruntime/core/providers/webgpu/shader_variable.cc @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +#include #include #include #include @@ -39,7 +40,7 @@ constexpr static const std::string_view STORAGE_TYPE_ARRAY[] = { "u32", // Uint4x8 "u32", // Int4x8 }; -constexpr static const auto STORAGE_TYPE = details::_to_std_array(STORAGE_TYPE_ARRAY); +constexpr static const auto STORAGE_TYPE = std::to_array(STORAGE_TYPE_ARRAY); constexpr static const std::string_view VALUE_TYPE_ARRAY[] = { "f32", // Float32 @@ -66,7 +67,7 @@ constexpr static const std::string_view VALUE_TYPE_ARRAY[] = { "u32", // Uint4x8 "u32", // Int4x8 }; -constexpr static const auto VALUE_TYPE = details::_to_std_array(VALUE_TYPE_ARRAY); +constexpr static const auto VALUE_TYPE = std::to_array(VALUE_TYPE_ARRAY); constexpr static const std::string_view ELEMENT_TYPE_ARRAY[] = { "f32", // Float32 @@ -93,7 +94,7 @@ constexpr static const std::string_view ELEMENT_TYPE_ARRAY[] = { "u32", // Uint4x8 "i32", // Int4x8 }; -constexpr static const auto ELEMENT_TYPE = details::_to_std_array(ELEMENT_TYPE_ARRAY); +constexpr static const auto ELEMENT_TYPE = std::to_array(ELEMENT_TYPE_ARRAY); constexpr static const uint32_t BYTES_ARRAY[] = { 4, // Float32 @@ -120,7 +121,7 @@ constexpr static const uint32_t BYTES_ARRAY[] = { 4, // Uint4x8 (packed in u32) 4, // Int4x8 (packed in u32) }; -constexpr static const auto BYTES = details::_to_std_array(BYTES_ARRAY); +constexpr static const auto BYTES = std::to_array(BYTES_ARRAY); inline std::string GetIndicesType(int rank) { return rank < 2 ? "u32" From cc56f12e1665835d65770846c80917fbd031492f Mon Sep 17 00:00:00 2001 From: Yulong Wang <7679871+fs-eire@users.noreply.github.com> Date: Mon, 30 Mar 2026 14:55:08 -0700 Subject: [PATCH 2/7] Modernize WebGPU EP code to C++20 idioms - Replace find()!=end() / count()>0 with contains() (4 sites) - Replace .size()==0 / .size()>0 with .empty() (6 sites) - Replace enable_if_t SFINAE with requires clauses (4 sites) - Replace typedef struct with plain struct (1 site) - Replace erase-remove idiom with std::erase_if (1 site) - Remove redundant std::move in rvalue-qualified return (1 site) - Fix + vs << inconsistency in stream operator chains (2 sites) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- onnxruntime/contrib_ops/webgpu/bert/bias_add.cc | 2 +- .../contrib_ops/webgpu/bert/rotary_embedding.cc | 2 +- onnxruntime/core/providers/webgpu/math/einsum.cc | 4 ++-- onnxruntime/core/providers/webgpu/nn/pool.cc | 2 +- .../core/providers/webgpu/reduction/reduction_ops.cc | 2 +- .../core/providers/webgpu/reduction/reduction_ops.h | 4 ++-- onnxruntime/core/providers/webgpu/shader_helper.cc | 2 +- onnxruntime/core/providers/webgpu/shader_variable.h | 7 ++++--- onnxruntime/core/providers/webgpu/string_utils.h | 10 +++++----- onnxruntime/core/providers/webgpu/tensor/split.cc | 2 +- onnxruntime/core/providers/webgpu/tensor/upsample.cc | 2 +- onnxruntime/core/providers/webgpu/webgpu_context.cc | 4 ++-- onnxruntime/core/providers/webgpu/webgpu_context.h | 2 +- .../core/providers/webgpu/webgpu_execution_provider.cc | 2 +- 14 files changed, 24 insertions(+), 23 deletions(-) diff --git a/onnxruntime/contrib_ops/webgpu/bert/bias_add.cc b/onnxruntime/contrib_ops/webgpu/bert/bias_add.cc index 0f2e6d725007f..e53192bbed618 100644 --- a/onnxruntime/contrib_ops/webgpu/bert/bias_add.cc +++ b/onnxruntime/contrib_ops/webgpu/bert/bias_add.cc @@ -30,7 +30,7 @@ Status BiasAddProgram::GenerateShaderCode(ShaderHelper& shader) const { << " let value = " << input.GetByOffset("global_idx") << " + " << bias.GetByOffset("global_idx % uniforms.channels") << " + " << residual.GetByOffset("global_idx") << ";\n" - << " " + output.SetByOffset("global_idx", "value"); + << " " << output.SetByOffset("global_idx", "value"); return Status::OK(); } diff --git a/onnxruntime/contrib_ops/webgpu/bert/rotary_embedding.cc b/onnxruntime/contrib_ops/webgpu/bert/rotary_embedding.cc index 5536a26b3e00b..9f81e490971cd 100644 --- a/onnxruntime/contrib_ops/webgpu/bert/rotary_embedding.cc +++ b/onnxruntime/contrib_ops/webgpu/bert/rotary_embedding.cc @@ -40,7 +40,7 @@ Status RotaryEmbeddingProgram::GenerateShaderCode(ShaderHelper& shader) const { << " let j = i + select(half_rotary_emb_dim, 1, " << interleaved_str << ");\n" << " let re = " << input.GetByOffset("i") << " * " << cos_cache.GetByIndices("vec2(position_id, bsnh[3])") << " - " << input.GetByOffset("j") << " * " << sin_cache.GetByIndices("vec2(position_id, bsnh[3])") << ";\n" << " " << output.SetByOffset("i", "re") << "\n" - << " let im = " << input.GetByOffset("i") << " * " << sin_cache.GetByIndices("vec2(position_id, bsnh[3])") << " + " << input.GetByOffset("j") + " * " << cos_cache.GetByIndices("vec2(position_id, bsnh[3])") << ";\n" + << " let im = " << input.GetByOffset("i") << " * " << sin_cache.GetByIndices("vec2(position_id, bsnh[3])") << " + " << input.GetByOffset("j") << " * " << cos_cache.GetByIndices("vec2(position_id, bsnh[3])") << ";\n" << " " << output.SetByOffset("j", "im") << "\n" << " } else { \n" " let k = dot(bsnh, uniforms.input_output_stride) + half_rotary_emb_dim;\n" diff --git a/onnxruntime/core/providers/webgpu/math/einsum.cc b/onnxruntime/core/providers/webgpu/math/einsum.cc index e17c0281c738f..e5ef659c03ebf 100644 --- a/onnxruntime/core/providers/webgpu/math/einsum.cc +++ b/onnxruntime/core/providers/webgpu/math/einsum.cc @@ -24,7 +24,7 @@ static const std::regex lhs_pattern("(([a-zA-Z]|\\.\\.\\.)*,)*([a-zA-Z]|\\.\\.\\ // Helper function to remove all whitespaces in a given string. std::string RemoveAllWhitespace(const std::string& str) { std::string result = str; - result.erase(std::remove_if(result.begin(), result.end(), ::isspace), result.end()); + std::erase_if(result, [](unsigned char c) { return std::isspace(c); }); return result; } @@ -318,7 +318,7 @@ Status EinsumProgram::GenerateShaderCode(ShaderHelper& shader) const { symbol)); // Check if we've already processed this symbol to avoid duplicate loop generation - if (uniform_symbol_set.find(symbol) == uniform_symbol_set.end()) { + if (!uniform_symbol_set.contains(symbol)) { // Add symbol to tracked set to prevent duplicate processing uniform_symbol_set.insert(symbol); diff --git a/onnxruntime/core/providers/webgpu/nn/pool.cc b/onnxruntime/core/providers/webgpu/nn/pool.cc index 6698a831b0c1a..a4bc8638c4265 100644 --- a/onnxruntime/core/providers/webgpu/nn/pool.cc +++ b/onnxruntime/core/providers/webgpu/nn/pool.cc @@ -249,7 +249,7 @@ Status Pool::ComputeInternal(ComputeContext& context) const { Tensor* Y = context.Output(0, output_shape); std::vector kernel_strides(kernel_shape.size()); - ORT_ENFORCE(kernel_shape.size() > 0, "kernel_shape must have at least one element."); + ORT_ENFORCE(!kernel_shape.empty(), "kernel_shape must have at least one element."); // Calculate the kernel element strides for each dimension in reverse order. For example: // kernel_shape = [3, 2], kernel_strides = [2, 1] // kernel_shape = [2, 3, 2], kernel_strides = [6, 2, 1] diff --git a/onnxruntime/core/providers/webgpu/reduction/reduction_ops.cc b/onnxruntime/core/providers/webgpu/reduction/reduction_ops.cc index 4b23f6fc67669..db51675e81513 100644 --- a/onnxruntime/core/providers/webgpu/reduction/reduction_ops.cc +++ b/onnxruntime/core/providers/webgpu/reduction/reduction_ops.cc @@ -187,7 +187,7 @@ std::unordered_map reduce_op_naive_code_map }; ReduceOpType StringToReduceOp(std::string name) { - ORT_ENFORCE(reduce_op_types.find(name) != reduce_op_types.end(), "Unsupported reduction op type: ", name); + ORT_ENFORCE(reduce_op_types.contains(name), "Unsupported reduction op type: ", name); return reduce_op_types[name]; } diff --git a/onnxruntime/core/providers/webgpu/reduction/reduction_ops.h b/onnxruntime/core/providers/webgpu/reduction/reduction_ops.h index 53abff9ca30c9..8f3eb7b216bfa 100644 --- a/onnxruntime/core/providers/webgpu/reduction/reduction_ops.h +++ b/onnxruntime/core/providers/webgpu/reduction/reduction_ops.h @@ -18,11 +18,11 @@ namespace webgpu { // The loop header is the code that is executed before the loop starts. The loop body is the code that is executed for each element in the loop. // The loop footer is the code that is executed after the loop ends. The loop body should contain the code that accumulates the result of the reduction and // the loop footer should contain the code that assigins output_value the result of the reduction. -typedef struct ReduceOpSpecificCode { +struct ReduceOpSpecificCode { std::string loop_header_; std::string loop_body_; std::string loop_footer_; -} ReduceOpSpecificCode; +}; enum class ReduceOpType { Max, diff --git a/onnxruntime/core/providers/webgpu/shader_helper.cc b/onnxruntime/core/providers/webgpu/shader_helper.cc index 07e9e9e793c56..657fc3a6f45ad 100644 --- a/onnxruntime/core/providers/webgpu/shader_helper.cc +++ b/onnxruntime/core/providers/webgpu/shader_helper.cc @@ -499,7 +499,7 @@ Status ShaderHelper::GenerateSourceCode(std::string& code, std::vector& sha // store shape uniform ranks in shape_uniform_ranks bool use_any_shape_uniform = false; - ORT_ENFORCE(shape_uniform_ranks.size() == 0); + ORT_ENFORCE(shape_uniform_ranks.empty()); shape_uniform_ranks.reserve(input_vars_.size() + output_vars_.size() + indices_vars_.size()); for (const auto& input : input_vars_) { diff --git a/onnxruntime/core/providers/webgpu/shader_variable.h b/onnxruntime/core/providers/webgpu/shader_variable.h index c62eff0209df8..f5bda85ca1b5e 100644 --- a/onnxruntime/core/providers/webgpu/shader_variable.h +++ b/onnxruntime/core/providers/webgpu/shader_variable.h @@ -14,8 +14,8 @@ namespace onnxruntime { namespace webgpu { template || std::is_same_v>> + typename TRank> + requires (std::is_same_v || std::is_same_v) std::string GetElementAt(std::string_view var, const TIdx& idx, TRank rank, bool is_f16 = false) { if (var.starts_with("uniforms.")) { if (is_f16) { @@ -235,7 +235,8 @@ inline ShaderUsage& operator&=(ShaderUsage& a, ShaderUsage b) { #endif namespace detail { -template >> +template + requires std::is_integral_v std::string pass_as_string(T&& v) { return std::to_string(std::forward(v)); } diff --git a/onnxruntime/core/providers/webgpu/string_utils.h b/onnxruntime/core/providers/webgpu/string_utils.h index 1ed65fbc8d509..5faa02339956f 100644 --- a/onnxruntime/core/providers/webgpu/string_utils.h +++ b/onnxruntime/core/providers/webgpu/string_utils.h @@ -48,7 +48,7 @@ class FastOStringStream { } std::string str() && { - return std::move(str_); + return str_; } // String types @@ -75,8 +75,8 @@ class FastOStringStream { // Integer types template - std::enable_if_t && !std::is_same_v, FastOStringStream&> - operator<<(T value) { + requires (std::is_integral_v && !std::is_same_v) + FastOStringStream& operator<<(T value) { std::array buffer; auto [ptr, ec] = std::to_chars(buffer.data(), buffer.data() + buffer.size(), value); str_.append(buffer.data(), ptr - buffer.data()); @@ -85,8 +85,8 @@ class FastOStringStream { // Floating point types template - std::enable_if_t, FastOStringStream&> - operator<<(T value) { + requires std::is_floating_point_v + FastOStringStream& operator<<(T value) { std::array buffer; auto [ptr, ec] = std::to_chars(buffer.data(), buffer.data() + buffer.size(), value); str_.append(buffer.data(), ptr - buffer.data()); diff --git a/onnxruntime/core/providers/webgpu/tensor/split.cc b/onnxruntime/core/providers/webgpu/tensor/split.cc index 25d829c4e8bad..43de5cc4d32d5 100644 --- a/onnxruntime/core/providers/webgpu/tensor/split.cc +++ b/onnxruntime/core/providers/webgpu/tensor/split.cc @@ -82,7 +82,7 @@ Status Split::ComputeInternal(ComputeContext& context) const { split_sizes.assign(split_sizes_.begin(), split_sizes_.end()); // Compute split_sizes from the 'split' input tensor. - if (split_sizes_.size() == 0 && context.InputCount() > 1) { + if (split_sizes_.empty() && context.InputCount() > 1) { const Tensor* split_tensor = context.Input(1); // Check if split_tensor is valid. if (split_tensor != nullptr) { diff --git a/onnxruntime/core/providers/webgpu/tensor/upsample.cc b/onnxruntime/core/providers/webgpu/tensor/upsample.cc index 8f51ed45004bf..9b88731832fc4 100644 --- a/onnxruntime/core/providers/webgpu/tensor/upsample.cc +++ b/onnxruntime/core/providers/webgpu/tensor/upsample.cc @@ -19,7 +19,7 @@ Status Upsample::BaseCompute(ComputeContext& context, auto dims = X->Shape().GetDims(); ORT_ENFORCE(output_dims.size() == dims.size(), "Rank of input and output tensor should be same."); - if (dims.size() == 0) { + if (dims.empty()) { return Status(ONNXRUNTIME, INVALID_ARGUMENT, is_resize_ ? "Resize: input tensor cannot be scalar." : "Upsample: input tensor cannot be scalar."); diff --git a/onnxruntime/core/providers/webgpu/webgpu_context.cc b/onnxruntime/core/providers/webgpu/webgpu_context.cc index ec20bf2fdbdfb..ada9a2e8ab692 100644 --- a/onnxruntime/core/providers/webgpu/webgpu_context.cc +++ b/onnxruntime/core/providers/webgpu/webgpu_context.cc @@ -86,7 +86,7 @@ void WebGpuContext::Initialize(const WebGpuContextConfig& config) { #endif std::vector required_features = GetAvailableRequiredFeatures(adapter); - if (required_features.size() > 0) { + if (!required_features.empty()) { device_desc.requiredFeatures = required_features.data(); device_desc.requiredFeatureCount = required_features.size(); } @@ -188,7 +188,7 @@ Status WebGpuContext::Run(ComputeContextBase& context, const ProgramBase& progra const auto& inputs = program.Inputs(); const auto& outputs = program.Outputs(); - if (outputs.size() == 0) { + if (outputs.empty()) { return Status::OK(); } diff --git a/onnxruntime/core/providers/webgpu/webgpu_context.h b/onnxruntime/core/providers/webgpu/webgpu_context.h index 10a5f278df88a..021c7f383a6d7 100644 --- a/onnxruntime/core/providers/webgpu/webgpu_context.h +++ b/onnxruntime/core/providers/webgpu/webgpu_context.h @@ -170,7 +170,7 @@ class WebGpuContext final { const wgpu::AdapterInfo& AdapterInfo() const { return adapter_info_; } const wgpu::Limits& DeviceLimits() const { return device_limits_; } - bool DeviceHasFeature(wgpu::FeatureName feature) const { return device_features_.find(feature) != device_features_.end(); } + bool DeviceHasFeature(wgpu::FeatureName feature) const { return device_features_.contains(feature); } #if !defined(__wasm__) const wgpu::AdapterPropertiesSubgroupMatrixConfigs& SubgroupMatrixConfigs() const { return subgroup_matrix_configs_; } #endif diff --git a/onnxruntime/core/providers/webgpu/webgpu_execution_provider.cc b/onnxruntime/core/providers/webgpu/webgpu_execution_provider.cc index 28e207a9aa598..0cee96d41ce68 100644 --- a/onnxruntime/core/providers/webgpu/webgpu_execution_provider.cc +++ b/onnxruntime/core/providers/webgpu/webgpu_execution_provider.cc @@ -1031,7 +1031,7 @@ std::vector> WebGpuExecutionProvider::GetCapa auto cpu_nodes = GetCpuPreferredNodes(graph, kernel_lookup, tenative_candidates, *GetLogger()); std::vector> result; for (auto& node_index : candidates) { - if (cpu_nodes.count(node_index) > 0) { + if (cpu_nodes.contains(node_index)) { continue; } From 3817f8b9a44efd043d4e34b6d940a05ebc46f230 Mon Sep 17 00:00:00 2001 From: Yulong Wang <7679871+fs-eire@users.noreply.github.com> Date: Mon, 30 Mar 2026 15:05:16 -0700 Subject: [PATCH 3/7] code format --- onnxruntime/core/providers/webgpu/program.h | 24 +++++++++---------- .../core/providers/webgpu/program_test.cc | 8 +++---- .../core/providers/webgpu/shader_variable.h | 2 +- .../core/providers/webgpu/string_utils.h | 2 +- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/onnxruntime/core/providers/webgpu/program.h b/onnxruntime/core/providers/webgpu/program.h index 26a7824b2990c..8dc85f1a7134e 100644 --- a/onnxruntime/core/providers/webgpu/program.h +++ b/onnxruntime/core/providers/webgpu/program.h @@ -446,26 +446,26 @@ inline constexpr bool has_member_uniform_variables concept has_constants_correct_type = requires { - T::constants; - requires is_const_std_array::value; - requires std::is_const_v; - requires !std::is_member_pointer_v; + T::constants; + requires is_const_std_array::value; + requires std::is_const_v; + requires !std::is_member_pointer_v; }; template concept has_overridable_constants_correct_type = requires { - T::overridable_constants; - requires is_const_std_array::value; - requires std::is_const_v; - requires !std::is_member_pointer_v; + T::overridable_constants; + requires is_const_std_array::value; + requires std::is_const_v; + requires !std::is_member_pointer_v; }; template concept has_uniform_variables_correct_type = requires { - T::uniform_variables; - requires is_const_std_array::value; - requires std::is_const_v; - requires !std::is_member_pointer_v; + T::uniform_variables; + requires is_const_std_array::value; + requires std::is_const_v; + requires !std::is_member_pointer_v; }; } // namespace details diff --git a/onnxruntime/core/providers/webgpu/program_test.cc b/onnxruntime/core/providers/webgpu/program_test.cc index b541aa34ab925..6ed590a148ae8 100644 --- a/onnxruntime/core/providers/webgpu/program_test.cc +++ b/onnxruntime/core/providers/webgpu/program_test.cc @@ -21,10 +21,10 @@ inline constexpr bool test_has_a> = true; // Test-specific type correctness concept for a member named 'a' template concept test_has_a_correct_type = requires { - T::a; - requires is_const_std_array::value; - requires std::is_const_v; - requires !std::is_member_pointer_v; + T::a; + requires is_const_std_array::value; + requires std::is_const_v; + requires !std::is_member_pointer_v; }; struct TestClass_Empty {}; diff --git a/onnxruntime/core/providers/webgpu/shader_variable.h b/onnxruntime/core/providers/webgpu/shader_variable.h index f5bda85ca1b5e..045ceac8f5d77 100644 --- a/onnxruntime/core/providers/webgpu/shader_variable.h +++ b/onnxruntime/core/providers/webgpu/shader_variable.h @@ -15,7 +15,7 @@ namespace webgpu { template - requires (std::is_same_v || std::is_same_v) + requires(std::is_same_v || std::is_same_v) std::string GetElementAt(std::string_view var, const TIdx& idx, TRank rank, bool is_f16 = false) { if (var.starts_with("uniforms.")) { if (is_f16) { diff --git a/onnxruntime/core/providers/webgpu/string_utils.h b/onnxruntime/core/providers/webgpu/string_utils.h index 5faa02339956f..272679b75c87b 100644 --- a/onnxruntime/core/providers/webgpu/string_utils.h +++ b/onnxruntime/core/providers/webgpu/string_utils.h @@ -75,7 +75,7 @@ class FastOStringStream { // Integer types template - requires (std::is_integral_v && !std::is_same_v) + requires(std::is_integral_v && !std::is_same_v) FastOStringStream& operator<<(T value) { std::array buffer; auto [ptr, ec] = std::to_chars(buffer.data(), buffer.data() + buffer.size(), value); From b31a0a506568fbf1a931bc7b6f7654de1d950ccc Mon Sep 17 00:00:00 2001 From: Yulong Wang <7679871+fs-eire@users.noreply.github.com> Date: Mon, 30 Mar 2026 15:17:56 -0700 Subject: [PATCH 4/7] Fix strict aliasing violations in WebGPU EP - program.h: Replace C-style (int&) reference casts in enum bitwise operators with static_cast; remove -Wstrict-aliasing suppression - shader_variable.h: Replace (uint32_t&) reference casts in ShaderUsage bitwise operators with value-based operations; remove suppression - unary_elementwise_ops.cc: Replace reinterpret_cast(attr) with std::bit_cast for MLFloat16[2]->float packing; remove suppression Remaining -Wstrict-aliasing pragmas are for external headers only (dawn_proc.h, webgpu_cpp.h). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../webgpu/math/unary_elementwise_ops.cc | 11 +++-------- onnxruntime/core/providers/webgpu/program.h | 17 ++++------------- .../core/providers/webgpu/shader_variable.h | 16 ++++------------ 3 files changed, 11 insertions(+), 33 deletions(-) diff --git a/onnxruntime/core/providers/webgpu/math/unary_elementwise_ops.cc b/onnxruntime/core/providers/webgpu/math/unary_elementwise_ops.cc index 7db65a42eac4d..86c33fb954eba 100644 --- a/onnxruntime/core/providers/webgpu/math/unary_elementwise_ops.cc +++ b/onnxruntime/core/providers/webgpu/math/unary_elementwise_ops.cc @@ -194,10 +194,6 @@ class Clip final : public UnaryElementwise { "Clip", std::is_same_v ? ClipF16Impl : ClipImpl, "", ShaderUsage::UseElementTypeAlias} {} -#if defined(__GNUC__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wstrict-aliasing" -#endif Status ConfigureProgram(const ComputeContext& context, UnaryElementwiseProgram& program) const override { const auto* clip_min_tensor = context.Input(1); @@ -209,7 +205,9 @@ class Clip final : public UnaryElementwise { : std::numeric_limits::max()}; if constexpr (std::is_same_v) { // F16: stores span as a single float - float encoded_value = *reinterpret_cast(attr); + float encoded_value; + static_assert(sizeof(encoded_value) == 2 * sizeof(MLFloat16)); + std::memcpy(&encoded_value, attr, sizeof(encoded_value)); program.AddUniformVariable({encoded_value}); } else { static_assert(sizeof(T) == sizeof(float), "T must be f32, i32 or u32"); @@ -218,9 +216,6 @@ class Clip final : public UnaryElementwise { } return Status::OK(); } -#if defined(__GNUC__) -#pragma GCC diagnostic pop -#endif // uniforms.attr is a f32 value. It is encoded as a float for 2 f16 values. // bitcast>(uniforms.attr)[0] is clip_min, bitcast>(uniforms.attr)[1] is clip_max diff --git a/onnxruntime/core/providers/webgpu/program.h b/onnxruntime/core/providers/webgpu/program.h index 8dc85f1a7134e..ebb17b4e20700 100644 --- a/onnxruntime/core/providers/webgpu/program.h +++ b/onnxruntime/core/providers/webgpu/program.h @@ -165,28 +165,19 @@ enum class ProgramTensorMetadataDependency : int { }; OStringStream& operator<<(OStringStream& os, ProgramTensorMetadataDependency); -#if defined(__GNUC__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wstrict-aliasing" -#endif - inline ProgramTensorMetadataDependency operator|(ProgramTensorMetadataDependency a, ProgramTensorMetadataDependency b) { - return (ProgramTensorMetadataDependency)((int&)a | (int&)b); + return static_cast(static_cast(a) | static_cast(b)); } inline ProgramTensorMetadataDependency operator&(ProgramTensorMetadataDependency a, ProgramTensorMetadataDependency b) { - return (ProgramTensorMetadataDependency)((int&)a & (int&)b); + return static_cast(static_cast(a) & static_cast(b)); } inline ProgramTensorMetadataDependency& operator|=(ProgramTensorMetadataDependency& a, ProgramTensorMetadataDependency b) { - return (ProgramTensorMetadataDependency&)((int&)a |= (int&)b); + return a = a | b; } inline ProgramTensorMetadataDependency& operator&=(ProgramTensorMetadataDependency& a, ProgramTensorMetadataDependency b) { - return (ProgramTensorMetadataDependency&)((int&)a &= (int&)b); + return a = a & b; } -#if defined(__GNUC__) -#pragma GCC diagnostic pop -#endif - constexpr SafeInt WORKGROUP_SIZE = 64; // data type of variable diff --git a/onnxruntime/core/providers/webgpu/shader_variable.h b/onnxruntime/core/providers/webgpu/shader_variable.h index 045ceac8f5d77..c50453c9b23f0 100644 --- a/onnxruntime/core/providers/webgpu/shader_variable.h +++ b/onnxruntime/core/providers/webgpu/shader_variable.h @@ -210,30 +210,22 @@ class ShaderVariableHelper : public ShaderIndicesHelper { friend class ShaderHelper; }; -#if defined(__GNUC__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wstrict-aliasing" -#endif inline ShaderUsage operator|(ShaderUsage a, ShaderUsage b) { - return (uint32_t)a.usage | (uint32_t)b.usage; + return static_cast(a.usage) | static_cast(b.usage); } inline ShaderUsage operator&(ShaderUsage a, ShaderUsage b) { - return (uint32_t)a.usage & (uint32_t)b.usage; + return static_cast(a.usage) & static_cast(b.usage); } inline ShaderUsage& operator|=(ShaderUsage& a, ShaderUsage b) { - (uint32_t&)a.usage |= (uint32_t)b.usage; + a = a | b; return a; } inline ShaderUsage& operator&=(ShaderUsage& a, ShaderUsage b) { - (uint32_t&)a.usage &= (uint32_t)b.usage; + a = a & b; return a; } -#if defined(__GNUC__) -#pragma GCC diagnostic pop -#endif - namespace detail { template requires std::is_integral_v From 38b1cc7d9075ed8380033804fee84f6b708a1360 Mon Sep 17 00:00:00 2001 From: Yulong Wang <7679871+fs-eire@users.noreply.github.com> Date: Mon, 30 Mar 2026 15:39:40 -0700 Subject: [PATCH 5/7] Address PR review feedback - Add missing include for std::memcpy in unary_elementwise_ops.cc - Add missing include for std::isspace in einsum.cc - Fix misleading comment: decltype(T::member) detects static members only - Exclude program_test.cc from provider build (compile-time tests only) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- onnxruntime/core/providers/webgpu/math/einsum.cc | 1 + onnxruntime/core/providers/webgpu/math/unary_elementwise_ops.cc | 1 + onnxruntime/core/providers/webgpu/program.h | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/onnxruntime/core/providers/webgpu/math/einsum.cc b/onnxruntime/core/providers/webgpu/math/einsum.cc index e5ef659c03ebf..e608a13b68a1a 100644 --- a/onnxruntime/core/providers/webgpu/math/einsum.cc +++ b/onnxruntime/core/providers/webgpu/math/einsum.cc @@ -4,6 +4,7 @@ #include "core/providers/webgpu/math/einsum.h" #include +#include #include #include #include diff --git a/onnxruntime/core/providers/webgpu/math/unary_elementwise_ops.cc b/onnxruntime/core/providers/webgpu/math/unary_elementwise_ops.cc index 86c33fb954eba..b8a3f923e81cc 100644 --- a/onnxruntime/core/providers/webgpu/math/unary_elementwise_ops.cc +++ b/onnxruntime/core/providers/webgpu/math/unary_elementwise_ops.cc @@ -2,6 +2,7 @@ // Licensed under the MIT License. #include +#include #include #include "core/providers/webgpu/math/unary_elementwise_ops.h" diff --git a/onnxruntime/core/providers/webgpu/program.h b/onnxruntime/core/providers/webgpu/program.h index ebb17b4e20700..b702a30eb4794 100644 --- a/onnxruntime/core/providers/webgpu/program.h +++ b/onnxruntime/core/providers/webgpu/program.h @@ -416,7 +416,7 @@ template struct is_const_std_array> : std::true_type {}; // The following variable templates check whether certain static members exist in the derived class. -// Uses std::void_t for SFINAE-based member detection, working with both static and non-static members. +// Uses std::void_t with decltype(T::member) for SFINAE-based detection of named static data members. template inline constexpr bool has_member_constants = false; From 73f48d4ba50cde7bb88781a0a5cc545c8473dba0 Mon Sep 17 00:00:00 2001 From: Yulong Wang <7679871+fs-eire@users.noreply.github.com> Date: Mon, 30 Mar 2026 23:01:41 -0700 Subject: [PATCH 6/7] Address PR review comments: fix std::move, static_assert order, typo - string_utils.h: Use std::move(str_) in rvalue-qualified str() to avoid copying a data member (implicit move only applies to locals/params) - program.h: Move static_assert before .data()/.size() accesses so the user-friendly error message fires before cryptic pointer type errors - reduction_ops.h: Fix typo 'assigins' -> 'assigns' Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- onnxruntime/core/providers/webgpu/program.h | 18 +++++++++--------- .../providers/webgpu/reduction/reduction_ops.h | 2 +- .../core/providers/webgpu/string_utils.h | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/onnxruntime/core/providers/webgpu/program.h b/onnxruntime/core/providers/webgpu/program.h index b702a30eb4794..736111d2a18bc 100644 --- a/onnxruntime/core/providers/webgpu/program.h +++ b/onnxruntime/core/providers/webgpu/program.h @@ -470,39 +470,39 @@ class Program : public details::ProgramWrapper { static ProgramMetadata GetMetadata() { ProgramMetadata metadata; if constexpr (details::has_member_constants) { - constexpr const ProgramConstant* ptr = T::constants.data(); - constexpr size_t len = T::constants.size(); - static_assert(details::has_constants_correct_type, "Derived class of \"Program\" has member \"constants\" but its type is incorrect. " "Please use macro WEBGPU_PROGRAM_DEFINE_CONSTANTS() or WEBGPU_PROGRAM_EXTEND_CONSTANTS() to declare constants."); + constexpr const ProgramConstant* ptr = T::constants.data(); + constexpr size_t len = T::constants.size(); + metadata.constants = {ptr, len}; } else { metadata.constants = {}; } if constexpr (details::has_member_overridable_constants) { - constexpr const ProgramOverridableConstantDefinition* ptr = T::overridable_constants.data(); - constexpr size_t len = T::overridable_constants.size(); - static_assert(details::has_overridable_constants_correct_type, "Derived class of \"Program\" has member \"overridable_constants\" but its type is incorrect. " "Please use macro WEBGPU_PROGRAM_DEFINE_OVERRIDABLE_CONSTANTS() or WEBGPU_PROGRAM_EXTEND_OVERRIDABLE_CONSTANTS() to declare overridable constants."); + constexpr const ProgramOverridableConstantDefinition* ptr = T::overridable_constants.data(); + constexpr size_t len = T::overridable_constants.size(); + metadata.overridable_constants = {ptr, len}; } else { metadata.overridable_constants = {}; } if constexpr (details::has_member_uniform_variables) { - constexpr const ProgramUniformVariableDefinition* ptr = T::uniform_variables.data(); - constexpr size_t len = T::uniform_variables.size(); - static_assert(details::has_uniform_variables_correct_type, "Derived class of \"Program\" has member \"uniform_variables\" but its type is incorrect. " "Please use macro WEBGPU_PROGRAM_DEFINE_UNIFORM_VARIABLES() or WEBGPU_PROGRAM_EXTEND_UNIFORM_VARIABLES() to declare uniform variables."); + constexpr const ProgramUniformVariableDefinition* ptr = T::uniform_variables.data(); + constexpr size_t len = T::uniform_variables.size(); + metadata.uniform_variables = {ptr, len}; } else { metadata.uniform_variables = {}; diff --git a/onnxruntime/core/providers/webgpu/reduction/reduction_ops.h b/onnxruntime/core/providers/webgpu/reduction/reduction_ops.h index 8f3eb7b216bfa..6958b52a6c880 100644 --- a/onnxruntime/core/providers/webgpu/reduction/reduction_ops.h +++ b/onnxruntime/core/providers/webgpu/reduction/reduction_ops.h @@ -17,7 +17,7 @@ namespace webgpu { // The first element is the loop header, the second element is the loop body, and the third element is the loop footer. // The loop header is the code that is executed before the loop starts. The loop body is the code that is executed for each element in the loop. // The loop footer is the code that is executed after the loop ends. The loop body should contain the code that accumulates the result of the reduction and -// the loop footer should contain the code that assigins output_value the result of the reduction. +// the loop footer should contain the code that assigns output_value the result of the reduction. struct ReduceOpSpecificCode { std::string loop_header_; std::string loop_body_; diff --git a/onnxruntime/core/providers/webgpu/string_utils.h b/onnxruntime/core/providers/webgpu/string_utils.h index 272679b75c87b..cc96d8482fe2b 100644 --- a/onnxruntime/core/providers/webgpu/string_utils.h +++ b/onnxruntime/core/providers/webgpu/string_utils.h @@ -48,7 +48,7 @@ class FastOStringStream { } std::string str() && { - return str_; + return std::move(str_); } // String types From d9babd56a919c658c45a5d3d560a68787b3817bd Mon Sep 17 00:00:00 2001 From: Yulong Wang <7679871+fs-eire@users.noreply.github.com> Date: Wed, 1 Apr 2026 17:54:57 -0700 Subject: [PATCH 7/7] Rewrite program_test.cc to test production templates and concepts directly Address review feedback: the previous tests defined local clones (test_has_a, test_has_a_correct_type) that only used is_const_std_array from program.h. Now tests directly exercise the production utilities from program.h: - is_const_std_array trait - has_member_constants/overridable_constants/uniform_variables templates - has_constants/overridable_constants/uniform_variables_correct_type concepts The constants member gets thorough edge-case coverage (wrong name, plain int, const int, C-array, static C-array, non-const std::array, non-const static std::array, static constexpr, static const, static constexpr const). overridable_constants and uniform_variables are spot-checked. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../core/providers/webgpu/program_test.cc | 165 +++++++++++------- 1 file changed, 101 insertions(+), 64 deletions(-) diff --git a/onnxruntime/core/providers/webgpu/program_test.cc b/onnxruntime/core/providers/webgpu/program_test.cc index 6ed590a148ae8..2a524cce7a756 100644 --- a/onnxruntime/core/providers/webgpu/program_test.cc +++ b/onnxruntime/core/providers/webgpu/program_test.cc @@ -2,7 +2,7 @@ // Licensed under the MIT License. // Compile-time tests for the type detection utilities in program.h. -// These static_asserts verify that the has_member_* variable templates +// These static_asserts verify that is_const_std_array, the has_member_* variable templates, // and has_*_correct_type concepts correctly detect static const std::array members. #include "core/providers/webgpu/program.h" @@ -12,96 +12,133 @@ namespace webgpu { namespace details { namespace test { -// Test-specific member detection for a member named 'a' -template -inline constexpr bool test_has_a = false; -template -inline constexpr bool test_has_a> = true; +// ============================================================================ +// Tests for is_const_std_array +// ============================================================================ -// Test-specific type correctness concept for a member named 'a' -template -concept test_has_a_correct_type = requires { - T::a; - requires is_const_std_array::value; - requires std::is_const_v; - requires !std::is_member_pointer_v; +static_assert(!is_const_std_array::value); +static_assert(!is_const_std_array::value); +static_assert(!is_const_std_array>::value); +static_assert(is_const_std_array>::value); +static_assert(is_const_std_array>::value); + +// ============================================================================ +// Tests for has_member_constants / has_constants_correct_type +// ============================================================================ + +struct NoMembers {}; +static_assert(!has_member_constants); +static_assert(!has_constants_correct_type); + +struct Constants_WrongName { + static constexpr std::array not_constants = {1}; +}; +static_assert(!has_member_constants); +static_assert(!has_constants_correct_type); + +struct Constants_PlainInt { + int constants; +}; +static_assert(has_member_constants); +static_assert(!has_constants_correct_type); + +struct Constants_ConstInt { + const int constants; }; +static_assert(has_member_constants); +static_assert(!has_constants_correct_type); -struct TestClass_Empty {}; -static_assert(!test_has_a); -static_assert(!test_has_a_correct_type); +struct Constants_CArray { + const int constants[2]; +}; +static_assert(has_member_constants); +static_assert(!has_constants_correct_type); -struct TestClass_NotArray_0 { - int b; +struct Constants_StaticCArray { + static constexpr int constants[] = {0}; }; -static_assert(!test_has_a); -static_assert(!test_has_a_correct_type); +static_assert(has_member_constants); +static_assert(!has_constants_correct_type); -struct TestClass_NotArray_1 { - int a; +struct Constants_StaticNonConstCArray { + static int constants[]; }; -static_assert(test_has_a); -static_assert(!test_has_a_correct_type); +static_assert(has_member_constants); +static_assert(!has_constants_correct_type); -struct TestClass_NotArray_2 { - const int a; +struct Constants_StaticConstCArray { + static const int constants[]; }; -static_assert(test_has_a); -static_assert(!test_has_a_correct_type); +static_assert(has_member_constants); +static_assert(!has_constants_correct_type); -struct TestClass_NotStdArray_0 { - const int a[2]; +struct Constants_NonConstStdArray { + std::array constants = {1}; }; -static_assert(test_has_a); -static_assert(!test_has_a_correct_type); +static_assert(has_member_constants); +static_assert(!has_constants_correct_type); -struct TestClass_NotStdArray_1 { - static constexpr int a[] = {0}; +struct Constants_NonConstStaticStdArray { + static std::array constants; }; -static_assert(test_has_a); -static_assert(!test_has_a_correct_type); +static_assert(has_member_constants); +static_assert(!has_constants_correct_type); -struct TestClass_NotStdArray_2 { - static int a[]; +struct Constants_StaticConstexprStdArray { + static constexpr std::array constants = {1, 2}; }; -static_assert(test_has_a); -static_assert(!test_has_a_correct_type); +static_assert(has_member_constants); +static_assert(has_constants_correct_type); -struct TestClass_NotStdArray_3 { - static const int a[]; +struct Constants_StaticConstStdArray { + static const std::array constants; }; -static_assert(test_has_a); -static_assert(!test_has_a_correct_type); +static_assert(has_member_constants); +static_assert(has_constants_correct_type); -struct TestClass_StdArray_0 { - std::array a = {1}; +struct Constants_StaticConstexprConstStdArray { + static constexpr const std::array constants = {1, 2, 3, 4}; }; -static_assert(test_has_a); -static_assert(!test_has_a_correct_type); +static_assert(has_member_constants); +static_assert(has_constants_correct_type); + +// ============================================================================ +// Tests for has_member_overridable_constants / has_overridable_constants_correct_type +// ============================================================================ -struct TestClass_StdArray_1 { - static constexpr std::array a = {1, 2}; +static_assert(!has_member_overridable_constants); +static_assert(!has_overridable_constants_correct_type); + +struct OverridableConstants_WrongType { + int overridable_constants; }; -static_assert(test_has_a); -static_assert(test_has_a_correct_type); +static_assert(has_member_overridable_constants); +static_assert(!has_overridable_constants_correct_type); -struct TestClass_StdArray_2 { - static const std::array a; +struct OverridableConstants_Correct { + static constexpr std::array overridable_constants = {1}; }; -static_assert(test_has_a); -static_assert(test_has_a_correct_type); +static_assert(has_member_overridable_constants); +static_assert(has_overridable_constants_correct_type); + +// ============================================================================ +// Tests for has_member_uniform_variables / has_uniform_variables_correct_type +// ============================================================================ + +static_assert(!has_member_uniform_variables); +static_assert(!has_uniform_variables_correct_type); -struct TestClass_StdArray_3 { - static constexpr const std::array a = {1, 2, 3, 4}; +struct UniformVariables_WrongType { + int uniform_variables; }; -static_assert(test_has_a); -static_assert(test_has_a_correct_type); +static_assert(has_member_uniform_variables); +static_assert(!has_uniform_variables_correct_type); -struct TestClass_StdArray_4 { - static std::array a; +struct UniformVariables_Correct { + static constexpr std::array uniform_variables = {1}; }; -static_assert(test_has_a); -static_assert(!test_has_a_correct_type); +static_assert(has_member_uniform_variables); +static_assert(has_uniform_variables_correct_type); } // namespace test } // namespace details