Skip to content

Commit 3756e79

Browse files
fs-eireCopilot
andauthored
[webgpu] house keeping after ORT switch to c++20 (microsoft#27904)
### Description WebGPU EP: C++20 housekeeping Modernize WebGPU EP code after the repo-wide C++20 migration. 19 files changed, net -3 lines. C++20 idiom modernization: - find() != end() / count() > 0 → contains() (4 sites) - .size() == 0 / .size() > 0 → .empty() (6 sites) - std::enable_if_t SFINAE → requires clauses (4 sites) - typedef struct → plain struct (1 site) - erase-remove idiom → std::erase_if (1 site) - Redundant std::move in rvalue-qualified return removed (1 site) - + vs << inconsistency in stream operator chains fixed (2 sites) Strict aliasing fixes (removed 3 #pragma GCC diagnostic ignored "-Wstrict-aliasing" blocks): - program.h: Replaced C-style (int&)a | (int&)b reference casts in ProgramTensorMetadataDependency bitwise operators with static_cast - shader_variable.h: Replaced (uint32_t&)a.usage |= ... reference casts in ShaderUsage bitwise operators with value-based operations - unary_elementwise_ops.cc: Replaced *reinterpret_cast<const float*>(attr) with std::memcpy for MLFloat16[2] → float type punning (consistent with pad.cc) ### Motivation and Context <!-- - Why is this change required? What problem does it solve? - If it fixes an open issue, please link to the issue here. --> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent eb706ed commit 3756e79

19 files changed

Lines changed: 237 additions & 201 deletions

cmake/onnxruntime_providers_webgpu.cmake

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,6 @@
248248
endif()
249249
endif()
250250
251-
target_compile_features(onnxruntime_providers_webgpu PRIVATE cxx_std_20)
252251
add_dependencies(onnxruntime_providers_webgpu onnx ${onnxruntime_EXTERNAL_DEPENDENCIES})
253252
254253
if (onnxruntime_WGSL_TEMPLATE)

onnxruntime/contrib_ops/webgpu/bert/bias_add.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ Status BiasAddProgram::GenerateShaderCode(ShaderHelper& shader) const {
3030
<< " let value = " << input.GetByOffset("global_idx")
3131
<< " + " << bias.GetByOffset("global_idx % uniforms.channels")
3232
<< " + " << residual.GetByOffset("global_idx") << ";\n"
33-
<< " " + output.SetByOffset("global_idx", "value");
33+
<< " " << output.SetByOffset("global_idx", "value");
3434

3535
return Status::OK();
3636
}

onnxruntime/contrib_ops/webgpu/bert/rotary_embedding.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ Status RotaryEmbeddingProgram::GenerateShaderCode(ShaderHelper& shader) const {
4040
<< " let j = i + select(half_rotary_emb_dim, 1, " << interleaved_str << ");\n"
4141
<< " let re = " << input.GetByOffset("i") << " * " << cos_cache.GetByIndices("vec2<u32>(position_id, bsnh[3])") << " - " << input.GetByOffset("j") << " * " << sin_cache.GetByIndices("vec2<u32>(position_id, bsnh[3])") << ";\n"
4242
<< " " << output.SetByOffset("i", "re") << "\n"
43-
<< " let im = " << input.GetByOffset("i") << " * " << sin_cache.GetByIndices("vec2<u32>(position_id, bsnh[3])") << " + " << input.GetByOffset("j") + " * " << cos_cache.GetByIndices("vec2<u32>(position_id, bsnh[3])") << ";\n"
43+
<< " let im = " << input.GetByOffset("i") << " * " << sin_cache.GetByIndices("vec2<u32>(position_id, bsnh[3])") << " + " << input.GetByOffset("j") << " * " << cos_cache.GetByIndices("vec2<u32>(position_id, bsnh[3])") << ";\n"
4444
<< " " << output.SetByOffset("j", "im") << "\n"
4545
<< " } else { \n"
4646
" let k = dot(bsnh, uniforms.input_output_stride) + half_rotary_emb_dim;\n"

onnxruntime/core/providers/webgpu/math/einsum.cc

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include "core/providers/webgpu/math/einsum.h"
55

66
#include <algorithm>
7+
#include <cctype>
78
#include <regex>
89
#include <set>
910
#include <vector>
@@ -24,7 +25,7 @@ static const std::regex lhs_pattern("(([a-zA-Z]|\\.\\.\\.)*,)*([a-zA-Z]|\\.\\.\\
2425
// Helper function to remove all whitespaces in a given string.
2526
std::string RemoveAllWhitespace(const std::string& str) {
2627
std::string result = str;
27-
result.erase(std::remove_if(result.begin(), result.end(), ::isspace), result.end());
28+
std::erase_if(result, [](unsigned char c) { return std::isspace(c); });
2829
return result;
2930
}
3031

@@ -318,7 +319,7 @@ Status EinsumProgram::GenerateShaderCode(ShaderHelper& shader) const {
318319
symbol));
319320

320321
// Check if we've already processed this symbol to avoid duplicate loop generation
321-
if (uniform_symbol_set.find(symbol) == uniform_symbol_set.end()) {
322+
if (!uniform_symbol_set.contains(symbol)) {
322323
// Add symbol to tracked set to prevent duplicate processing
323324
uniform_symbol_set.insert(symbol);
324325

onnxruntime/core/providers/webgpu/math/unary_elementwise_ops.cc

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the MIT License.
33

44
#include <utility>
5+
#include <cstring>
56
#include <limits>
67

78
#include "core/providers/webgpu/math/unary_elementwise_ops.h"
@@ -194,10 +195,6 @@ class Clip final : public UnaryElementwise {
194195
"Clip",
195196
std::is_same_v<T, MLFloat16> ? ClipF16Impl : ClipImpl,
196197
"", ShaderUsage::UseElementTypeAlias} {}
197-
#if defined(__GNUC__)
198-
#pragma GCC diagnostic push
199-
#pragma GCC diagnostic ignored "-Wstrict-aliasing"
200-
#endif
201198

202199
Status ConfigureProgram(const ComputeContext& context, UnaryElementwiseProgram& program) const override {
203200
const auto* clip_min_tensor = context.Input<Tensor>(1);
@@ -209,7 +206,9 @@ class Clip final : public UnaryElementwise {
209206
: std::numeric_limits<T>::max()};
210207
if constexpr (std::is_same_v<T, MLFloat16>) {
211208
// F16: stores span<f16, 2> as a single float
212-
float encoded_value = *reinterpret_cast<const float*>(attr);
209+
float encoded_value;
210+
static_assert(sizeof(encoded_value) == 2 * sizeof(MLFloat16));
211+
std::memcpy(&encoded_value, attr, sizeof(encoded_value));
213212
program.AddUniformVariable({encoded_value});
214213
} else {
215214
static_assert(sizeof(T) == sizeof(float), "T must be f32, i32 or u32");
@@ -218,9 +217,6 @@ class Clip final : public UnaryElementwise {
218217
}
219218
return Status::OK();
220219
}
221-
#if defined(__GNUC__)
222-
#pragma GCC diagnostic pop
223-
#endif
224220

225221
// uniforms.attr is a f32 value. It is encoded as a float for 2 f16 values.
226222
// bitcast<vec2<f16>>(uniforms.attr)[0] is clip_min, bitcast<vec2<f16>>(uniforms.attr)[1] is clip_max

onnxruntime/core/providers/webgpu/nn/pool.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,7 @@ Status Pool<PoolType, is_nhwc>::ComputeInternal(ComputeContext& context) const {
249249
Tensor* Y = context.Output(0, output_shape);
250250

251251
std::vector<uint32_t> kernel_strides(kernel_shape.size());
252-
ORT_ENFORCE(kernel_shape.size() > 0, "kernel_shape must have at least one element.");
252+
ORT_ENFORCE(!kernel_shape.empty(), "kernel_shape must have at least one element.");
253253
// Calculate the kernel element strides for each dimension in reverse order. For example:
254254
// kernel_shape = [3, 2], kernel_strides = [2, 1]
255255
// kernel_shape = [2, 3, 2], kernel_strides = [6, 2, 1]

onnxruntime/core/providers/webgpu/program.h

Lines changed: 53 additions & 153 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
#pragma once
55

6+
#include <array>
67
#include <string>
78
#include <vector>
89
#include <iosfwd>
@@ -164,28 +165,19 @@ enum class ProgramTensorMetadataDependency : int {
164165
};
165166
OStringStream& operator<<(OStringStream& os, ProgramTensorMetadataDependency);
166167

167-
#if defined(__GNUC__)
168-
#pragma GCC diagnostic push
169-
#pragma GCC diagnostic ignored "-Wstrict-aliasing"
170-
#endif
171-
172168
inline ProgramTensorMetadataDependency operator|(ProgramTensorMetadataDependency a, ProgramTensorMetadataDependency b) {
173-
return (ProgramTensorMetadataDependency)((int&)a | (int&)b);
169+
return static_cast<ProgramTensorMetadataDependency>(static_cast<int>(a) | static_cast<int>(b));
174170
}
175171
inline ProgramTensorMetadataDependency operator&(ProgramTensorMetadataDependency a, ProgramTensorMetadataDependency b) {
176-
return (ProgramTensorMetadataDependency)((int&)a & (int&)b);
172+
return static_cast<ProgramTensorMetadataDependency>(static_cast<int>(a) & static_cast<int>(b));
177173
}
178174
inline ProgramTensorMetadataDependency& operator|=(ProgramTensorMetadataDependency& a, ProgramTensorMetadataDependency b) {
179-
return (ProgramTensorMetadataDependency&)((int&)a |= (int&)b);
175+
return a = a | b;
180176
}
181177
inline ProgramTensorMetadataDependency& operator&=(ProgramTensorMetadataDependency& a, ProgramTensorMetadataDependency b) {
182-
return (ProgramTensorMetadataDependency&)((int&)a &= (int&)b);
178+
return a = a & b;
183179
}
184180

185-
#if defined(__GNUC__)
186-
#pragma GCC diagnostic pop
187-
#endif
188-
189181
constexpr SafeInt<uint32_t> WORKGROUP_SIZE = 64;
190182

191183
// data type of variable
@@ -417,133 +409,55 @@ class ProgramWrapper : public ProgramBase {
417409
ProgramWrapper(Args&&... args) : ProgramBase{std::forward<Args>(args)...} {}
418410
};
419411

420-
#if defined(ORT_WEBGPU_REGISTER_DERIVED_PROGRAM_CLASS_TYPE_CHECK)
421-
#error "macro ORT_WEBGPU_REGISTER_DERIVED_PROGRAM_CLASS_TYPE_CHECK is already defined"
422-
#endif
423-
424-
#define ORT_WEBGPU_REGISTER_DERIVED_PROGRAM_CLASS_TYPE_CHECK(identifier, element_type) \
425-
private: \
426-
template <typename U> \
427-
static auto test_has_##identifier(int) -> decltype(U::identifier, std::true_type{}); /* checks if member exists */ \
428-
template <typename...> \
429-
static auto test_has_##identifier(...) -> std::false_type; \
430-
\
431-
template <typename U, /* The following type check uses SFINAE */ \
432-
typename = std::enable_if_t< /* to ensure the specific member: */ \
433-
is_const_std_array<decltype(U::identifier)>::value && /* - is a const std::array */ \
434-
std::is_const_v<decltype(U::identifier)> && /* - has "const" modifier */ \
435-
!std::is_member_pointer_v<decltype(&U::identifier)>>> /* - is static */ \
436-
static auto test_has_##identifier##_with_correct_type(int) -> std::true_type; \
437-
template <typename...> \
438-
static auto test_has_##identifier##_with_correct_type(...) -> std::false_type; \
439-
\
440-
public: \
441-
static constexpr bool has_##identifier = decltype(test_has_##identifier<T>(0))::value; \
442-
static constexpr bool has_##identifier##_with_correct_type = decltype(test_has_##identifier##_with_correct_type<T>(0))::value
443-
444412
// the following template class checks whether the type is a const std::array
445413
template <typename T>
446414
struct is_const_std_array : std::false_type {};
447415
template <typename T, size_t N>
448416
struct is_const_std_array<const std::array<T, N>> : std::true_type {};
449417

450-
// the following template class checks whether certain static members exist in the derived class (SFINAE)
451-
template <typename T>
452-
class DerivedProgramClassTypeCheck {
453-
ORT_WEBGPU_REGISTER_DERIVED_PROGRAM_CLASS_TYPE_CHECK(constants, ProgramConstant);
454-
ORT_WEBGPU_REGISTER_DERIVED_PROGRAM_CLASS_TYPE_CHECK(overridable_constants, ProgramOverridableConstantDefinition);
455-
ORT_WEBGPU_REGISTER_DERIVED_PROGRAM_CLASS_TYPE_CHECK(uniform_variables, ProgramUniformVariableDefinition);
456-
};
457-
458-
// compile-time tests for the type check
459-
//
460-
// TODO: move this to test folder
461-
namespace test {
418+
// The following variable templates check whether certain static members exist in the derived class.
419+
// Uses std::void_t with decltype(T::member) for SFINAE-based detection of named static data members.
462420

421+
template <typename T, typename = void>
422+
inline constexpr bool has_member_constants = false;
463423
template <typename T>
464-
class TestTypeCheck {
465-
ORT_WEBGPU_REGISTER_DERIVED_PROGRAM_CLASS_TYPE_CHECK(a, int);
466-
};
467-
468-
struct TestClass_Empty {};
469-
static_assert(!TestTypeCheck<TestClass_Empty>::has_a);
470-
static_assert(!TestTypeCheck<TestClass_Empty>::has_a_with_correct_type);
471-
472-
struct TestClass_NotArray_0 {
473-
int b;
474-
};
475-
static_assert(!TestTypeCheck<TestClass_NotArray_0>::has_a);
476-
static_assert(!TestTypeCheck<TestClass_NotArray_0>::has_a_with_correct_type);
424+
inline constexpr bool has_member_constants<T, std::void_t<decltype(T::constants)>> = true;
477425

478-
struct TestClass_NotArray_1 {
479-
int a;
480-
};
481-
static_assert(TestTypeCheck<TestClass_NotArray_1>::has_a);
482-
static_assert(!TestTypeCheck<TestClass_NotArray_1>::has_a_with_correct_type);
483-
484-
struct TestClass_NotArray_2 {
485-
const int a;
486-
};
487-
static_assert(TestTypeCheck<TestClass_NotArray_2>::has_a);
488-
static_assert(!TestTypeCheck<TestClass_NotArray_2>::has_a_with_correct_type);
489-
490-
struct TestClass_NotStdArray_0 {
491-
const int a[2];
492-
};
493-
static_assert(TestTypeCheck<TestClass_NotStdArray_0>::has_a);
494-
static_assert(!TestTypeCheck<TestClass_NotStdArray_0>::has_a_with_correct_type);
495-
496-
struct TestClass_NotStdArray_1 {
497-
static constexpr int a[] = {0};
498-
};
499-
static_assert(TestTypeCheck<TestClass_NotStdArray_1>::has_a);
500-
static_assert(!TestTypeCheck<TestClass_NotStdArray_1>::has_a_with_correct_type);
501-
502-
struct TestClass_NotStdArray_2 {
503-
static int a[];
504-
};
505-
static_assert(TestTypeCheck<TestClass_NotStdArray_2>::has_a);
506-
static_assert(!TestTypeCheck<TestClass_NotStdArray_2>::has_a_with_correct_type);
507-
508-
struct TestClass_NotStdArray_3 {
509-
static const int a[];
510-
};
511-
static_assert(TestTypeCheck<TestClass_NotStdArray_3>::has_a);
512-
static_assert(!TestTypeCheck<TestClass_NotStdArray_3>::has_a_with_correct_type);
426+
template <typename T, typename = void>
427+
inline constexpr bool has_member_overridable_constants = false;
428+
template <typename T>
429+
inline constexpr bool has_member_overridable_constants<T, std::void_t<decltype(T::overridable_constants)>> = true;
513430

514-
struct TestClass_StdArray_0 {
515-
std::array<int, 1> a = {1};
516-
};
517-
static_assert(TestTypeCheck<TestClass_StdArray_0>::has_a);
518-
static_assert(!TestTypeCheck<TestClass_StdArray_0>::has_a_with_correct_type);
431+
template <typename T, typename = void>
432+
inline constexpr bool has_member_uniform_variables = false;
433+
template <typename T>
434+
inline constexpr bool has_member_uniform_variables<T, std::void_t<decltype(T::uniform_variables)>> = true;
519435

520-
struct TestClass_StdArray_1 {
521-
static constexpr std::array<int, 2> a = {1, 2};
522-
};
523-
static_assert(TestTypeCheck<TestClass_StdArray_1>::has_a);
524-
static_assert(TestTypeCheck<TestClass_StdArray_1>::has_a_with_correct_type);
436+
// C++20 concepts for checking whether the member has the correct type (static const std::array).
525437

526-
struct TestClass_StdArray_2 {
527-
static const std::array<int, 3> a;
438+
template <typename T>
439+
concept has_constants_correct_type = requires {
440+
T::constants;
441+
requires is_const_std_array<decltype(T::constants)>::value;
442+
requires std::is_const_v<decltype(T::constants)>;
443+
requires !std::is_member_pointer_v<decltype(&T::constants)>;
528444
};
529-
static_assert(TestTypeCheck<TestClass_StdArray_2>::has_a);
530-
static_assert(TestTypeCheck<TestClass_StdArray_2>::has_a_with_correct_type);
531445

532-
struct TestClass_StdArray_3 {
533-
static constexpr const std::array<int, 4> a = {1, 2, 3, 4};
446+
template <typename T>
447+
concept has_overridable_constants_correct_type = requires {
448+
T::overridable_constants;
449+
requires is_const_std_array<decltype(T::overridable_constants)>::value;
450+
requires std::is_const_v<decltype(T::overridable_constants)>;
451+
requires !std::is_member_pointer_v<decltype(&T::overridable_constants)>;
534452
};
535-
static_assert(TestTypeCheck<TestClass_StdArray_3>::has_a);
536-
static_assert(TestTypeCheck<TestClass_StdArray_3>::has_a_with_correct_type);
537453

538-
struct TestClass_StdArray_4 {
539-
static std::array<int, 5> a;
454+
template <typename T>
455+
concept has_uniform_variables_correct_type = requires {
456+
T::uniform_variables;
457+
requires is_const_std_array<decltype(T::uniform_variables)>::value;
458+
requires std::is_const_v<decltype(T::uniform_variables)>;
459+
requires !std::is_member_pointer_v<decltype(&T::uniform_variables)>;
540460
};
541-
static_assert(TestTypeCheck<TestClass_StdArray_4>::has_a);
542-
static_assert(!TestTypeCheck<TestClass_StdArray_4>::has_a_with_correct_type);
543-
544-
} // namespace test
545-
546-
#undef ORT_WEBGPU_REGISTER_DERIVED_PROGRAM_CLASS_TYPE_CHECK
547461

548462
} // namespace details
549463

@@ -555,40 +469,40 @@ class Program : public details::ProgramWrapper {
555469

556470
static ProgramMetadata GetMetadata() {
557471
ProgramMetadata metadata;
558-
if constexpr (details::DerivedProgramClassTypeCheck<T>::has_constants) {
559-
constexpr const ProgramConstant* ptr = T::constants.data();
560-
constexpr size_t len = T::constants.size();
561-
562-
static_assert(details::DerivedProgramClassTypeCheck<T>::has_constants_with_correct_type,
472+
if constexpr (details::has_member_constants<T>) {
473+
static_assert(details::has_constants_correct_type<T>,
563474
"Derived class of \"Program\" has member \"constants\" but its type is incorrect. "
564475
"Please use macro WEBGPU_PROGRAM_DEFINE_CONSTANTS() or WEBGPU_PROGRAM_EXTEND_CONSTANTS() to declare constants.");
565476

477+
constexpr const ProgramConstant* ptr = T::constants.data();
478+
constexpr size_t len = T::constants.size();
479+
566480
metadata.constants = {ptr, len};
567481
} else {
568482
metadata.constants = {};
569483
}
570484

571-
if constexpr (details::DerivedProgramClassTypeCheck<T>::has_overridable_constants) {
572-
constexpr const ProgramOverridableConstantDefinition* ptr = T::overridable_constants.data();
573-
constexpr size_t len = T::overridable_constants.size();
574-
575-
static_assert(details::DerivedProgramClassTypeCheck<T>::has_overridable_constants_with_correct_type,
485+
if constexpr (details::has_member_overridable_constants<T>) {
486+
static_assert(details::has_overridable_constants_correct_type<T>,
576487
"Derived class of \"Program\" has member \"overridable_constants\" but its type is incorrect. "
577488
"Please use macro WEBGPU_PROGRAM_DEFINE_OVERRIDABLE_CONSTANTS() or WEBGPU_PROGRAM_EXTEND_OVERRIDABLE_CONSTANTS() to declare overridable constants.");
578489

490+
constexpr const ProgramOverridableConstantDefinition* ptr = T::overridable_constants.data();
491+
constexpr size_t len = T::overridable_constants.size();
492+
579493
metadata.overridable_constants = {ptr, len};
580494
} else {
581495
metadata.overridable_constants = {};
582496
}
583497

584-
if constexpr (details::DerivedProgramClassTypeCheck<T>::has_uniform_variables) {
585-
constexpr const ProgramUniformVariableDefinition* ptr = T::uniform_variables.data();
586-
constexpr size_t len = T::uniform_variables.size();
587-
588-
static_assert(details::DerivedProgramClassTypeCheck<T>::has_uniform_variables_with_correct_type,
498+
if constexpr (details::has_member_uniform_variables<T>) {
499+
static_assert(details::has_uniform_variables_correct_type<T>,
589500
"Derived class of \"Program\" has member \"uniform_variables\" but its type is incorrect. "
590501
"Please use macro WEBGPU_PROGRAM_DEFINE_UNIFORM_VARIABLES() or WEBGPU_PROGRAM_EXTEND_UNIFORM_VARIABLES() to declare uniform variables.");
591502

503+
constexpr const ProgramUniformVariableDefinition* ptr = T::uniform_variables.data();
504+
constexpr size_t len = T::uniform_variables.size();
505+
592506
metadata.uniform_variables = {ptr, len};
593507
} else {
594508
metadata.uniform_variables = {};
@@ -599,20 +513,6 @@ class Program : public details::ProgramWrapper {
599513
};
600514

601515
namespace details {
602-
// helper function to convert a C-style array to std::array
603-
//
604-
// This is basically the same as std::to_array in C++20.
605-
//
606-
template <typename T, size_t N, size_t... Idx>
607-
constexpr auto _to_std_array_impl(T (&arr)[N], std::index_sequence<Idx...>) -> std::array<std::remove_cv_t<T>, N> {
608-
return {{arr[Idx]...}};
609-
}
610-
611-
template <typename T, size_t N>
612-
constexpr auto _to_std_array(T (&arr)[N]) -> std::array<std::remove_cv_t<T>, N> {
613-
return _to_std_array_impl(arr, std::make_index_sequence<N>{});
614-
}
615-
616516
// helper function to concatenate a std::array and a C-style array to a std::array
617517
//
618518
template <typename T, size_t L, size_t... IdxL, size_t R, size_t... IdxR>
@@ -632,7 +532,7 @@ constexpr std::array<std::remove_cv_t<T>, L + R> _concat2(const std::array<T, L>
632532
#define WEBGPU_PROGRAM_DEFINE_(identifier, T, ...) \
633533
static constexpr const T identifier##_own[] = {__VA_ARGS__}; \
634534
static constexpr const auto identifier = \
635-
onnxruntime::webgpu::details::_to_std_array(identifier##_own)
535+
std::to_array(identifier##_own)
636536

637537
#define WEBGPU_PROGRAM_EXTEND_(identifier, T, BASE, ...) \
638538
static constexpr const T identifier##_own[] = {__VA_ARGS__}; \

0 commit comments

Comments
 (0)