Skip to content

Support building ONNX runtime with clang on Windows for Windows ML#29741

Draft
adrastogi wants to merge 4 commits into
mainfrom
adrastogi/clang-windows
Draft

Support building ONNX runtime with clang on Windows for Windows ML#29741
adrastogi wants to merge 4 commits into
mainfrom
adrastogi/clang-windows

Conversation

@adrastogi

Copy link
Copy Markdown
Contributor

Description

Enable ONNX Runtime to build with clang-cl on Windows for the Windows ML distribution flag set (--use_dml --enable_wcos --client_package_build --enable_msvc_static_runtime ). Adds a --use_clang_cl  build flag that selects the  ClangCL  VS platform toolset (x64 + Visual Studio generator only; ARM and non-VS generators are rejected), and fixes the source-level incompatibilities clang-cl surfaces that MSVC cl.exe silently tolerates:

• MLAS ( onnxruntime_mlas.cmake ): clang-cl feature-gates intrinsics, so per-ISA kernels get explicit -m flags (mirroring the non-MSVC branch); assembly-backed kernels like qgemm_kernel_avx2.cpp need none. Runtime CPUID dispatch is unchanged.
• spin_pause.cc: isolate the _tpause  ( waitpkg ) intrinsic into a helper carrying attribute((target("waitpkg"))) ; the  HasTPAUSE() runtime guard is preserved
• float16.h: give MLFloat16 derived-typed  operator== / !=  to resolve C++20 reversed-candidate ambiguity (behavior-preserving; mirrors BFloat16 ).
• DML EP: out-of-line  AbstractOperatorDesc  special members for two-phase name lookup; uint32_t → size_t  template params to match std::array deduction; drop an invalid ##  token-paste.
• Build/link: skip MSVC-only /Qspectre  under clang-cl; remove a duplicate  path_lib.cc  translation unit (lld-link enforces ODR); test-only  noexcept  macro adjustment.
• ONNX 1.22.0 patch: replace a  reinterpret_cast -based non-type template argument (INVALID_HANDLE_VALUE) in  scoped_resource.h  with an equivalent non-template  ScopedHandle  class.

No ABI changes; no change to MSVC build behavior (all clang-cl paths are compiler-gated).

Validation: Built and tested under both clang-cl and MSVC on a DirectML GPU (RTX 5060 Ti):  onnxruntime_test_all  1824/0 and  onnxruntime_provider_test  5435/0 with identical pass sets and clean DML inference.

Motivation and Context

Building ORT with clang on Windows unblocks security-testing workflows (ASAN / libFuzzer / WebNN fuzzing) that depend on Clang-only tooling.

Copilot AI and others added 4 commits July 10, 2026 00:08
Add a --use_clang_cl build.py option that selects the Visual Studio
'ClangCL' toolset (-T ClangCL,host=x64), plus the source/cmake fixes
required for the core onnxruntime.dll to compile with clang-cl:

- build_args.py: new --use_clang_cl arg + validation (VS generator only,
  not for arm/arm64/arm64ec).
- build.py: prepend 'ClangCL,' to the x64 VS toolset; guard /Qspectre
  (unsupported by clang-cl) in the binskim-compliant flag set.
- spin_pause.cc: isolate the _tpause (waitpkg) intrinsic in a
  target-attributed helper so clang-cl will emit it (MSVC unaffected).
- onnxruntime_mlas.cmake: clang-cl feature-gates x86 intrinsics (unlike
  MSVC cl.exe), so give the per-ISA x64 kernels that carry no /arch: flag
  the equivalent -m<feature> flags (mirroring the non-MSVC branch), incl.
  -msse4.1 for qgemm_kernel_sse41.cpp and -mamx-tile -mamx-int8 for
  qgemm_kernel_amx.cpp.

Relates to #29465, #1025.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b55a1989-57d8-45eb-88fb-8619f855611c
Three portability fixes let the DirectML execution provider build under
clang-cl (conforming two-phase name lookup / stricter template rules),
while remaining valid for MSVC cl.exe:

- AbstractOperatorDesc.h: OperatorField and AbstractOperatorDesc are
  mutually recursive, so OperatorField is incomplete where the struct is
  defined. Move the special member functions and the GetTensors template
  out of line, defined after OperatorField is completed (via a
  bottom-of-header include of GeneratedSchemaTypes.h). cl.exe defers these
  instantiations; clang-cl performs them at definition time and otherwise
  fails with 'incomplete type OperatorField'.
- MLOperatorAuthorImpl.cpp: drop the spurious token paste in
  'initializer.##Z()' (CASE_PROTO). '.##Z' never forms a valid token;
  clang-cl errors with -Winvalid-token-paste. 'initializer.Z()' is the
  intended member call on both compilers.
- DmlDFT.h / DmlGridSample.h: the Dispatch() non-type template parameter
  is deduced from std::array's size_type, so it must be size_t, not
  uint32_t; clang requires an exact type match for the deduced argument.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b55a1989-57d8-45eb-88fb-8619f855611c
Enable onnxruntime_test_all, onnxruntime_provider_test, and
onnxruntime_perf_test to build under clang-cl (lld-link). All fixes are
standard-conformant and behavior-preserving on MSVC cl.exe.

- float16.h: MLFloat16 defines operator==/!= on the derived type instead of
  inheriting the CRTP base version via using. Under C++20 (which clang-cl
  applies), comparing two MLFloat16 through the inherited base operator
  synthesizes a reversed candidate that is ambiguous with the non-reversed one.
  Defining a homogeneous derived-type operator== (mirroring BFloat16) resolves
  it. The delegating call Base::operator==(rhs) is qualified, so no reversed
  candidate is synthesized. Inline, ABI-neutral.

- element_wise_ops_test.cc: MATH_NO_EXCEPT now also expands to empty for clang
  targeting MSVC (defined(__clang__) && defined(_MSC_VER)). clang-cl sees the
  global-namespace UCRT math functions (::sinf, ::cosf, ...) as non-noexcept, so
  the reference template parameter float (op)(float) noexcept failed to bind.
  cl.exe and clang+Linux are unaffected.

- onnxruntime_unittests.cmake: stop compiling core/platform/path_lib.cc directly
  into onnxruntime_perf_test. onnxruntime_common (linked in both the shared and
  non-shared configurations) already provides GetDirNameFromFilePath; the direct
  compile is a duplicate definition that link.exe tolerates but lld-link rejects.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b55a1989-57d8-45eb-88fb-8619f855611c
ONNX 1.22.0's onnx/common/scoped_resource.h defines
`using ScopedHandle = ScopedResource<INVALID_HANDLE_VALUE, close_handle>`.
INVALID_HANDLE_VALUE expands to ((HANDLE)(LONG_PTR)-1), a reinterpret_cast
that is not a constant expression and therefore cannot be used as a non-type
template argument. MSVC accepts this as an extension; clang-cl rejects it as a
hard error ("non-type template argument is not a constant expression"), which
blocks clang-cl builds of ONNX Runtime against the pinned ONNX.

Extend cmake/patches/onnx/onnx.patch to replace the templated ScopedHandle
alias with an equivalent non-template ScopedHandle class (same get()/release()
API) that only references INVALID_HANDLE_VALUE at runtime. ScopedFd is left
untouched since -1 is a valid constant expression.

Validated on an RTX 5060 Ti GPU box: clang-cl and MSVC builds both compile the
patched ONNX cleanly, and onnxruntime_test_all (1824 passed / 0 failed) and
onnxruntime_provider_test (5440 tests / 0 failed) run identically under both
compilers with the DirectML EP on real hardware.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 716cc750-9984-4096-b8bb-e0f9e548ab80
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants