Skip to content

Commit cee6a3d

Browse files
authored
Merge branch 'main' into fix-spotless-module-kt
2 parents 5940bf2 + 129c687 commit cee6a3d

59 files changed

Lines changed: 6673 additions & 236 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.ci/scripts/setup-webgpu-linux-deps.sh

Lines changed: 83 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,93 @@
55
# This source code is licensed under the BSD-style license found in the
66
# LICENSE file in the root directory of this source tree.
77

8+
# Vendor Dawn (Tint) + SwiftShader for the WebGPU backend CI WITHOUT hosting a
9+
# private prebuilt:
10+
# * Dawn : Google's official nightly prebuilt, downloaded directly from
11+
# github.com/google/dawn/releases (pinned tag+rev+sha256) -- the same
12+
# "fetch a pinned upstream prebuilt" pattern used for other CI deps.
13+
# * SwiftShader : built from source at a pinned rev compatible with the Dawn
14+
# above (the ossci prebuilt is from 2020, too old for current Dawn). No S3.
15+
# Dawn (Chrome's WebGPU impl; its WGSL compiler Tint is the spec reference) on
16+
# SwiftShader gives a headless, deterministic, spec-faithful CLI backend.
17+
#
18+
# Exports Dawn_DIR / VK_ICD_FILENAMES / LD_LIBRARY_PATH for the cmake build+run.
19+
# Local/rig override: set DAWN_PREBUILT_DIR=<dir containing lib64/cmake/Dawn> to
20+
# skip the Dawn download.
821
set -ex
922

10-
# SwiftShader: software Vulkan adapter for GPU-less CI (LunarG SDK not needed).
11-
install_swiftshader() {
12-
_https_amazon_aws=https://ossci-android.s3.amazonaws.com
13-
_swiftshader_archive=swiftshader-abe07b943-prebuilt.tar.gz
14-
_swiftshader_dir=/tmp/swiftshader
15-
mkdir -p $_swiftshader_dir
23+
# --- pinned versions (bump rev+sha together when upgrading Dawn) --------------
24+
DAWN_TAG="${DAWN_TAG:-v20260423.175430}"
25+
DAWN_REV="${DAWN_REV:-31e25af254ab572c77054edec4946d2244e184dd}"
26+
DAWN_SHA256="${DAWN_SHA256:-ac76fac090162dc1ecea5ed0f28a557bb8f49efc47faab01886105ace82b7b64}"
27+
# SwiftShader rev verified compatible with DAWN_REV (the old ossci prebuilt is
28+
# from 2020 and is incompatible with current Dawn -> no adapter / zero compute).
29+
SWIFTSHADER_REV="${SWIFTSHADER_REV:-9898204d91d6a60b6a08ad74fe4ac52a6913111b}"
1630

17-
_tmp_archive="/tmp/${_swiftshader_archive}"
31+
_dawn_dir="${DAWN_PREBUILT_DIR:-/tmp/dawn-ci}"
32+
_ss_dir=/tmp/swiftshader
1833

19-
curl --silent --show-error --location --fail --retry 3 --retry-all-errors \
20-
--output "${_tmp_archive}" "$_https_amazon_aws/${_swiftshader_archive}"
34+
# --- toolchain prereqs --------------------------------------------------------
35+
# Dawn dlopens the system Vulkan loader at runtime (libvulkan1). And the
36+
# ubuntu-latest prebuilt is built with a bleeding-edge GCC: it references
37+
# libstdc++ symbols newer than ubuntu-22.04's default (e.g. _M_replace_cold,
38+
# GCC 13+), so the static .a won't link against the stock runtime. Pull a current
39+
# libstdc++ from the ubuntu-toolchain-r PPA when the symbol floor isn't met. All
40+
# of this is scoped to the WebGPU CI job; newer libstdc++ is backward-compatible.
41+
if command -v apt-get >/dev/null 2>&1; then
42+
_SUDO=""; command -v sudo >/dev/null 2>&1 && _SUDO="sudo"
43+
${_SUDO} apt-get update -y || true
44+
${_SUDO} apt-get install -y libvulkan1 software-properties-common || true
45+
if ! strings /usr/lib/x86_64-linux-gnu/libstdc++.so.6 2>/dev/null \
46+
| grep -q "GLIBCXX_3.4.32"; then
47+
${_SUDO} add-apt-repository -y ppa:ubuntu-toolchain-r/test || true
48+
${_SUDO} apt-get update -y || true
49+
${_SUDO} apt-get install -y libstdc++6 || true # newest GCC runtime
50+
fi
51+
fi
2152

22-
tar -C "${_swiftshader_dir}" -xzf "${_tmp_archive}"
53+
# The native binaries / pybind lib run INSIDE the CI conda env, whose libstdc++
54+
# predates GLIBCXX_3.4.32 (the Dawn prebuilt's floor) -- the same wall ssjia hit
55+
# for the vulkan op tests. Upgrade the conda runtime libstdc++ so the loaded
56+
# libstdc++.so.6 (conda's, not the system one) satisfies Dawn at run time.
57+
if command -v conda >/dev/null 2>&1; then
58+
conda install -y -c conda-forge "libstdcxx-ng>=14" || true
59+
fi
60+
61+
# --- Dawn: official prebuilt from GitHub (no S3) ------------------------------
62+
mkdir -p "${_dawn_dir}"
63+
if [[ ! -d "${_dawn_dir}/lib64/cmake/Dawn" ]]; then
64+
_dawn_tar="/tmp/Dawn-${DAWN_REV}-ubuntu-latest-Release.tar.gz"
65+
curl --silent --show-error --location --fail --retry 3 --retry-all-errors \
66+
--output "${_dawn_tar}" \
67+
"https://github.com/google/dawn/releases/download/${DAWN_TAG}/Dawn-${DAWN_REV}-ubuntu-latest-Release.tar.gz"
68+
echo "${DAWN_SHA256} ${_dawn_tar}" | sha256sum -c -
69+
# archive top dir is Dawn-<rev>-ubuntu-latest-Release/{lib64,include,bin}
70+
tar -C "${_dawn_dir}" --strip-components=1 -xzf "${_dawn_tar}"
71+
fi
2372

24-
export VK_ICD_FILENAMES="${_swiftshader_dir}/swiftshader/build/Linux/vk_swiftshader_icd.json"
25-
export LD_LIBRARY_PATH="${_swiftshader_dir}/swiftshader/build/Linux/:${LD_LIBRARY_PATH}"
26-
export ETVK_USING_SWIFTSHADER=1
27-
}
73+
# --- SwiftShader: build from source at a pinned rev (no S3) -------------------
74+
# The old ossci prebuilt (swiftshader-abe07b943, 2020) is incompatible with the
75+
# current Dawn; build a matching modern SwiftShader instead. Self-contained
76+
# cmake build (vendored LLVM); the ICD lands under build/<OS>/.
77+
if [[ ! -d "${_ss_dir}/build" ]]; then
78+
if [[ ! -d "${_ss_dir}/.git" ]]; then
79+
git clone https://github.com/google/swiftshader "${_ss_dir}"
80+
fi
81+
git -C "${_ss_dir}" checkout "${SWIFTSHADER_REV}"
82+
# vk_swiftshader's deps are vendored in-tree; tolerate unreachable
83+
# disabled-feature submodules (angle, test-only) failing to fetch.
84+
git -C "${_ss_dir}" submodule update --init --recursive || true
85+
cmake -S "${_ss_dir}" -B "${_ss_dir}/build" -DCMAKE_BUILD_TYPE=Release \
86+
-DSWIFTSHADER_BUILD_TESTS=OFF -DSWIFTSHADER_BUILD_PVR=OFF \
87+
-DSWIFTSHADER_BUILD_BENCHMARKS=OFF
88+
cmake --build "${_ss_dir}/build" --parallel "$(nproc)" --target vk_swiftshader
89+
fi
90+
_ss_icd="$(find "${_ss_dir}/build" -name vk_swiftshader_icd.json 2>/dev/null | head -1)"
91+
[[ -n "${_ss_icd}" ]] || { echo "ERROR: SwiftShader ICD not found after build" >&2; exit 1; }
2892

29-
install_swiftshader
30-
bash backends/webgpu/scripts/setup-wgpu-native.sh
93+
_ss_libdir="$(dirname "${_ss_icd}")"
94+
export Dawn_DIR="${_dawn_dir}/lib64/cmake/Dawn"
95+
export VK_ICD_FILENAMES="${_ss_icd}"
96+
export LD_LIBRARY_PATH="${_ss_libdir}:${LD_LIBRARY_PATH:-}"
97+
export WEBGPU_USING_SWIFTSHADER=1

.ci/scripts/test_backend.sh

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,10 @@ if [[ "$FLOW" == *vulkan* ]]; then
5858
fi
5959

6060
if [[ "$FLOW" == *webgpu* ]]; then
61-
# Setup swiftshader (software Vulkan adapter for GPU-less runners) and wgpu-native,
62-
# which are required to build and run the WebGPU delegate.
61+
# Dawn (Tint) + SwiftShader, the spec-faithful headless WebGPU backend.
6362
source .ci/scripts/setup-webgpu-linux-deps.sh
6463

65-
EXTRA_BUILD_ARGS+=" -DEXECUTORCH_BUILD_WEBGPU=ON"
64+
EXTRA_BUILD_ARGS+=" -DEXECUTORCH_BUILD_WEBGPU=ON -DDawn_DIR=$Dawn_DIR"
6665
fi
6766

6867
if [[ "$FLOW" == *arm* ]]; then
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
name: Test WebGPU Native (Dawn)
2+
3+
# The substantive WebGPU op-coverage gate. The shared operators suite only
4+
# delegates add.Tensor to WebGPU (everything else is CPU fallback), so the real
5+
# Dawn coverage comes from the native test executables (rms_norm, multi-dispatch
6+
# ordering, scratch). This runs them on Dawn (Tint) + SwiftShader, headless, on a
7+
# CPU runner -- separate from _test_backend.yml so that reusable template stays
8+
# untouched.
9+
10+
# Nightly-only for now: this job builds SwiftShader from source (vendored LLVM),
11+
# which is too expensive to run on every PR while the workflow's reliability is
12+
# still being established. Once it has proven stable, re-enable a scoped PR
13+
# trigger with a paths: filter (backends/webgpu/**, the webgpu CI scripts, and
14+
# this file). The pull_request-aware ref/concurrency expressions below are kept
15+
# intentionally so that re-enable is a one-line change.
16+
on:
17+
schedule:
18+
- cron: 0 2 * * *
19+
push:
20+
branches:
21+
- main
22+
- release/*
23+
tags:
24+
- ciflow/nightly/*
25+
workflow_dispatch:
26+
27+
concurrency:
28+
group: ${{ github.workflow }}--${{ github.event.pull_request.number || github.sha }}-${{ github.event_name == 'workflow_dispatch' }}
29+
cancel-in-progress: true
30+
31+
jobs:
32+
test-webgpu-native:
33+
uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main
34+
with:
35+
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
36+
runner: linux.4xlarge.memory
37+
docker-image: ci-image:executorch-ubuntu-22.04-clang12
38+
submodules: recursive
39+
timeout: 120
40+
script: |
41+
set -eux
42+
43+
# The generic Linux job uses the base conda env, not the image's; activate
44+
# the image env (it has the pinned from-source torch). Mirrors
45+
# test-vulkan-operators-linux in pull.yml.
46+
CONDA_ENV=$(conda env list --json | jq -r ".envs | .[-1]")
47+
conda activate "${CONDA_ENV}"
48+
49+
# Install the python package + runtime deps (the .pte exporters).
50+
PYTHON_EXECUTABLE=python bash .ci/scripts/setup-linux.sh --build-tool cmake
51+
52+
# Vendor Dawn (Tint) + SwiftShader, then build + run the native executables.
53+
source .ci/scripts/setup-webgpu-linux-deps.sh
54+
bash backends/webgpu/scripts/test_webgpu_native_ci.sh

backends/arm/runtime/VGFSetup.cpp

Lines changed: 91 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,38 @@ static bool is_tensor_like_descriptor_type(VkDescriptorType descriptor_type) {
486486
descriptor_type == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
487487
}
488488

489+
static VkResult submit_and_wait_with_fence(
490+
VkDevice device,
491+
VkQueue queue,
492+
const VkSubmitInfo* submit_info) {
493+
VkFence fence = VK_NULL_HANDLE;
494+
495+
const VkFenceCreateInfo fence_info = {
496+
.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
497+
.pNext = nullptr,
498+
.flags = 0,
499+
};
500+
501+
VkResult result = vkCreateFence(device, &fence_info, nullptr, &fence);
502+
if (result != VK_SUCCESS) {
503+
ET_LOG(Error, "Failed to create Vulkan fence, error %d", result);
504+
return result;
505+
}
506+
507+
result = vkQueueSubmit(queue, 1, submit_info, fence);
508+
if (result != VK_SUCCESS) {
509+
ET_LOG(Error, "Vulkan queue submit failed, error %d", result);
510+
vkDestroyFence(device, fence, nullptr);
511+
return result;
512+
}
513+
514+
result = vkWaitForFences(
515+
device, 1, &fence, VK_TRUE, std::numeric_limits<uint64_t>::max());
516+
517+
vkDestroyFence(device, fence, nullptr);
518+
return result;
519+
}
520+
489521
static void record_image_layout_transition(
490522
VkCommandBuffer command_buffer,
491523
VkImage image,
@@ -1278,10 +1310,11 @@ VkResult transition_image_layout(
12781310
.signalSemaphoreCount = 0,
12791311
.pSignalSemaphores = nullptr,
12801312
};
1281-
result = vkQueueSubmit(queue, 1, &submit_info, VK_NULL_HANDLE);
1282-
if (result == VK_SUCCESS) {
1283-
result = vkQueueWaitIdle(queue);
1284-
}
1313+
1314+
// creates a temporary one-time command buffer, submits it once, waits, and
1315+
// frees it immediately.
1316+
result = submit_and_wait_with_fence(device, queue, &submit_info);
1317+
12851318
vkFreeCommandBuffers(device, command_pool, 1, &command_buffer);
12861319
return result;
12871320
}
@@ -3078,19 +3111,33 @@ bool VgfRepr::process_vgf(
30783111
{
30793112
VGF_PROFILE_SCOPE(event_tracer, "VGF_INIT_ALLOCATE_COMMAND_BUFFER");
30803113

3081-
// Allocate command buffer
30823114
VkCommandBufferAllocateInfo buffer_allocate_info{
30833115
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
30843116
.pNext = nullptr,
30853117
.commandPool = vk_command_pool,
30863118
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
30873119
.commandBufferCount = 1};
3120+
30883121
result = vkAllocateCommandBuffers(
30893122
vk_device, &buffer_allocate_info, &vk_execute_cmd);
30903123
if (result != VK_SUCCESS) {
30913124
ET_LOG(Error, "Failed to allocate command buffers");
30923125
return false;
30933126
}
3127+
3128+
const VkFenceCreateInfo fence_info{
3129+
.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
3130+
.pNext = nullptr,
3131+
.flags = VK_FENCE_CREATE_SIGNALED_BIT,
3132+
};
3133+
3134+
result = vkCreateFence(vk_device, &fence_info, nullptr, &vk_execute_fence);
3135+
if (result != VK_SUCCESS) {
3136+
ET_LOG(Error, "Failed to create VGF execute fence, error %d", result);
3137+
vkFreeCommandBuffers(vk_device, vk_command_pool, 1, &vk_execute_cmd);
3138+
vk_execute_cmd = VK_NULL_HANDLE;
3139+
return false;
3140+
}
30943141
}
30953142

30963143
{
@@ -3392,31 +3439,51 @@ bool VgfRepr::process_vgf(
33923439
bool VgfRepr::execute_vgf(executorch::runtime::EventTracer* event_tracer) {
33933440
ET_LOG(Info, "Executing vgf");
33943441

3395-
VkSubmitInfo submit{VK_STRUCTURE_TYPE_SUBMIT_INFO};
3396-
submit.commandBufferCount = 1;
3397-
submit.pCommandBuffers = &vk_execute_cmd;
3442+
VkSubmitInfo submit{
3443+
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
3444+
.pNext = nullptr,
3445+
.waitSemaphoreCount = 0,
3446+
.pWaitSemaphores = nullptr,
3447+
.pWaitDstStageMask = nullptr,
3448+
.commandBufferCount = 1,
3449+
.pCommandBuffers = &vk_execute_cmd,
3450+
.signalSemaphoreCount = 0,
3451+
.pSignalSemaphores = nullptr,
3452+
};
33983453

33993454
VkResult result;
34003455

34013456
{
3402-
VGF_PROFILE_SCOPE(event_tracer, "VGF_QUEUE_SUBMIT");
3457+
VGF_PROFILE_SCOPE(event_tracer, "VGF_QUEUE_SUBMIT_AND_WAIT_FENCE");
34033458

3404-
result = vkQueueSubmit(vk_queue, 1, &submit, VK_NULL_HANDLE);
3405-
}
3459+
if (vk_execute_fence == VK_NULL_HANDLE) {
3460+
ET_LOG(Error, "VGF execute fence is not initialized");
3461+
return false;
3462+
}
34063463

3407-
if (result != VK_SUCCESS) {
3408-
ET_LOG(Error, "VGF/VkCommandBuffer command submission failed");
3409-
return false;
3410-
}
3464+
result = vkResetFences(vk_device, 1, &vk_execute_fence);
3465+
if (result != VK_SUCCESS) {
3466+
ET_LOG(Error, "VGF/VkFence reset failed, error %d", result);
3467+
return false;
3468+
}
34113469

3412-
{
3413-
VGF_PROFILE_SCOPE(event_tracer, "VGF_QUEUE_WAIT_IDLE");
3470+
result = vkQueueSubmit(vk_queue, 1, &submit, vk_execute_fence);
3471+
if (result != VK_SUCCESS) {
3472+
ET_LOG(Error, "VGF/VkFence wait failed, error %d", result);
3473+
return false;
3474+
}
34143475

3415-
result = vkQueueWaitIdle(vk_queue);
3476+
result = vkWaitForFences(
3477+
vk_device,
3478+
1,
3479+
&vk_execute_fence,
3480+
VK_TRUE,
3481+
std::numeric_limits<uint64_t>::max());
34163482
}
34173483

34183484
if (result != VK_SUCCESS) {
3419-
ET_LOG(Error, "VGF/VkQueue wait idle failed");
3485+
ET_LOG(
3486+
Error, "VGF/VkCommandBuffer command submission or fence wait failed");
34203487
return false;
34213488
}
34223489

@@ -3431,6 +3498,11 @@ void VgfRepr::free_vgf() {
34313498
vk_timestamp_query_pool = VK_NULL_HANDLE;
34323499
}
34333500

3501+
if (vk_execute_fence != VK_NULL_HANDLE) {
3502+
vkDestroyFence(vk_device, vk_execute_fence, nullptr);
3503+
vk_execute_fence = VK_NULL_HANDLE;
3504+
}
3505+
34343506
vkFreeCommandBuffers(vk_device, vk_command_pool, 1, &vk_execute_cmd);
34353507
vector<VkDeviceMemory> owned_memory;
34363508
auto remember_owned_memory = [&](VkDeviceMemory memory) {

backends/arm/runtime/VGFSetup.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ class VgfRepr {
163163
// per-VgfRepr-instance objects allocated in process_vgf, used (can be more
164164
// than once) in execute_vgf
165165
VkCommandBuffer vk_execute_cmd = VK_NULL_HANDLE;
166+
VkFence vk_execute_fence = VK_NULL_HANDLE;
166167
// Note: the vector of tensor memory is stored in IOs above
167168

168169
bool init_timestamp_queries();

backends/cuda/tests/test_int4_dispatch.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,10 @@ def _make_int4_linear(N, K, group_size=128, symmetric=False, bias=False):
5959
)
6060
int4_w = quantize_weight(w_bf16, config)
6161

62-
module = nn.Linear(K, N, bias=bias, dtype=torch.bfloat16)
62+
# device="cuda" so the random init draws from the CUDA RNG to match the
63+
# same random weight as regular int4 dispatch and fit the same numerical
64+
# error tolerance.
65+
module = nn.Linear(K, N, bias=bias, dtype=torch.bfloat16, device="cuda")
6366
pack_linear_for_cuda(module, {"weight": int4_w})
6467
module.cuda()
6568
return module, w_bf16.cuda()

backends/qualcomm/_passes/decompose_cdist.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,19 @@ class DecomposeCDist(ExportPass):
3636
Decompose for math equivalent op.
3737
"""
3838

39+
cdist_targets = {
40+
torch.ops.aten.cdist.default,
41+
torch.ops.aten._cdist_forward.default,
42+
}
43+
3944
def __init__(self) -> None:
4045
super().__init__()
4146

4247
def call(self, graph_module: torch.fx.GraphModule) -> PassResult:
4348
graph = graph_module.graph
4449
for node in graph.nodes:
4550
model = CDist()
46-
if torch.ops.aten.cdist.default == node.target:
51+
if node.target in self.cdist_targets:
4752
if len(node.args) > 2:
4853
assert (
4954
node.args[2] == 2

0 commit comments

Comments
 (0)