llvm-spirv
Skill1.1k starsChanged 35 days agoApache-2.0View on GitHub
Experimental Vulkan AST-to-LLVM-to-SPIR-V backend, its fail-closed runtime-interface boundary, LLVM build integration, and validation path.
Trending
Quiet since July.thin dataabout the whole repository
Who's talking about LuisaGroup/LuisaCompute
- Stack Overflow“c++ error: ‘struct std::atomic_flag’ has no member named ‘test’”Stack Overflow · Dec 26, 2022 · 1 votes
- Tencent/ncnn“optimize multi-head attention (increased shared-memory tile size, add fused kernel)”GitHub · Jul 10, 2026
- bazelbuild/bazel-central-registry“wanted: [LuisaGroup/LuisaCompute]”GitHub · Nov 8, 2025
- MuGdxy/SymEigen“Try to add support for luisa-compute”GitHub · Sep 15, 2025
- LuisaGroup/LuisaRender“Remove `util/u64.h` and use `Var<ulong>` instead”GitHub · Jun 2, 2024
The biggest 5 of 5 mentions linking to the repository that our crawl found. Each line is their own headline.
In this repository22 other files it gives its agents.
- .github/copilot-instructions.mdCopilot instructions
- astSkill
- backend-architectureSkill
- cmakeSkill
- cpp-styleSkill
- debugSkill
- gitSkill
- glslangSkill
- hlslSkill
- ir-pipelineSkill
- lc_apiSkill
- lc_native_shaderSkill
- lc_optimizeSkill
- lspSkill
- project_structureSkill
- renderdocSkill
- spirv_codegenSkill
- spv-optSkill
- testSkill
- xir-passesSkill
- xmakeSkill
- yyjsonSkill
The file
Show its rules and text
We split this skill into the rules it gives an agent
17 rules. About 0 are common, 7 are distinctive, and 10 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈7
Distinctive · estimate
This author's own choices. Worth a look.
10
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 37
No clang or LLVMExts component is required by this route: clang_target and LLVMExts have zero hits in …
- Very specific: line 50
Do not expose an LLVM type through spirv_llvm.h or llvm_codegen_result.h.
- Distinctive: line 59
every kernel argument, including ordinary value arguments and direct buffers.
- Very specific: line 107
Create a fresh LLVM context/module/builder and register LLVM's SPIR-V target exactly once with std::call_once.
- Very specific: line 116
Before target emission, recursively scalarize aggregate loads/stores, lower aggregate returns to void plus an out pointer, scalarize again, …
- Very specific: line 139
Only DISPATCH_ID, THREAD_ID and BLOCK_ID are lowered, through llvm::Intrinsic::spv_thread_id, spv_thread_id_in_group and spv_group_id.
- Distinctive: line 148
Verify the full module before invoking LLVM target passes.
- Very specific: line 165
Use the declarations from <llvm/Support/TargetSelect.h>.
- Distinctive: line 187
keep LLVM includes and ordinary definitions private.
- Very specific: line 193
use llvm_config(... USE_SHARED ...) when a monolithic LLVM target exists so target-specific archives omitted by the dylib remain linked.
- Very specific: line 211
Treat the selected llvm-config as the source of truth:
- Very specific: line 220
use static LLVM components on Windows because llvm-config reports DLL filenames rather than MSVC import libraries for shared mode.
- Distinctive: line 224
verify the libstdc++ ABI and, on Windows, the CRT family and Debug/non-Debug mode before compiling the facade.
- Distinctive: line 232
do not apply native XIR capability reconciliation or exact accel-role rules to LLVM artifacts.
- Distinctive: line 259
When adding support, include at least:
- Very specific: line 274
Do not call EmitSPIRV() directly from Vulkan.
- Distinctive: line 276
Do not report an AST opcode as supported merely because a visitor case exists.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
LLVM SPIR-V Codegen
The experimental backend lives in
src/backends/common/spirv_llvm/. It lowers a Luisa AST Function to LLVM IR
and asks LLVM's native SPIR-V target to emit a
spirv64-unknown-vulkan1.2 module.
This is not the native XIR-to-SPIR-V path. The two Vulkan codegen selections are mutually exclusive, and enabling both fails at configure time:
- CMake:
LUISA_COMPUTE_ENABLE_VK_AST_LLVM_SPIRV=ON(defaultOFF) andLUISA_COMPUTE_ENABLE_VK_XIR_SPIRV=OFF(defaultON); the combinationmessage(FATAL_ERROR)s in the rootCMakeLists.txt,src/backends/CMakeLists.txtandsrc/backends/vk/CMakeLists.txt. - XMake:
lc_vk_backend_use_ast_llvm_spirv=true(defaultfalse), withlc_llvm_pathpointing to an LLVM installation/build prefix or itsllvm-config. Rootxmake.luaraise()s on the conflict andscripts/xmake_func.luaraise()s whenlc_llvm_pathis unset. Both xmake route options default tofalse, so there the native route is opt-in too.
The selected route arrives as a compile definition on the Vulkan plugin:
LUISA_XIR_TO_SPIRV or LUISA_AST_LLVM_TO_SPIRV
(src/backends/vk/CMakeLists.txt, src/backends/vk/xmake.lua);
src/backends/vk/device.cpp #errors if both are defined. This repository
builds against LLVM 22 (lc_llvm_path in scripts/options.lua), but
src/backends/common/spirv_llvm/CMakeLists.txt pins no version:
find_package(LLVM CONFIG REQUIRED) only requires the SPIRV target and
llvm/IR/IntrinsicsSPIRV.h. scripts/download_and_patch_llvm.cmake
(LUISA_COMPUTE_DOWNLOAD_LLVM, official LLVM 19.1.5, reached from the fallback
backend only) is not this route's LLVM.
No clang or LLVMExts component is required by this route: clang_target and
LLVMExts have zero hits in src/backends/common/spirv_llvm/CMakeLists.txt and
src/backends/common/spirv_llvm/xmake.lua — verify with
git grep -c "clang_target" -- src/backends/common/spirv_llvm (no match).
The public entry is deliberately LLVM-header-free:
#include <spirv_llvm/spirv_llvm.h>
auto result = lc::llvm_codegen::compile_spirv(kernel, option);Do not expose an LLVM type through spirv_llvm.h or
llvm_codegen_result.h; consumers of the static facade must not inherit LLVM
include directories or unrelated LLVM preprocessor definitions.
Current support boundary
The Vulkan descriptor interface is preflighted before AST lowering by
validate_llvm_vulkan_resource_model. At present it rejects:
- every kernel argument, including ordinary value arguments and direct buffers;
- textures, bindless arrays, acceleration structures, custom/indirect resources, and their global heaps;
- shader printing.
Consequently the current end-to-end Vulkan contract is intentionally narrow: no-argument, no-print compute kernels whose operations are implemented by the visitor. The successful interface contains only the fixed 16-entry immutable sampler property. Do not describe the visitor's partial resource stubs as runtime support, and do not bypass this preflight merely because LLVM happens to emit some SPIR-V for an address-space global.
Unsupported AST operations call LUISA_NOT_IMPLEMENTED or otherwise fail
closed: unimplemented builtin call ops hit
LUISA_ERROR_WITH_LOCATION("Unimplemented CallOp in LLVM backend: {}") in
LLVMStateVisitor::_codegen_builtin_call, resource-shaped kernels hit the
LUISA_ASSERT in LLVMCodegenUtility::GenerateProperties, and software
(use_fallback_rtx()) ray traversal is refused in Device::create_shader.
LUISA_ERROR is [[noreturn]] (include/luisa/core/logging.h), so those
paths abort instead of emitting a shader. Returning zero, undef, or a no-op
is not an acceptable way to claim support.
This route is also the only SPIR-V producer in that build: the
_create_shader_hlsl compatibility dispatch in Device::create_shader sits
inside #ifdef LUISA_XIR_TO_SPIRV, so an LUISA_AST_LLVM_TO_SPIRV build never
re-routes a kernel to HLSL/DXC: printing and unimplemented call ops must fail
in the facade instead. When expanding the backend, implement the LLVM IR
lowering, extend the property preflight, validate the Vulkan runtime binding
shape, and add an end-to-end SPIR-V validator test together.
Source map
| Files | Responsibility |
spirv_llvm.h/.cpp | LLVM-free facade and once-only SPIR-V target registration |
llvm_codegen_result.h | SPIR-V words, properties, printer records, and global bindless flags returned to Vulkan |
llvm_codegen_stack_data.h/.cpp | Per-compilation state and mutex-protected reusable state pool |
llvm_codegen_utility.h/.cpp | Type/constant/function lowering, module legalization, target-machine emission, post-processing, and validation |
llvm_state_visitor.h/.cpp | AST expression and statement lowering through llvm::IRBuilder<> |
llvm_compat.h | API-detection boundary for LLVM-version differences such as nullable terminator lookup |
vulkan_binding_properties.h | Pure property planner and fail-closed resource-model support check |
CMakeLists.txt / xmake.lua | Component-aware LLVM discovery and static-facade linkage |
Compilation pipeline
LLVMCodegenUtility::CompileSPIRV owns the complete path:
- Create a fresh LLVM context/module/builder and register LLVM's SPIR-V target
exactly once with
std::call_once. - Initialize a
spirv64-unknown-vulkan1.2target machine and its data layout. - Detect prospective bindless/property usage from the AST, validate the current Vulkan resource-model boundary, and freeze the property plan before visiting the AST.
- Lower the kernel and reachable callables to LLVM IR. A compute entry is
named
main, has no function parameters, and carrieshlsl.shader=computeplushlsl.numthreadsattributes. - Before target emission, recursively scalarize aggregate loads/stores,
lower aggregate returns to
voidplus an out pointer, scalarize again, and verify the LLVM module. - Emit object bytes through LLVM's legacy pass manager. The expected output is raw SPIR-V words; an ELF result is not silently accepted as valid SPIR-V and will fail the final validator (there is no ELF section extractor yet).
- Strip LLVM's
Addresses/Linkagecapabilities and linkage decorations, and convertOpPtrAccessChainforms to their logical-addressing counterparts. - Validate the returned module with SPIRV-Tools under
SPV_ENV_VULKAN_1_2.
Unlike the native path, this backend currently has no SPIRV-Tools optimizer
stage and does not produce the native exact per-argument role plan. Vulkan
serializes it as LLVM_SPIRV, embeds constants directly in the module, and
uses the backend's conservative SPIR-V artifact feature requirements.
Function and control-flow rules
- Vulkan entry points have no LLVM function parameters. The current code has provisional global-variable lowering for arguments, but the property preflight rejects those kernels until a real descriptor ABI exists.
- Callable arguments remain LLVM function parameters.
- Only
DISPATCH_ID,THREAD_IDandBLOCK_IDare lowered, throughllvm::Intrinsic::spv_thread_id,spv_thread_id_in_groupandspv_group_id; every other builtin variable currently storesllvm::UndefValueinto an alloca inLLVMCodegenUtility::CodegenFunction. That is a stub, not support. - Save and restore the builder insertion point, current function, and variable map around recursive callable generation.
- Probe incomplete blocks through
llvm_compat.h::terminator_or_null; do not guess the API from an LLVM major version. - Verify the full module before invoking LLVM target passes. Per-function verification warnings are useful during construction but do not replace the module check.
- Aggregate legalization is mandatory because the LLVM SPIR-V target cannot reliably legalize the aggregate memory/return forms produced here.
Target initialization
InitializeLLVMSPIRVTarget registers global LLVM state in dependency order:
LLVMInitializeSPIRVTargetInfo();
LLVMInitializeSPIRVTarget();
LLVMInitializeSPIRVTargetMC();
LLVMInitializeSPIRVAsmPrinter();Use the declarations from <llvm/Support/TargetSelect.h>. Hand-written
declarations can acquire the wrong language linkage. Keep the std::call_once
guard because shader compilation may be concurrent.
The target triple is deliberately spirv64: the supported LLVM revision's
spirv32 path fails in pointer-cast legalization. Vulkan logical addressing is
restored by the checked post-processing step and then enforced by validation;
raw EmitSPIRV() output is not the public contract.
CMake integration
The target is luisa-compute-spirv-llvm. It is created only when Vulkan and
LUISA_COMPUTE_ENABLE_VK_AST_LLVM_SPIRV are enabled. The Vulkan plugin also
links luisa-compute-spirv, because the shared artifact codec still needs the
native/common SPIR-V validation and feature-reconciliation utilities.
src/backends/common/spirv_llvm/CMakeLists.txt must:
find_package(LLVM CONFIG REQUIRED)without allowingLLVMConfig.cmaketo overwrite the project'sCMAKE_MSVC_RUNTIME_LIBRARYdefault;- require an exact
SPIRVtoken inLLVM_TARGETS_TO_BUILDand locatellvm/IR/IntrinsicsSPIRV.hin LLVM's reported include directories; - keep LLVM includes and ordinary definitions private;
- compile-check
_GLIBCXX_USE_CXX11_ABIand propagate only that ABI macro through the C++ facade when LLVM reports it; - request
core,support,bitwriter,transformutils,analysis,codegen,target,mc,spirvcodegen,spirvdesc,spirvinfo, andspirvanalysisthrough LLVM's component mapper; - use
llvm_config(... USE_SHARED ...)when a monolithicLLVMtarget exists so target-specific archives omitted by the dylib remain linked; - reject incompatible Windows CRT families or Debug iterator modes, and stage and install a monolithic LLVM DLL when that is the selected import.
The facade is static, so implementation link dependencies must still reach the final Vulkan plugin/test link. Do not make LLVM headers public as a workaround for a link failure.
XMake integration
The target is lc-spirv-llvm. Configuration requires an explicit
lc_llvm_path. Keep lc_vk_backend_use_xir_spirv=false when selecting it:
the top-level configuration rejects an explicit conflict, while the option
hook forces lc_enable_xir on and normalizes the native option off. The
SPIR-V targets and tests are created only when the Vulkan backend itself is
enabled.
Treat the selected llvm-config as the source of truth:
- accept an executable path, install prefix, build prefix, or common source-tree build layout;
- parse
--quote-pathsoutput withos.argv, including all-Ipaths from--cppflagsso generated intrinsics headers in development trees are found; - require an exact
SPIRVtoken from--targets-built; - query
--shared-modefor the complete component set before selecting--link-sharedor--link-static; - use static LLVM components on Windows because
llvm-configreports DLL filenames rather than MSVC import libraries for shared mode; - propagate shared-library rpaths on Unix and the required component system libraries on every platform;
- verify the libstdc++ ABI and, on Windows, the CRT family and Debug/non-Debug mode before compiling the facade.
Vulkan artifact boundary
The LLVM result reuses hlsl::Property and the common Vulkan artifact codec,
but it is a distinct LLVM_SPIRV dialect:
- do not apply native XIR capability reconciliation or exact accel-role rules to LLVM artifacts;
- constants are embedded in SPIR-V, so there is no constant-UBO payload;
- saved arguments use the legacy/unspecified resource-role sentinel;
- loaded artifacts keep the codec's MD5 section-digest and
CODEGEN_DIALECT_MISMATCHchecks, and their SPIR-V header/entry point is re-verified, butvalidate_spirv_artifact_modulesruns SPIRV-Tools validation and capability reconciliation only forShaderCodegenDialect::XIR_SPIRVand returns early for every other dialect; LLVM artifacts depend on the facade's own compile-timeSPV_ENV_VULKAN_1_2validation.
Tests
test_spirv_llvm_facade is registered only when the LLVM facade target exists
(if (TARGET luisa-compute-spirv-llvm) in src/tests/CMakeLists.txt, CTest
labels unit;unit_ext;spirv;spirv_llvm; on xmake it needs lc_vk_backend plus
lc_vk_backend_use_ast_llvm_spirv). It compiles a no-argument kernel through
the public header, checks the fixed sampler property, and independently
validates/disassembles the returned module for Vulkan 1.2.
test_vk_shader_binary_contract separately covers the common artifact boundary
under the LUISA_AST_LLVM_TO_SPIRV dialect define. Both are host-only tests:
neither calls luisa::test::create_device, so argv[1] is not a backend name
here and xmake run test_spirv_llvm_facade is the whole command.
test_spirv_llvm_facade is the only test that calls compile_spirv; no
runtime Vulkan test exercises this route yet.
When adding support, include at least:
- a pure property-preflight rejection/acceptance test;
- a public-facade compile test;
- independent
SPV_ENV_VULKAN_1_2validation; - a Vulkan artifact round trip when properties or feature contracts change;
- a runtime Vulkan test before claiming descriptor or dispatch support.
Diagnostics and pitfalls
EmitSPIRV()writesllvm_ir_debug.llin the process working directory.- Missing SPIR-V intrinsics or target components are configuration errors, not reasons to guess library filenames or add every LLVM archive.
getDeclarationIfExists()may return null. Handle that at the operation's semantic boundary; never call through a null intrinsic declaration.- Do not call
EmitSPIRV()directly from Vulkan. Only the public facade runs required post-processing and Vulkan validation. - Do not report an AST opcode as supported merely because a visitor case exists; it must survive resource preflight, LLVM verification, SPIR-V emission, Vulkan validation, artifact loading, and runtime dispatch.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

