|
| 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