You may follow the steps below or use docker files (available for Linux only) as a reference on how to build LLVM and ISPC.
Most of development day-to-day tasks can be performed using pre-built Clang/LLVM that can be downloaded from
ispc.dependencies. Download and unpack the archive. Make sure that unpacked clang works that is usually fine with Windows but it can be tricky with Linux.
Additionally, on Windows you need:
Additionally, on Linux you need:
These section explains how to build ISPC using prebuilt Clang/LLVM.
Ubuntu 22.04/24.04:
apt-get install git curl cmake xz-utils m4 flex bison python3 python3-dev libtbb-dev g++-multilib
Arch Linux:
pacman -S git cmake make gcc-libs glibc lib32-glibc gcc m4 flex bison python onetbb
choco install winflexbison3 choco install gnuwin32-m4
Build ISPC with the following commands:
git clone --recursive https://github.com/ispc/ispc ./ispc/scripts/quick-start-build.py
Build ISPC with the following commands:
git clone --recursive https://github.com/ispc/ispc python ispc\scripts\quick-start-build.py
On Windows, the quick-start-build.py script requires py7zr to unpack downloaded archives.
pip install py7zr
Note: Incremental builds can be performed by simply re-running the quick-start-build.py script.
There is a script named quick-start-build.py that automatically downloads Clang/LLVM and builds ISPC.
You can provide the required LLVM version as a positional argument. By default, this script downloads the current version LLVM used for development.
./ispc/scripts/quick-start-build.py 18
You can also specify the directory where LLVM/Clang will be unpacked usign the LLVM_HOME environment variable.
bash:
LLVM_HOME=/path/to/llvm python quick-start-build.py
cmd (Windows):
set LLVM_HOME=C:\path\to\llvm && python quick-start-build.py
PowerShell:
$env:LLVM_HOME = "C:\path\to\llvm"; python quick-start-build.py
Additionally, there is an environment variable ISPC_HOME to specify where this script should create the build directory for the ISPC build. Under this directory, this script creates a build-<version_number> directory, where you can find the built ISPC executable, rerun the build, or inspect it. It also safe to rerun the entire quick-start-build.py script to rebuild ISPC after making changes to the codebase.
Supported LLVM & glibc versionsWhen running quick-start-build.py
, the script downloads LLVM built on Ubuntu 22.04, which has glibc 2.35. So for quick-start-build.py
to work correctly, your system must have glibc 2.35 or later. Below are the default glibc versions for different Ubuntu releases:
Here is a sample output from running quick-start-build.py
with a Ubuntu 20.04 as operating system:
It fails with the following output: Change Dir: /home/user/workspace/ispc/build-18/CMakeFiles/CMakeTmp Run Build Command(s):/usr/bin/make cmTC_8dbd9/fast && /usr/bin/make -f CMakeFiles/cmTC_8dbd9.dir/build.make CMakeFiles/cmTC_8dbd9.dir/build make[1]: Entering directory '/home/user/workspace/ispc/build-18/CMakeFiles/CMakeTmp' Building C object CMakeFiles/cmTC_8dbd9.dir/testCCompiler.c.o /home/user/workspace/llvm-18/bin/clang -o CMakeFiles/cmTC_8dbd9.dir/testCCompiler.c.o -c /home/user/workspace/ispc/build-18/CMakeFiles/CMakeTmp/testCCompiler.c /home/user/workspace/llvm-18/bin/clang: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.34' not found (required by /home/user/workspace/llvm-18/bin/clang) /home/user/workspace/llvm-18/bin/clang: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.32' not found (required by /home/user/workspace/llvm-18/bin/clang) /home/user/workspace/llvm-18/bin/clang: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.33' not found (required by /home/user/workspace/llvm-18/bin/clang) /home/user/workspace/llvm-18/bin/clang: /lib/x86_64-linux-gnu/libstdc++.so.6: version `GLIBCXX_3.4.30' not found (required by /home/user/workspace/llvm-18/bin/clang) /home/user/workspace/llvm-18/bin/clang: /lib/x86_64-linux-gnu/libstdc++.so.6: version `GLIBCXX_3.4.29' not found (required by /home/user/workspace/llvm-18/bin/clang) /home/user/workspace/llvm-18/bin/clang: /lib/x86_64-linux-gnu/libstdc++.so.6: version `CXXABI_1.3.13' not found (required by /home/user/workspace/llvm-18/bin/clang) make[1]: *** [CMakeFiles/cmTC_8dbd9.dir/build.make:66: CMakeFiles/cmTC_8dbd9.dir/testCCompiler.c.o] Error 1 make[1]: Leaving directory '/home/user/workspace/ispc/build-18/CMakeFiles/CMakeTmp' make: *** [Makefile:121: cmTC_8dbd9/fast] Error 2
What to do if your Ubuntu system has an older LLVM or glibc version? If your system has an older version that does not meet the minimum requirement, you have two options:
18.1.0
or higher. ISPC can be built with LLVM installed from the system package manager if the version is supported by ISPC. However, some lit-tests may fail with the system LLVM. For example, on Ubuntu 24.04, you can install clang and llvm with the following command:apt install llvm clang libclang-dev
PATH=/usr/lib/llvm-18/bin:$PATH cmake <ispc-source-dir>
For other Linux and BSD OSes, the best way to determine how to build ISPC with the system LLVM is to consult the ISPC package build instructions. The following links point to some of them:
First, build the LLVM headers and libraries and the clang compiler on your system. For successful ISPC build LLVM must be built with proper flags. The recommended way to build LLVM is to use superbuild CMake configuration. It passes all required flags depending on LLVM version and applies required patches.
Note: all examples below are using LLVM 18.1 version but you can use any supported LLVM version. They also suppose that ISPC_HOME environment variable points to the path of ISPC repository and HOME_DIR points to the directory where an user starts to run command listed below.
Building LLVM using superbuildgit clone --recursive https://github.com/ispc/ispc cmake -B build-llvm ispc/superbuild --preset os -DLLVM_VERSION=18.1 -DCMAKE_INSTALL_PREFIX=llvm-18.1 -DBUILD_STAGE2_TOOLCHAIN_ONLY=ON cmake --build build-llvm
You may want to speed up your build with -jN switch, where N is a number of parallel jobs to run. Once cmake build is completed, the llvm-18.1
directory contains clang and LLVM with required libraries and headers ready to be used to build ISPC.
Make sure to add bin
folder under this directory to the system PATH environment variable.
For Linux/Mac OS:
export PATH=$HOME_DIR/llvm-18.1/bin:$PATH
For Windows PowerShell:
$env:PATH = "$env:HOME_DIR\llvm-18.1\bin;" + $env:PATH
For Windows cmd:
set "PATH=%HOME_DIR\llvm-18.1\bin;%PATH%"2.1 Building ISPC for CPU development
You're now ready to build ISPC. Run CMake with the following options:
cmake -B build-ispc ispc cmake --build build-ispc
ISPC executable is placed under bin
directory in the build directory. You may use it or provide CMAKE_INSTALL_PRERIX
and run install target to install ISPC in the desired location.
ISPC-specific variables
You can specify path to required tools using the following variables:
You can build ISPC with cross-OS compilation support by passing -DISPC_CROSS=ON
on Linux/macOS and -DISPC_CROSS=ON -DISPC_GNUWIN32_PATH=/absolute/path/to/GnuWin32/bin
on Windows to CMake command described in 2.1. Depending on your host system ISPC may be built for Windows, Linux, FreeBSD, macOS, Android, iOS, and PS targets. You can disable any target system by passing -DISPC_[WINDOWS|LINUX|FREEBSD|MACOS|ANDROID|IOS|PS]_TARGET=OFF
to CMake. By default the following combinations are supported:
Depending on which target OSes are selected you will be asked to provide additional paths:
Xe-enabled build is supported on Windows and Linux, and it has three additional dependencies:
Please use exact commits SHA as used in
osPresetsand the same LLVM as you plan to build ISPC. CMake commands used in
Dockerfileare applicable for both Windows and Linux. Install build artifacts from SPIRV-LLVM-Translator and vc-intrinsics to one folder (
XE_DEPS
) and artifacts from level-zero build to another folder (
L0_ROOT_DIR
). Now you're ready to build ISPC with Xe support, just provide extra flags to CMake commands from
2.1:
-DXE_ENABLED=ON -DXE_DEPS_DIR=$XE_DEPS -DLEVEL_ZERO_ROOT=$L0_ROOT_DIR
2.4 Building ISPC package with cross-compilation and Xe support
If you want to build a complete ISPC package with cross-compilation and Xe support, the easiest way to sue superbuild CMake using prebuilt LLVM from the stage 1. Run the following CMake commands:
On Linux:
cmake superbuild -B build-xe-cross-ispc --preset os -DISPC_CROSS=ON -DINSTALL_WITH_XE_DEPS=ON -DPREBUILT_STAGE2_PATH="${HOME_DIR}/llvm-18.1" -DCMAKE_INSTALL_PREFIX="${HOME_DIR}"/ispc-xe-cross
On Windows cmd:
cmake superbuild -B build --preset os -G "NMake Makefiles" -DINSTALL_WITH_XE_DEPS=ON -DPREBUILT_STAGE2_PATH=%HOME_DIR%/llvm-18.1 -DCMAKE_INSTALL_PREFIX=%HOME_DIR%\ispc-xe-cross -DGNUWIN32=%CROSS_TOOLS_GNUWIN32% -DISPC_CROSS=ON
The system consists of three main scripts:
Also system has some additional files:
Tests are in three directories:
If you want to check stability of ISPC compiler after your changes you should use run_tests.py
or alloy.py -r --only=stability
. Note that you must set LLVM_HOME
and ISPC_HOME
for alloy.py
Run_tests.py will execute all tests from “tests” and “tests_errors” directories for a given target, arch and opt_level. Then it will report which failed tests (runfail or compfail). Also run_tests.py will check fail_db.txt file with known fails and will report if the fails are new or known.
If you want to have more general view you should use alloy.py -r --only=stability
. This will run run_tests.py for all/selected supported targets, archs, opt_levels and LLVM versions (Note that generic targets will run only with x86-64 arch, this is by design). Then you will have full report about fails, new_fails, passes and new_passes. If you don’t have appropriate LLVM version alloy will silently download and build it. You can select combinations for your test runs by using option --only=”” and select targets by using --only-targets=””. Note that alloy will automatically detect targets of your system and SDE (if you set SDE_HOME environment variable).
If you want to change fail_db.txt file by adding or deleting fails of current run you should use option --update-errors=F (update fails) or --update-errors=FP (update fails and passes) both in alloy.py
and run_tests.py
.
Each test in tests
folder is in a self-contained ispc source file checking particular functionality; it must define two functions:
The test script compiles each test function to object file for CPU targets or SPIR-V or ZE Binary for Xe targets and then runs the functions above, passing the test function particular values of particular types based on its signature. It then checks to make sure that the values returned from the call to
result()
match the values returned from the call to the test function; if they don't the differing values are printed along with an error message.
To make this concrete, here is a example of a test (a cleaned-up version of tests/bool-float-typeconv.ispc). It does a quick sanity check of bool to float type conversion.
#include "../test_static.isph" task void f_f(uniform float RET[], uniform float aUniform[]) { float a = aUniform[programIndex]; RET[programIndex] = a < 3.; } task void result(uniform float RET[]) { RET[programIndex] = 0; RET[0] = RET[1] = 1; }
The test starts from #include "../test_static.isph"
. It contains CPU wrappers for task
functions used in tests. It is motivated by different entry points of ISPC program on CPU and Xe - on CPU it is an export
function, on Xe it is a task
. To have unified set of tests for CPU and Xe we use task
modifier in test functions and wrap them into export
functions with a launch task
inside for CPU using test_static.isph
. For example, the wrapper for the f_f
task will be:
task void f_f(uniform float res[], uniform float vfloat[]); export void f_f_cpu_entry_point(uniform float res[], uniform float vfloat[]) { launch[1] f_f(res, vfloat); }
Now let's look into ISPC functions themselves. First, notice that the test function defined here is f_f. In addition to the array in which to store the result values computed by the function, the RET parameter, functions with the name f_f are also passed an array of floats, with values {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}. The test function converts this to a varying value
aby directly indexing into this array with
programIndex
, giving
athe value one in the first program instance and so forth. By inspection, we can see that the boolean test in the last line of f_f should evaluate to true for the first two program instances running, and false for all of the rest, and that the conversion of those bools to floats should put 1 in the first two program instances result values and zero in the rest. These, in turn, are the values that result() reports are expected.
Here are the types and values passed as parameters by the test driver for functions with the various signatures listed above:
task void f_v(uniform float RET[]); // i.e. no parameters passed other than the output array // a[] = { 1, 2, 3, ... }; task void f_f(uniform float RET[], uniform float a[]); // a[] = { 1, 2, 3, ... }; // b = 5; task void f_fu(uniform float RET[], uniform float a[], float b); // a[] = { 1, 2, 3, ... }; // b[] = { 2, 4, 6, ... }; task void f_fi(uniform float RET[], uniform float a[], int b[]); // a[] = { 1, 2, 3, ... }; // b[] = { 5, 6, 7, ... }; task void f_di(uniform float RET[], uniform double a[], int b[]); // a[] = { 1, 2, 3, ... }; // b = 5; task void f_du(uniform float RET[], uniform double a[], double b); // a[] = { 1, 2, 3, ... }; // b = 5; task void f_duf(uniform float RET[], uniform double a[], float b);
There is a slightly different group of tests related to print (e.g. print_uf
, print_f
, print_fuf
). Both testing function and result
prints output to stdout and run_tests.py
checks that that output is correct.
New functionality should have targeted tests that exercise the set of features that the functionality introduces. If the functionality is in any way dependent on the mask, it's important to exercise a few cases like 'mask all on', 'mask all off', and 'mixed mask'.
Main usage of run_tests.py to understand stabilityTo run the tests, run the run_tests.py python script in the top-level ispc source directory.
If successful, the test script will print output like:
% ./scripts/run_tests.py Executed 1517 / 1543 (26 skipped) PASSRATE (1517/1517) = 100% 1517 / 1543 tests PASSED 0 / 1543 tests FAILED compilation 0 / 1543 tests FAILED execution 26 / 1543 tests SKIPPED <List of skipped tests> No new fails %
If some tests fail, the test system will generate an additional output indicating which test failed and how it failed. The exit code is equal to the number of tests that failed. Thus, if all pass, it generates a regular exit code of 0.
Useful commands:
When running on Windows, failing tests may cause a dialog window suggesting to find a solution on the web or debug a problem. This may be annoying. To turn it off, you need to do two things:
https://docs.microsoft.com/en-us/windows/win32/wer/wer-settings
In addition to functional tests described in previous sections, ISPC has a set of lit tests in tests/lit-tests
based on LLVM test infrastructure. They are mainly used to check compiler code generation or to check compiler diagnostics. To write ISPC lit tests, follow the same rules as for LLVM FileCheck. ISPC lit tests can be run using make check-all
on Linux or Tests/check-all
target in Visual Studio on Windows. There is a set of features that you can use in lit tests if you need to set up execution rules:
The whole list of supported features, you can find in
tests/lit-tests/lit.cfg
Example of ISPC lit test:
// The test checks that cpu definitions (including all synonyms) are successfully consumed by compiler. // RUN: %{ispc} %s -o %t.o --nostdlib --target=sse2-i32x4 --cpu=znver3 // RUN: %{ispc} %s -o %t.o --nostdlib --target=sse2-i32x4 --cpu=alderlake // RUN: %{ispc} %s -o %t.o --nostdlib --target=sse2-i32x4 --cpu=adl // RUN: %{ispc} %s -o %t.o --nostdlib --target=sse2-i32x4 --cpu=sapphirerapids // RUN: %{ispc} %s -o %t.o --nostdlib --target=sse2-i32x4 --cpu=spr // REQUIRES: X86_ENABLED // REQUIRES: LLVM_12_0+ uniform int i; void foo() {}
All compiler changes should be covered by lit tests.
If you want to validate performance (applied to CPU only)If you want to validate how your changes influence ISPC performance you should use perf.py or alloy -r --only=performance. Note that you must set LLVM_HOME
and ISPC_HOME
for alloy.py
If you want to measure performance of your changes use perf.py. Perf.py will build and run all tests listed in perf.ini from “examples/cpu” directory and report performance numbers. If you want to compare performance of two ISPC compilers you should use perf.py --ref=reference_compiler. This will generate a comparison report between two compiler versions.
If you want to compare two branches of ISPC (For example branch with your changes and master) you should use alloy -r --only=performance. This will build the newest LLVM if needed (Note that LLVM will be built silently. If you want selfbuild or source from tar you should use alloy -b first), build your ISPC compiler, switched to “master” branch, build reference compiler and then execute perf.py. Logs will be in alloy_results[date] directory. Option --compare-with=name_of_chekout_or_branch will change reference branch.
If you get suspicious results of runs you can increase the number of runs using -nX ( the switch is available in both alloy.py and perf.py).
Main usage of perf.py to measure performanceWith the introduction of generic targets in ISPC, adding a new target has become straightforward. Follow the steps below to integrate a new target seamlessly. You can use https://github.com/ispc/ispc/pull/3188 as a reference:
1. Define the New Target in ISPCTarget ClassISPCTarget
class in target_enums.h and target_enums.cpp (a870459).ISPCTargetIsNeon
) (52337f5).ISA
nativeVectorWidth
nativeVectorAlignment
dataTypeWidth
vectorWidth
maskBitCount
target-<your_target>.ll
in the <root>/builtins
folder using the appropriate template.targetParentMap
in builtins.cpp: determine the parent target, which provides standard library function implementations. If custom implementations are required, place them in builtins/target-<your_target>.ll
. If you want to learn more about hierarchical ISPC targets, read this (030dc9e).ispc --support-matrix
scripts/run_tests.py --target=<your-target>
scripts/run_tests.py --target=neon-i16x167. Enable Hardware Features (Optional)
m_has
, e.g., m_hasHalfConverts
).lIsARMFeatureSupported
function to follow the logic).scripts/run_tests.py --target=<your-target> -o [O0|O1|O2]
By following these steps, you can successfully add and validate a new target in ISPC.
HOWTO: Bisecting ISPC and LLVMBisecting is often necessary to find the faulty change or the change that fixes an issue. This can be accomplished by dissecting ISPC and/or LLVM history with git bisect
.
Important Note: Bisect works well when something changes monotonously in history (e.g., good → good → good → bad → bad → bad). However, if there are multiple points where the behavior swaps back and forth, bisect may find the wrong change.
Before starting bisect, you need:
: If your test case was previously failing and accidentally started passing, and you need to find the change that fixed it, you can use
old
/
new
aliases instead of
bad
/
good
. You can also override these names with user-provided ones (consult the git manual for details).
1. Minimize the Test CaseIt's highly recommended to minimize your test case before bisecting:
delta
or similarSometimes the wrong behavior can be isolated to LLVM only. In this case:
opt
/llc
on minimal LLVM IRbugpoint
to minimize LLVM IRgit bisect start
good
/bad
(or old
/new
) accordinglyIf the faulty behavior can be checked automatically, create a script and run:
git bisect run <your-test-script>Linear History Considerations
Bisecting is only possible on linear history. Use git merge-base
to find start and end points of linear history when the project has forked release branches.
git bisect skipISPC/LLVM Bisection Workflow
This example demonstrates bisecting LLVM history when an ISPC test starts failing due to an LLVM update.
tests/func-tests/bug.ispc
started to fail at some point# Known failing state PATH=/path-to-llvm-K+1/bin:$PATH ./scripts/run_tests.py ./tests/func-tests/bug.ispc # → FAIL # Known working state PATH=/path-to-llvm-K/bin:$PATH ./scripts/run_tests.py ./tests/func-tests/bug.ispc # → OK1. Configure and Build LLVM (Known Good State)
# Checkout known good LLVM version [/llvm-project] $ git checkout K # Configure LLVM (build only what you need for faster iteration) [/build-llvm] $ cmake /llvm-project/llvm -G Ninja \ -DLLVM_TARGETS_TO_BUILD="X86" \ -DLLVM_ENABLE_PROJECTS="clang" \ -DCMAKE_INSTALL_PREFIX=/dir-llvm-bin \ -DCMAKE_BUILD_TYPE=Release \ -DLLVM_ENABLE_ZLIB=OFF \ -DLLVM_ENABLE_ZSTD=OFF \ -DLLVM_INSTALL_UTILS=ON # Build and install [/build-llvm] $ ninja && ninja install2. Configure and Build ISPC
# Checkout ISPC version [/ispc] $ git checkout 1.N # Configure ISPC [/build-ispc] $ PATH=/dir-llvm-bin/bin:$PATH cmake /ispc -G Ninja \ -DISPC_SLIM_BINARY=ON \ -DARM_ENABLED=OFF # Build ISPC [/build-ispc] $ ninja3. Verify Known Good State
[/ispc] $ PATH=/build-ispc/bin:$PATH ./scripts/run_tests.py ./tests/func-tests/bug.ispc # Should return: OK
# Initialize bisect [/llvm-project] $ git bisect start # Mark current (known good) commit as good [/llvm-project] $ git bisect good # Checkout known bad commit [/llvm-project] $ git checkout K+1 # Rebuild and install LLVM [/build-llvm] $ ninja && ninja install # Rebuild ISPC [/build-ispc] $ ninja clean && ninja # Test the behavior (should fail) [/ispc] $ PATH=/build-ispc/bin:$PATH ./scripts/run_tests.py ./tests/func-tests/bug.ispc # Should return: FAIL # Mark as bad [/llvm-project] $ git bisect bad
At this point, git will change HEAD to an intermediate commit. Repeat the following process:
ninja && ninja install
ninja clean && ninja
PATH=/build-ispc/bin:$PATH ./scripts/run_tests.py ./tests/func-tests/bug.ispc
git bisect good
or git bisect bad
Continue until git identifies the guilty commit.
# When done, clean up bisect state [/llvm-project] $ git bisect reset
git bisect run
for faster resultsgit bisect skip
for commits that don't buildRetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4