Contributing to DPC++

General guidelines

Read CONTRIBUTING.md first.

Maintaining stable ABI/API

All changes made to the DPC++ compiler and runtime library should generally preserve existing ABI/API and contributors should avoid making incompatible changes. One of the exceptions is experimental APIs, clearly marked so by namespace or related specification. If you wish to propose a new experimental DPC++ extension then read README-process.md.

Another exceptional case is the transition from SYCL 1.2.1 to SYCL 2020 standard.

Deprecation of older APIs is happening in the following order:

  • Newer API implemented and covered with tests

  • Deprecation warning is added to older API

  • Wait some time allowing users to migrate their codebases (exact period depends on the change made)

  • Old API is removed in headers

  • Old API is removed in library

See ABI Policy Guide for more information.

Project build and local testing

See Get Started Guide instructions

Commit message

For any DPC++-related commit, the [SYCL] tag should be present in the commit message title. To a reasonable extent, additional tags can be used to signify the component changed, e.g.: [UR], [CUDA], [Doc].

Using <iostream>

According to LLVM Coding Standards, the use #include <iostream> is forbidden in library files. Instead, the sycl/detail/iostream_proxy.hpp header offers the functionality of without its static constructor. This header should be used in place of in DPC++ headers and runtime library files.

Tests development

Every product change should be accompanied with corresponding test modification (adding new test(s), extending, removing or modifying existing test(s)).

There are 3 types of tests which are used for DPC++ toolchain validation:

  • DPC++ device-independent tests

  • DPC++ end-to-end (E2E) tests

  • SYCL Conformance Test Suite (CTS)

DPC++ device-independent tests

DPC++ device-independent tests are hosted in this repository. They can be run by check-llvm, check-clang, check-llvm-spirv and check-sycl targets. These tests are expected not to have hardware (e.g. GPU, FPGA, etc.) or external software (e.g. OpenCL, Level Zero, CUDA runtimes) dependencies. All other tests should land at DPC++ end-to-end or SYCL CTS tests.

Generally, any functional change to any of the DPC++ toolchain components should be accompanied by one or more tests of this type when possible. They allow verifying individual components and tend to be more lightweight than end-to-end or SYCL-CTS tests.

General guidelines

  • Use sycl:: namespace instead of cl::sycl::

  • Add a helpful comment describing what the test does at the beginning and other comments throughout the test as necessary.

  • All identifiers used in llvm/sycl headers files must contain at least one lowercase letter due to avoid conflicts with user-defined macros.

  • Try to follow descriptive naming convention for variables, functions as much as possible. Please refer to LLVM naming convention

DPC++ clang FE tests

  • Include sycl mock headers as system headers. Example: -internal-isystem %S/Inputs

    `#include "sycl.hpp"`
    
  • Use SYCL functions for invoking kernels from the mock header (single_task, parallel_for, parallel_for_work_group) Example:

    `#include "Inputs/sycl.hpp"`
    sycl::queue q;
    q.submit([&](sycl::handler &h) {
    h.single_task( { //code });
    });
    

DPC++ headers and runtime tests

  • check-sycl target contains 2 types of tests: LIT tests and unit tests. LIT tests make compile-time checks of DPC++ headers, e.g. device code IR verification, static_assert tests. Unit tests check DPC++ runtime behavior and do not perform any device code compilation, instead relying on redefining the UR API with UrMock and the Unified Runtime mocking interface when necessary.

When adding new test to check-sycl, please consider the following:

  • if you only need to check that compilation succeeds, please use -fsyntax-only compiler flag for such tests: it instructs the compiler to launch reduced set of commands and produce no output (no need to add -o %t.out).

  • if you are only interested in checking device or host compilation, please use corresponding flags to reduce the scope of test and therefore speed it up. To launch only device compilation, use -fsycl-device-only compiler flag; to launch only host compilation, use %fsycl-host-only substitution.

  • tests which want to check generated device code (either in LLVM IR or SPIR-V form) should be placed under check_device_code folder.

  • if compiler invocation in your LIT test produces an output file, please make sure to redirect it into a temporary file using -o option and %t substitution. This is needed to avoid possible race conditions when two LIT tests attempt to write into the same file.

  • if you need to check some runtime behavior please add unit test instead of LIT test. Unit tests are built with regular C++ compiler which is used to build the project and therefore they are not affected by clang++ being slow when the project is built in Debug mode. As another side effect of using standard C++ compiler, device side compilation is skipped entirely, making them quicker to compile. And finally, unit tests are written with googletest framework, which allows to use plenty of useful assertions and other helpers.

DPC++ end-to-end (E2E) tests

These tests are located in /sycl/test-e2e directory and are not configured to be run by default. See End-to-End tests documentation for instructions on how to run them.

A test which requires full stack including backend runtimes (e.g. OpenCL, Level Zero or CUDA) should be added to DPC++ E2E tests.

SYCL Conformance Test Suite (CTS)

These tests are hosted at Khronos SYCL conformance tests. These tests verify SYCL specification conformance. All implementation details are out of scope for the tests. See DPC++ compiler invocation definitions at FindIntel_SYCL)

Unified Runtime Updates

To integrate changes from the Unified Runtime project into DPC++ there two main options which depend on the scope of those changes and the current state of DPC++.

  1. Synchronized update:

  • When: If the Unified Runtime change touches the API/ABI, more than one adapter, or common code such as the loader.

  • How: Update the UNIFIED_RUNTIME_TAG to point at the desired commit or tag name in the Unified Runtime repository and ensure that any tag for specific adapters are set to use ${UNIFIED_RUNTIME_TAG}.

  1. Decoupled update:

  • When: If only a single Unified Runtime adatper has changed.

  • How: Update the tag used in the fetch_adapter_source() call for a specific Unified Runtime adapter, e.g. Level Zero, OpenCL, CUDA, HIP, or Native CPU.

In general, a synchronized update should be the default. However, when there are a lot of changes in flight in parallel always synchronizing the tag can be troublesome. This is when a decoupled update can help sustain the merge velocity of Unified Runtime changes.

The intel/unified-runtime-reviewers team is responsible for ensuring that the Unified Runtime tag is updated correctly and will only provide code owner approval to pull requests once the following criteria are met:

  • Tags are pointing to a valid commit or tag on Unified Runtime main branch.

  • Changes to additional code owned files are in a good state.

  • GitHub Actions checks are passing.