Skip to content

Commit 4f8447c

Browse files
committed
fix logic for OCL loader with conda-forge runtime
1 parent 1f34b63 commit 4f8447c

2 files changed

Lines changed: 446 additions & 74 deletions

File tree

Lines changed: 398 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,398 @@
1+
name: Build with Open Source LLVM SYCL compiler built from source (native_cpu)
2+
3+
# Variant of `os-llvm-sycl-build.yml`. Instead of downloading a pre-built
4+
# nightly bundle, this workflow builds the DPC++ toolchain from intel/llvm
5+
# sources with the experimental `native_cpu` backend enabled, then builds dpctl
6+
# against it and runs the test suite on the `native_cpu` device.
7+
#
8+
# `native_cpu` is NOT usable from any pre-built toolchain: neither the
9+
# productized oneAPI compiler nor the intel/llvm nightly tarballs ship
10+
# `libur_adapter_native_cpu.so` or the host-triple libclc/libspirv bitcode that
11+
# `-fsycl-targets=native_cpu` requires. Both artifacts only exist when the
12+
# toolchain is configured with `buildbot/configure.py --native_cpu`, which is
13+
# why this workflow has to build the compiler itself.
14+
#
15+
# See https://github.com/intel/llvm/blob/sycl/sycl/doc/design/SYCLNativeCPU.md
16+
17+
on:
18+
# Deliberately NOT attached to `pull_request`: a cold toolchain build takes
19+
# hours, which is not an acceptable per-PR cost. The toolchain is cached by
20+
# intel/llvm commit SHA, so re-runs against an already-built SHA are cheap.
21+
workflow_dispatch:
22+
inputs:
23+
llvm_ref:
24+
description: "intel/llvm ref (branch, tag or commit SHA) to build"
25+
type: string
26+
default: "sycl"
27+
with_onetbb:
28+
description: "Build the native_cpu backend against oneTBB (fetched by CMake)"
29+
type: boolean
30+
default: false
31+
schedule:
32+
# Track upstream intel/llvm weekly, Sundays at 03:00 UTC
33+
- cron: "0 3 * * 0"
34+
push:
35+
branches: [experimental/support-native-cpu-device]
36+
37+
permissions: read-all
38+
39+
env:
40+
LLVM_REF: ${{ inputs.llvm_ref || 'sycl' }}
41+
NATIVECPU_WITH_ONETBB: ${{ inputs.with_onetbb && 'On' || 'Off' }}
42+
TOOLCHAIN_ARCHIVE: llvm_sycl_native_cpu.tar.zst
43+
TOOLCHAIN_ARTIFACT: sycl-native-cpu-toolchain
44+
# Bump to force a rebuild of the toolchain and to invalidate ccache.
45+
CACHE_EPOCH: v1
46+
47+
jobs:
48+
build-toolchain:
49+
name: Build DPC++ toolchain with native_cpu enabled
50+
runs-on: ubuntu-24.04
51+
# A cold build of the toolchain dominates this job. 6h is the ceiling for
52+
# GitHub-hosted runners; a ccache-warm rebuild is far quicker.
53+
timeout-minutes: 360
54+
55+
env:
56+
SRC_DIR: /home/runner/work/llvm-src
57+
BUILD_DIR: /home/runner/work/llvm-build
58+
TOOLCHAIN_DIR: /home/runner/work/toolchain
59+
ARCHIVE_DIR: /home/runner/work/toolchain-archive
60+
CCACHE_DIR: /home/runner/work/ccache
61+
# Kept modest on purpose: the toolchain cache and this one share the
62+
# repository's 10 GB Actions cache quota.
63+
CCACHE_MAXSIZE: 4G
64+
65+
outputs:
66+
llvm_sha: ${{ steps.resolve.outputs.sha }}
67+
68+
steps:
69+
- name: Cancel Previous Runs
70+
uses: styfle/cancel-workflow-action@d07a454dad7609a92316b57b23c9ccfd4f59af66 # 0.13.1
71+
with:
72+
access_token: ${{ github.token }}
73+
74+
- name: Resolve intel/llvm ref to a commit SHA
75+
id: resolve
76+
shell: bash -l {0}
77+
run: |
78+
URL=https://github.com/intel/llvm.git
79+
# Match fully qualified refs so that e.g. `sycl` cannot resolve to
80+
# some unrelated ref that merely ends in "sycl".
81+
SHA=$(git ls-remote "${URL}" "refs/heads/${LLVM_REF}" | awk 'NR==1{print $1}')
82+
if [[ -z "${SHA}" ]]; then
83+
# Annotated tag: take the commit it dereferences to
84+
SHA=$(git ls-remote "${URL}" "refs/tags/${LLVM_REF}^{}" | awk 'NR==1{print $1}')
85+
fi
86+
if [[ -z "${SHA}" ]]; then
87+
# Lightweight tag
88+
SHA=$(git ls-remote "${URL}" "refs/tags/${LLVM_REF}" | awk 'NR==1{print $1}')
89+
fi
90+
if [[ -z "${SHA}" ]]; then
91+
# Neither branch nor tag, assume LLVM_REF already is a commit SHA
92+
SHA="${LLVM_REF}"
93+
fi
94+
if [[ ! "${SHA}" =~ ^[0-9a-f]{40}$ ]]; then
95+
echo "::error::could not resolve intel/llvm ref '${LLVM_REF}' to a commit SHA"
96+
exit 1
97+
fi
98+
echo "Building intel/llvm ${LLVM_REF} at ${SHA}"
99+
echo "sha=${SHA}" >> "${GITHUB_OUTPUT}"
100+
101+
- name: Cache built toolchain
102+
id: toolchain-cache
103+
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
104+
with:
105+
path: /home/runner/work/toolchain-archive
106+
key: ${{ runner.os }}-native-cpu-toolchain-${{ env.CACHE_EPOCH }}-tbb${{ env.NATIVECPU_WITH_ONETBB }}-${{ steps.resolve.outputs.sha }}
107+
108+
# Everything below is skipped when the toolchain for this SHA is cached.
109+
110+
- name: Free up disk space
111+
if: steps.toolchain-cache.outputs.cache-hit != 'true'
112+
shell: bash -l {0}
113+
run: |
114+
# A Release build tree of the toolchain needs well over 20 GB, more
115+
# than a stock runner has free.
116+
df -h /
117+
sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc \
118+
/usr/local/share/boost /usr/local/share/powershell \
119+
/usr/lib/jvm "${AGENT_TOOLSDIRECTORY}" || true
120+
sudo docker image prune --all --force || true
121+
df -h /
122+
123+
- name: Install toolchain build prerequisites
124+
if: steps.toolchain-cache.outputs.cache-hit != 'true'
125+
shell: bash -l {0}
126+
run: |
127+
sudo apt-get update
128+
sudo apt-get install -y --no-install-recommends \
129+
build-essential ninja-build cmake ccache zstd \
130+
hwloc libhwloc-dev libzstd-dev
131+
132+
- name: Cache ccache directory
133+
if: steps.toolchain-cache.outputs.cache-hit != 'true'
134+
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
135+
with:
136+
path: /home/runner/work/ccache
137+
key: ${{ runner.os }}-native-cpu-ccache-${{ env.CACHE_EPOCH }}-${{ steps.resolve.outputs.sha }}
138+
restore-keys: |
139+
${{ runner.os }}-native-cpu-ccache-${{ env.CACHE_EPOCH }}-
140+
${{ runner.os }}-native-cpu-ccache-
141+
142+
- name: Checkout intel/llvm
143+
if: steps.toolchain-cache.outputs.cache-hit != 'true'
144+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
145+
with:
146+
repository: intel/llvm
147+
ref: ${{ steps.resolve.outputs.sha }}
148+
path: llvm-src-checkout
149+
fetch-depth: 1
150+
151+
- name: Stage intel/llvm sources outside the workspace
152+
if: steps.toolchain-cache.outputs.cache-hit != 'true'
153+
shell: bash -l {0}
154+
run: |
155+
rm -rf "${SRC_DIR}"
156+
mv "${GITHUB_WORKSPACE}/llvm-src-checkout" "${SRC_DIR}"
157+
158+
- name: Configure toolchain
159+
if: steps.toolchain-cache.outputs.cache-hit != 'true'
160+
shell: bash -l {0}
161+
env:
162+
CC: gcc
163+
CXX: g++
164+
run: |
165+
mkdir -p "${CCACHE_DIR}" "${BUILD_DIR}"
166+
# `--native_cpu` is what adds the native_cpu UR adapter to
167+
# SYCL_ENABLE_BACKENDS and appends `native_cpu` to
168+
# LLVM_RUNTIME_TARGETS so libclc is built for the host triple. Both
169+
# are mandatory: without libclc, -fsycl-targets=native_cpu fails to
170+
# find libspirv and the link dies on __spirv_BuiltIn* references.
171+
#
172+
# `--ci-defaults` is intentionally not used: it pulls in NVPTX,
173+
# clang-tools-extra and compiler-rt, none of which are needed here.
174+
#
175+
# SYCL_ENABLE_BACKENDS is overridden to drop the Level Zero adapters;
176+
# GitHub-hosted runners have no GPU, and this saves fetching and
177+
# building the Level Zero loader. Passthrough `-D` options are
178+
# appended after configure.py's own, so this override wins.
179+
python3 "${SRC_DIR}/buildbot/configure.py" \
180+
-s "${SRC_DIR}" \
181+
-o "${BUILD_DIR}" \
182+
-t Release \
183+
--native_cpu \
184+
--no-assertions \
185+
--cmake-opt="-DNATIVECPU_WITH_ONETBB=${NATIVECPU_WITH_ONETBB}" \
186+
-DCMAKE_INSTALL_PREFIX="${TOOLCHAIN_DIR}" \
187+
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
188+
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
189+
-DSYCL_ENABLE_BACKENDS='native_cpu;opencl' \
190+
-DSYCL_INCLUDE_TESTS=OFF
191+
192+
- name: Build sycl-toolchain
193+
if: steps.toolchain-cache.outputs.cache-hit != 'true'
194+
shell: bash -l {0}
195+
run: |
196+
ccache --zero-stats
197+
cmake --build "${BUILD_DIR}" --target sycl-toolchain
198+
ccache --show-stats
199+
200+
- name: Install toolchain into CMAKE_INSTALL_PREFIX
201+
if: steps.toolchain-cache.outputs.cache-hit != 'true'
202+
shell: bash -l {0}
203+
run: |
204+
# `deploy-sycl-toolchain` is the target that populates
205+
# CMAKE_INSTALL_PREFIX with a slim, relocatable toolchain.
206+
cmake --build "${BUILD_DIR}" --target deploy-sycl-toolchain
207+
208+
- name: Verify the installed toolchain can build and run for native_cpu
209+
if: steps.toolchain-cache.outputs.cache-hit != 'true'
210+
shell: bash -l {0}
211+
run: |
212+
echo "--- native_cpu artifacts in the install tree ---"
213+
find "${TOOLCHAIN_DIR}" \( -name '*native_cpu*' -o -name 'libspirv*.bc' \) | sort
214+
215+
if ! ls "${TOOLCHAIN_DIR}"/lib/libur_adapter_native_cpu.so* >/dev/null 2>&1; then
216+
echo "::error::libur_adapter_native_cpu.so missing from the install tree"
217+
exit 1
218+
fi
219+
220+
# An end-to-end smoke test is a stronger check than looking for
221+
# libclc bitcode by path: it proves the compile side (libspirv is
222+
# found, __spirv_BuiltIn* resolve) and the runtime side (the adapter
223+
# loads and enqueues a kernel) both work.
224+
cat << 'EOF' > /tmp/native_cpu_smoke.cpp
225+
#include <sycl/sycl.hpp>
226+
#include <cstdio>
227+
int main() {
228+
sycl::queue q;
229+
auto dev = q.get_device();
230+
printf("device: %s\n", dev.get_info<sycl::info::device::name>().c_str());
231+
if (dev.get_backend() != sycl::backend::ext_oneapi_native_cpu) {
232+
printf("unexpected backend, not native_cpu\n");
233+
return 1;
234+
}
235+
constexpr size_t n = 8;
236+
int *d = sycl::malloc_shared<int>(n, q);
237+
q.parallel_for(n, [=](sycl::id<1> i) { d[i] = int(i[0]) * 2; }).wait();
238+
for (size_t i = 0; i < n; ++i)
239+
if (d[i] != int(i) * 2) { printf("mismatch at %zu\n", i); return 1; }
240+
sycl::free(d, q);
241+
printf("native_cpu smoke test OK\n");
242+
return 0;
243+
}
244+
EOF
245+
246+
export PATH="${TOOLCHAIN_DIR}/bin:${PATH}"
247+
export LD_LIBRARY_PATH="${TOOLCHAIN_DIR}/lib:${LD_LIBRARY_PATH}"
248+
clang++ -fsycl -fsycl-targets=native_cpu \
249+
/tmp/native_cpu_smoke.cpp -o /tmp/native_cpu_smoke
250+
ONEAPI_DEVICE_SELECTOR=native_cpu:cpu /tmp/native_cpu_smoke
251+
252+
- name: Archive toolchain
253+
if: steps.toolchain-cache.outputs.cache-hit != 'true'
254+
shell: bash -l {0}
255+
run: |
256+
mkdir -p "${ARCHIVE_DIR}"
257+
tar -I 'zstd -9 -T0' -cf "${ARCHIVE_DIR}/${TOOLCHAIN_ARCHIVE}" \
258+
-C "${TOOLCHAIN_DIR}" .
259+
ls -lh "${ARCHIVE_DIR}/${TOOLCHAIN_ARCHIVE}"
260+
261+
- name: Free the build tree before uploading
262+
if: steps.toolchain-cache.outputs.cache-hit != 'true'
263+
shell: bash -l {0}
264+
run: |
265+
rm -rf "${BUILD_DIR}" "${SRC_DIR}"
266+
df -h /
267+
268+
- name: Upload toolchain artifact
269+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
270+
with:
271+
name: ${{ env.TOOLCHAIN_ARTIFACT }}
272+
path: /home/runner/work/toolchain-archive/${{ env.TOOLCHAIN_ARCHIVE }}
273+
retention-days: 7
274+
# Already zstd-compressed.
275+
compression-level: 0
276+
277+
build-and-test-dpctl:
278+
name: Build dpctl and run tests on native_cpu
279+
needs: build-toolchain
280+
runs-on: ubuntu-24.04
281+
timeout-minutes: 90
282+
283+
env:
284+
SYCL_TOOLCHAIN: /home/runner/work/toolchain
285+
286+
steps:
287+
- name: Install runtime prerequisites
288+
shell: bash -l {0}
289+
run: |
290+
sudo apt-get update
291+
sudo apt-get install -y --no-install-recommends hwloc libhwloc-dev zstd lld
292+
293+
- name: Download toolchain artifact
294+
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
295+
with:
296+
name: ${{ env.TOOLCHAIN_ARTIFACT }}
297+
path: /home/runner/work/toolchain-archive
298+
299+
- name: Unpack toolchain
300+
shell: bash -l {0}
301+
run: |
302+
mkdir -p "${SYCL_TOOLCHAIN}"
303+
tar -I zstd -xf "/home/runner/work/toolchain-archive/${TOOLCHAIN_ARCHIVE}" \
304+
-C "${SYCL_TOOLCHAIN}"
305+
306+
- name: Setup Python
307+
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
308+
with:
309+
python-version: '3.12'
310+
architecture: x64
311+
312+
- name: Install dpctl dependencies
313+
shell: bash -l {0}
314+
run: |
315+
pip install numpy cython setuptools"<80" pytest scikit-build cmake ninja versioneer[toml]==0.29
316+
317+
- name: Checkout repo
318+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
319+
with:
320+
fetch-depth: 0
321+
322+
- name: Create set_allvars.sh
323+
shell: bash -l {0}
324+
run: |
325+
cat << 'EOF' > set_allvars.sh
326+
#!/usr/bin/bash
327+
export SYCL_TOOLCHAIN=/home/runner/work/toolchain
328+
export PATH=${SYCL_TOOLCHAIN}/bin:${PATH}
329+
export LD_LIBRARY_PATH=${SYCL_TOOLCHAIN}/lib:${LD_LIBRARY_PATH}
330+
# No OpenCL CPU runtime is installed, so native_cpu is the only
331+
# backend with devices. Pin the selector anyway to keep the intent
332+
# explicit and to fail loudly if that ever stops being true.
333+
export ONEAPI_DEVICE_SELECTOR=native_cpu:cpu
334+
EOF
335+
chmod +x set_allvars.sh
336+
cat set_allvars.sh
337+
338+
- name: Report compiler version
339+
shell: bash -l {0}
340+
run: |
341+
source set_allvars.sh
342+
clang++ --version
343+
344+
- name: Run sycl-ls
345+
shell: bash -l {0}
346+
run: |
347+
source set_allvars.sh
348+
sycl-ls
349+
if ! sycl-ls | grep -q 'native_cpu'; then
350+
echo "::error::no native_cpu device exposed by the built toolchain"
351+
exit 1
352+
fi
353+
354+
- name: build dpctl
355+
shell: bash -l {0}
356+
run: |
357+
source set_allvars.sh
358+
# --no-level-zero matches the toolchain, which was built without the
359+
# Level Zero adapters.
360+
python scripts/build_locally.py --c-compiler=clang --cxx-compiler=clang++ \
361+
--compiler-root="${SYCL_TOOLCHAIN}/bin" \
362+
--no-level-zero || exit 1
363+
364+
- name: Run lsplatforms
365+
shell: bash -l {0}
366+
run: |
367+
source set_allvars.sh
368+
python -m dpctl -f || exit 1
369+
370+
- name: Run dpctl tests covering backend enumeration
371+
shell: bash -l {0}
372+
env:
373+
SYCL_CACHE_PERSISTENT: 1
374+
run: |
375+
source set_allvars.sh
376+
# These exercise the dpctl-side native_cpu plumbing (backend enum,
377+
# platform/device/queue/context discovery) and are expected to pass.
378+
python -m pytest -sv \
379+
dpctl/tests/test_sycl_platform.py \
380+
dpctl/tests/test_sycl_device.py \
381+
dpctl/tests/test_sycl_device_factory.py \
382+
dpctl/tests/test_sycl_context.py \
383+
dpctl/tests/test_sycl_queue.py \
384+
dpctl/tests/test_sycl_queue_manager.py \
385+
dpctl/tests/test_sycl_event.py \
386+
dpctl/tests/test_sycl_usm.py || exit 1
387+
388+
- name: Run full dpctl test suite (informational)
389+
# native_cpu is experimental upstream: images, device globals, bfloat16
390+
# and ESIMD are unsupported, and the SPIR-V based kernel tests have no
391+
# native_cpu equivalent. Failures here are reported but do not gate.
392+
continue-on-error: true
393+
shell: bash -l {0}
394+
env:
395+
SYCL_CACHE_PERSISTENT: 1
396+
run: |
397+
source set_allvars.sh
398+
python -m pytest -rs -v dpctl/tests

0 commit comments

Comments
 (0)