Hubbry Logo
Standard Portable Intermediate RepresentationStandard Portable Intermediate RepresentationMain
Open search
Standard Portable Intermediate Representation
Community hub
Standard Portable Intermediate Representation
logo
8 pages, 0 posts
0 subscribers
Be the first to start a discussion here.
Be the first to start a discussion here.
Standard Portable Intermediate Representation
Standard Portable Intermediate Representation
from Wikipedia
SPIR-V
DeveloperKhronos Group
Initial release2015
Stable release
SPIR-V 1.6 / December 16, 2021; 3 years ago (2021-12-16)
Operating systemCross-platform
PlatformCross-platform
TypeIntermediate language
Websitewww.khronos.org/registry/SPIR-V

Standard Portable Intermediate Representation (SPIR) is an intermediate language for parallel computing and graphics by Khronos Group. It is used in multiple execution environments, including the Vulkan graphics API and the OpenCL compute API, to represent a shader or kernel. It is also used as an interchange language for cross compilation.[1][2]

SPIR-V is a new version of SPIR which was introduced in 2015 by the Khronos Group, and has since replaced the original SPIR, which was introduced in 2012.

On September 19th 2024, Microsoft has announced plans to adopt SPIR-V as the Direct3D Interchange format in place of DXIL, beginning support from Shader Model 7 on.[3]

Purpose

[edit]

The purposes of SPIR-V are to natively represent the primitives needed by compute and graphics; to separate high-level language from the interface to compute and graphics drivers; to be the distribution form, or distribute fully compiled binaries; to be a fully self-contained specification; and to support multiple APIs. It is also used as an intermediate target for cross-compilation tools.

For example, SPIR-V allows the Vulkan API to use any shading language, including GLSL and HLSL.[4][5] SPIR-V can be decompiled into several shading languages (GLSL, GLSL ES, MSL, HLSL) using SPIRV-Cross, so that these languages can be interconverted.[6] It also has paths to and/or from WebGPU, OpenCL, SYCL, C++, and Rust.

In target platforms, ingesting SPIR-V removes the need to build a high-level language source compiler into device drivers, which reduces driver complexity.[2]

Versions

[edit]

SPIR was originally introduced in 2011 and SPIR-V was introduced in 2015.

SPIR and SPIR-V
SPIR 1.2 SPIR 2.0 SPIR-V 1.X
LLVM Interaction LLVM IR version 3.2 LLVM IR version 3.4
100% Khronos defined
Round-trip lossless conversion
Compute Constructs Metadata/Intrinsics Metadata/Intrinsics Native
Graphics Constructs No No Native
Supported Language & Feature Supported OpenCL C 1.2
OpenCL C 1.2
OpenCL C 2.0
OpenCL C 1.2 / 2.X
OpenCL C++
GLSL
OpenCL Ingestion OpenCL 1.2 Extension OpenCL 2.0 Extension OpenCL 2.1/2.2 Core
Graphics API Ingestion
Vulkan 1.X
OpenGL 4.6 Core

LLVM-based versions

[edit]

SPIR prior to the 2015 SPIR-V release was based on the LLVM Intermediate Representation. A provisional specification for SPIR 1.0 was announced in 2012.[7] On July 22, 2013, a provisional specification SPIR 1.2 was announced at SIGGRAPH 2013.[8] The final SPIR 1.2 specification was released at HiPEAC 2014 on January 21, 2014.[9] On August 11, 2014, a provisional specification for SPIR 2.0 was released at SIGGRAPH 2014.[10] SPIR-V does not use LLVM.[2]

SPIR-V

[edit]

SPIR-V 1.0 is a new version of SPIR announced in March 2015,[11] and released on November 16, 2015.[12] The SPIR family now includes a true cross-API standard that is fully defined by Khronos with native support for shader and kernel features.

A separate program by the Khronos Group allows for interconversion with LLVM IR.[13]

Support for ingestion of SPIR-V is incorporated in the core specification of OpenCL 2.1, the Vulkan API, and OpenGL version 4.6.

SPIR-V Releases
Date Version Notes
April 18, 2016 1.1 Released at IWOCL 2016 along with Provisional OpenCL 2.2. SPIR-V 1.1 added support for OpenCL C++, initializer/finalizer function execution modes, named barriers, subgroup execution, program scope pipes and pipe storage.[14]
May 16, 2017 1.2 Released at IWOCL 2017 along with OpenCL 2.2. SPIR-V 1.2 added support for runtime specialization of key tuning parameters in OpenCL 2.2.[15]
March 7, 2018 1.3 Released along with Vulkan 1.1. SPIR-V 1.3 Added support for subgroup operations and enables enhanced compiler optimizations.[16]
May 7, 2019 1.4[1]
September 13, 2020 1.5[1]
December 16, 2021 1.6[1]

Features

[edit]

SPIR-V is a high-level intermediate language, exchanged in binary form. Functions are represented by a control-flow graph of basic blocks, using static single assignment (SSA) form. Data structures retain high-level hierarchical representation. It is not lossy like previous byte-code or virtual machine-like intermediate representations used for graphical shaders. This allows closer to optimum performance on the target devices.[17]

Extensibility

[edit]

SPIR-V can be extended by writing extensions to add semantics, or reserving ranges of the token values for the party's use. Vendors can independently add desired semantics to SPIR-V.[18] Additional sets of extended instruction sets can be provided in separate specifications. Multiple sets can be imported without issue, as extended instructions are used by specifying the ID of the set and of the instruction within the set.[18]

Shaders

[edit]

Debuggers include RenderDoc, SwiftShader, and Amber.[19]

Graphical shaders use structured control flow in SPIR-V to state how control flow nests. This helps in defining divergence and reconvergence of control flow on parallel execution environments.[20] Specialization reduces the number of variants of a shader that need to be distributed.[21]

Validation

[edit]

The SPIR-V specification states the rules that must be followed to have a valid SPIR-V module. This allows for offline validation. Drivers are not obligated to handle invalid SPIR-V modules. In testing, conformance testing verifies that drivers behave correctly when consuming valid SPIR-V, while validators verify that front-ends properly generate SPIR-V.[22]

Linking

[edit]

SPIR-V can express calls to functions in a different compilation unit. The standard version of SPIR-V uses this feature for OpenCL compute kernels, but not for shader stages, which the graphical APIs want fully linked into a single SPIR-V module.[23] There are extensions available to allow tools to temporarily use partially linked shaders and also kernels.[24]

Capabilities

[edit]

A SPIR-V module is used by a client API to support that module's features, which are classified through capabilities, and declared early in the module. A validator can confirm that the module uses only its declared capabilities, and a client API can reject modules that declare unsupported capabilities.[25]

SPIR-V for GLSL cross-compilation

[edit]

SPIR-V has been used to help deal with multiple versions of source-level languages. For example, the multiple versions of OpenGL Shading Language (GLSL) require distribution of multiple versions of shaders, due to implementations that are pegged to a specific older version of GLSL, such as for WebGL 1.0 and Apple's OpenGL implementation. One of the notable use cases of SPIR-V is its ability to be used as an interchange format between GLSL versions, using tools maintained by the Khronos Group for compiling GLSL to SPIR-V glslangValidator,[26] optimizing SPIR-V spirv-opt,[22] and cross-compiling to SPIR-V to different GLSL targets spirv-cross.[27]

As a format, however, SPIR-V has some limitations for cross-compilation, including the requirement that every SPIR-V module have at least one entry-point symbol. This prevents the format from being easily used for separate compilation, where complex shaders could be assembled by a series of partial compile steps followed by a linking step. This runs counter to the stated goals of some SPIR-V tools such as spirv-link,[22] which aims to provide full linking functionality for SPIR-V binary code.

See also

[edit]

References

[edit]
[edit]
Revisions and contributorsEdit on WikipediaRead on Wikipedia
from Grokipedia
The Standard Portable Intermediate Representation (SPIR) is a binary-level intermediate language developed by the to enable portable encoding and interchange of device programs, mapping C constructs to a constrained subset of IR for vendor-neutral compilation across diverse hardware platforms. Introduced in 2012 as part of the 1.2 specification, SPIR addresses limitations in source-level portability by allowing developers to compile C kernels into a compact, loadable bitcode format that protects while supporting third-party code generation without exposing . Its core structure relies on 3.2 IR for SPIR 1.2 and 3.4 for SPIR 2.0, incorporating specific triples like "spir-unknown-unknown" for 32-bit targets and enforcing rules for C data types, qualifiers, and built-ins, such as mapping scalar integers to LLVM's i32 and vectors to . SPIR's primary goals include providing forward compatibility with newer devices, efficient runtime loading, and support for vendor extensions, making it a foundational tool for environments. Tools for generating SPIR, such as a modified compiler, are available through open-source repositories maintained by Khronos, facilitating its integration into workflows via the cl_khr_spir extension. However, SPIR has been largely superseded by its successor, SPIR-V, introduced in 2015, which extends portability to graphics APIs like while maintaining with and addressing some of SPIR's LLVM-specific constraints through a new binary format. Despite this evolution, SPIR remains relevant for legacy 1.2 and 2.0 implementations, with its provisional 2.0 specification released in 2014 emphasizing compactness and non-source encoding for device program portability.

Introduction

Purpose and Design Goals

The Standard Portable Intermediate Representation (SPIR) is a standard defining an (IR) for parallel compute kernels, targeting graphics processing units (GPUs) and other parallel processors in environments. It serves as a binary format that bridges high-level programming languages, such as C, with low-level hardware-specific code generation in the API. Core design goals of SPIR emphasize portability across diverse hardware vendors, including , , and , by providing a standardized program form that avoids vendor-specific dialects. This enables separation of front-end compilation—where high-level is translated to SPIR offline—from back-end optimization and code generation performed by drivers at runtime, thereby reducing runtime compilation overhead. SPIR functions as a portable dialect for compute kernels, allowing developers to target execution environments without writing vendor-specific code, while supporting shared tools for optimization and validation across platforms. Key benefits include faster kernel loading times due to pre-compiled binaries, protection of by avoiding exposure, and a reduction in driver complexity by offloading initial compilation. This design promotes in ecosystems, fostering a for multi-architecture development. SPIR evolved from IR as its foundational basis to achieve these objectives.

Historical Development

The development of the Standard Portable Intermediate Representation (SPIR) began in 2012 as an initiative by the to enhance portability for kernels, leveraging the compiler infrastructure to create a vendor-neutral intermediate format. This effort addressed 's portability challenges by providing a binary encoding of LLVM IR tailored for parallel compute programs, with early involvement from key contributors including Apple, which had originated , and the broader community. The provisional specification for SPIR 1.2 was released on July 22, 2013, alongside the 2.0 provisional specification, basing the format on 3.2 IR to support C kernels. The final SPIR 1.2 specification followed on January 21, 2014, establishing it as an open, cross-platform intermediate representation for device programs. Subsequently, the provisional SPIR 2.0 specification was issued on August 11, 2014, updating to 3.4 IR to align with enhanced 2.0 features and improve support for parallel programming constructs. SPIR-V, the successor to the original SPIR, was introduced on March 3, 2015, as a new binary intermediate language designed for both graphics shaders and compute kernels, coinciding with the announcements of Vulkan 1.0 and OpenCL 2.1. This shift enabled broader applicability across Khronos APIs by defining a fully specified, cross-API format independent of LLVM bitcode limitations. The SPIR-V 1.0 specification was released on November 16, 2015, with formal ratification integrated into Vulkan's ecosystem by early 2016. Key milestones in SPIR-V's evolution include the release of version 1.5 on September 13, 2020, which introduced support for operations and ray tracing extensions to accommodate advanced parallel compute and workloads. Version 1.6 followed on December 16, 2021, adding native matrix types and enhanced execution models for improved shader and kernel expressiveness. The most recent update, SPIR-V 1.6 Revision 6, was published on July 10, 2025, providing clarifications and enhancements to variable pointers and cooperative matrix capabilities for better optimization in environments. Recent developments underscore SPIR-V's expanding adoption, including Microsoft's announcement on September 19, 2024, to integrate SPIR-V as the primary interchange format for 12 shaders in Shader Model 7, promoting cross-platform compatibility. Concurrently, SPIR-V has seen growing integration in ecosystems like Intel's oneAPI , where it serves as the for device code compilation, and AMD's within the platform, enabling portable GPU acceleration. The continues to oversee SPIR and SPIR-V through its working groups, maintaining the official registry of specifications, extensions, and tools to ensure ongoing standardization and evolution.

Versions

Original SPIR

The Original SPIR, or Standard Portable Intermediate Representation, refers to the initial LLVM-based intermediate representation developed by the specifically for encoding compute kernels in a portable, non-source format. Introduced as a of LLVM Intermediate Representation (IR), it enabled compilers to target a standardized binary or textual form without exposing vendor-specific details, facilitating cross-vendor portability for applications. SPIR 1.2, released in provisional form in July 2013 and finalized in January 2014, was based on 3.2 IR and serialized primarily as LLVM bitcode, with support for textual LLVM assembly notation. It provided a direct mapping of C 1.2 language constructs to LLVM IR, allowing OpenCL kernels to be represented in a compact, vendor-neutral binary format suitable for device loading. This version focused on core 1.2 features, such as buffer and image memory objects, without support for later extensions. SPIR 2.0, with a specification dated June 5, 2014, and provisionally released on August 11, 2014, updated the representation to 3.4 IR while maintaining the bitcode serialization approach. SPIR 2.0 remained in provisional status and was not fully ratified by Khronos. It extended compatibility to C 2.0, incorporating advanced features like pipes for kernel-to-kernel communication (modeled as opaque %opencl.pipe types in global ), shared (SVM) via a generic address space (LLVM addrspace(4)), and sub-group operations through specific memory scopes (e.g., memory_scope_sub_group). These additions enabled more sophisticated parallel programming patterns, such as producer-consumer data flows and fine-grained memory sharing across host and device. Key characteristics of Original SPIR included its reliance on textual LLVM IR with strict restrictions to ensure portability and safety for environments. For instance, it prohibited , , and certain LLVM intrinsics like atomics (replaced by built-ins), while mandating specific pointer types with address space qualifiers: addrspace(0) for private memory, addrspace(1) for global, addrspace(2) for constant, and addrspace(3) for local. No casts between address spaces were allowed, and functions required the nounwind attribute to avoid runtime errors. As a compiler target, front-ends like could generate SPIR directly from source, bypassing the need for on the host. Despite these strengths, Original SPIR had notable limitations, primarily its tight coupling to specific LLVM versions—3.2 for SPIR 1.2 and 3.4 for SPIR 2.0—which restricted tool compatibility and prevented seamless integration with newer advancements without patching. It was optimized for compute workloads and proved less efficient for shaders due to its LLVM heritage and lack of native support for graphics-specific primitives. Largely superseded by the more versatile SPIR-V binary format, Original SPIR remains supported in select 1.2 and 2.0 implementations for legacy compatibility. Adoption of Original SPIR was concentrated in early OpenCL drivers from vendors like , , and NVIDIA, where it served as an intermediate step for embedding kernels in applications without source code exposure.

SPIR-V

SPIR-V, introduced by the in 2015, serves as the current standard binary intermediate representation for parallel compute kernels and graphics shaders, distinct from its predecessor by employing a dedicated binary format rather than a subset of bitcode, thereby optimizing for high-performance GPU execution environments. This design facilitates direct consumption by APIs such as and , enabling offline compilation, reduced driver overhead, and enhanced portability across hardware vendors. As the primary format for modern Khronos APIs, SPIR-V supports a range of execution models tailored to graphics pipelines and compute tasks, promoting interoperability without exposing high-level . A SPIR-V module begins with a fixed header comprising a magic number (0x07230203) for identification, the specification version (encoded as major.minor, with revisions for clarifications), a generator identifier denoting the originating tool, and a bound value indicating the highest result ID used. Following the header, the module consists of a sequence of variable-length instructions encoded in word-aligned 32-bit words, where each instruction starts with an followed by operands referenced via unique IDs that represent types, constants, variables, and other objects. Core components include entry points, which define executable functions linked to specific execution models such as Vertex for vertex processing, Fragment for pixel shading, or GLCompute for general-purpose ; these are complemented by opcodes for fundamental operations like loads (OpLoad), stores (OpStore), and arithmetic (OpIAdd, OpFAdd). Since its initial 1.0 release in 2015, SPIR-V has evolved through incremental versions to incorporate advanced GPU features, reaching version 1.6 Revision 6 in July 2025. Early updates in 1.1 (2016) introduced 16-bit types and subgroup operations like OpGroupNonUniform, while 1.4 (2019) added ray-tracing capabilities via extensions such as SPV_NVX_raytracing. Subsequent releases expanded support for features including ray queries (SPV_KHR_ray_query in 1.5, 2020), integer dot products, and cooperative matrix operations in 1.6, enabling more efficient handling of workloads and enhanced rendering techniques. Unlike the original SPIR, which relied on human-readable LLVM-derived text, SPIR-V prioritizes a compact binary form while allowing disassembly for readability, and it natively accommodates multiple execution environments beyond just . The specification is actively maintained by the through its official registry at https://registry.khronos.org/SPIR-V/, which hosts the unified documentation, headers, and a growing set of extensions for vendor-specific functionality, such as NVIDIA's SPV_NV_ray_tracing for advanced ray-tracing acceleration structures.

Technical Features

Binary Format and Module Structure

The Standard Portable Intermediate Representation (SPIR) uses the LLVM bitcode format as its binary encoding, providing a compact, portable representation of LLVM Intermediate Representation (IR) tailored for OpenCL programs. This format is a serialized that begins with a magic number (e.g., BC 0x0 0xC 0xE 0xD) to identify it as LLVM bitcode, followed by nested blocks and records for efficient and storage. The bitstream supports variable-width integers, characters, and abbreviations to minimize size, with the entire module aligned to 32-bit words for hardware compatibility. This design allows SPIR modules to be loaded directly into OpenCL runtimes without exposure, achieving typical sizes under 100 KB for complex kernels. A SPIR module is structured as an module, containing global metadata, type definitions, constants, and function declarations specific to C. The top-level MODULE_BLOCK encapsulates the module's data layout (e.g., using triples like "spir-unknown-unknown" for 32-bit or "spir64-unknown-unknown" for 64-bit targets) and source triple. Sub-blocks include CONSTANTS_BLOCK for literal values, FUNCTION_BLOCK for kernel and helper functions, and a dedicated metadata section via the opencl.kernels named metadata node, which lists entry-point kernels with attributes such as work-group sizes, argument types, and vectorization hints. For example, a kernel might be annotated as !0 = metadata !{void (...)* @my_kernel, "my_kernel", i32 0, i32 0, i32 0, i32 0, i32 0, metadata !1, i32 0} where subsequent nodes detail argument address spaces (e.g., global as 1, local as 3). Functions in SPIR follow LLVM's SSA form, with instructions restricted to a subset compatible with OpenCL C, such as add, load, and store for arithmetic and memory operations, but excluding advanced features like invoke or most atomic intrinsics (replaced by OpenCL builtins). Types map directly: OpenCL scalars to LLVM integers/floats (e.g., int to i32), vectors to <n x type> (e.g., float4 to <4 x float>), and opaque types like images to structs (e.g., %opencl.image2d_t). Address spaces distinguish memory regions: 0 for private, 1 for global, 2 for constant, 3 for local, and 4 for generic. Built-in functions use C++ ABI , such as _Z3sinf for sin(float). The module ends with a string table for names and no explicit terminator, allowing streaming parsing similar to LLVM tools. For a simple OpenCL kernel computing a vector sum, the SPIR module would define types like i32 and <4 x i32>, declare the kernel function with spir_kernel calling convention, include metadata for arguments (e.g., pointer to global memory in address space 1), and contain basic blocks with loads, adds, and stores, all encoded in the bitstream for direct device loading via the cl_khr_spir extension.

Capabilities and Extensions

SPIR supports C features through a constrained subset of IR instructions and types, implicitly defining "capabilities" via the targeted version and version metadata (e.g., opencl.spir.version = !{i32 2, i32 0} for SPIR 2.0). Core capabilities include scalar and vector operations up to 16 elements, floating-point types (including half via cl_khr_fp16), and basic , but exclude features incompatible with , such as or certain intrinsics. Higher-level 2.0 features like sub-groups or are represented via specific constructs, with dependencies enforced by the frontend compiler. Extensions in SPIR are declared in module-level metadata, such as opencl.used.extensions listing KHR extensions (e.g., cl_khr_global_int32_base_atomics) or optional core features like double precision (cl_doubles). These declarations inform the runtime of required device capabilities, allowing rejection of unsupported modules. For example, the cl_khr_fp64 extension enables double types mapped to double, while vendor-specific extensions may add custom metadata or intrinsics, provided they adhere to the SPIR subset rules. Examples include the cl_khr_3d_image_writes extension, which permits write operations on 3D image types via decorated pointers, or atomic operations using builtins like atomic_add implemented as calls to mangled names. Validation ensures extension usage matches declared capabilities, promoting portability across devices while allowing vendors to extend via registered metadata without altering the core IR. However, heavy reliance on extensions can limit compatibility, as devices must explicitly support them per the conformance tests.

Validation and Debugging

Validation of SPIR modules combines LLVM's structural checks with -specific semantic rules to ensure compatibility and correctness. Structural validation, performed by tools like llvm-dis or llvm-verify, confirms well-formed bitcode, unique identifiers, type consistency, and adherence to SSA form, including proper function signatures and no invalid instructions (e.g., disallowing atomicrmw in favor of builtins). Semantic validation enforces C constraints, such as usage (no private pointers in global scope), vector size limits (up to 16 components), and built-in function availability based on extensions. Interface validation checks kernel metadata against expectations, like argument qualifiers and work-group hints matching device limits. These rules prevent runtime errors, such as memory access violations in restricted storage classes. The Khronos-provided Clang-based SPIR generator includes validation passes, and OpenCL drivers perform additional checks during clCreateProgramWithBinary. Common errors include mismatched types (e.g., using i64 without cl_khr_int64 ) or undeclared extensions for features like printf, which is supported via a mangled intrinsic int printf(constant char * restrict fmt, ... ). Tools output diagnostics for issues like invalid metadata or forward references, emphasizing early detection in development workflows. Debugging in SPIR leverages LLVM's debug info format, embedding source-level details without altering semantics. The !llvm.dbg.cu metadata node declares the compilation unit with source language (e.g., ), file names, and producer (e.g., ). Line information uses !DICompileUnit and !DILocation attachments to instructions, mapping IR operations back to OpenCL C lines and columns. Variable debugging employs !DIVariable for names and types, including OpenCL qualifiers like restrict or volatile. For kernels, metadata includes arg names and access qualifiers for traceability. Debug strings are stored in the module's string table, and tools like llvm-dis disassemble to readable IR with debug annotations. In OpenCL environments, runtime debuggers can use this info for breakpoints and inspection, though support varies by vendor. SPIR enhances debugging for features like shared via extended metadata.

Linking and Optimization

Linking SPIR modules uses LLVM's link-time optimization (LTO) framework, combining multiple bitcode files into a single module via tools like llvm-link. This resolves external references to functions and globals, merging type definitions and metadata while preserving OpenCL-specific annotations like kernel lists. Linkage types are restricted to internal, private, linkonce, or external, excluding Windows-specific dllimport/dllexport. The process handles specialization via metadata overrides, such as adjusting work-group sizes, and outputs a unified bitcode file for program creation. For libraries, exports can be retained for dynamic linking, though static linking is common for kernels. Optimization employs LLVM passes via opt, tailored for SPIR's subset to improve performance without violating OpenCL rules. Key passes include to remove unused kernels or blocks, and for built-ins, and inlining of helper functions marked linkonce. Loop optimizations use hints like opencl.loop.unroll_hint metadata to guide unrolling, while fast-math flags (nnan, ninf) enable aggressive floating-point simplifications if extensions allow. A legalizer pass can strip vendor extensions, converting to core equivalents for portability. For example, atomic builtins may be optimized to efficient LLVM sequences. Challenges include preserving semantics during interprocedural analysis and ensuring metadata integrity across linked modules. The resulting optimized bitcode reduces size and execution time, streamlining deployment in OpenCL 1.2/2.0 runtimes. In OpenCL workflows, linking and optimization occur before device compilation, with drivers like those from NVIDIA or AMD applying final passes. Tools such as Clang's -O3 -emit-llvm integrate SPIR generation with optimization, supporting heterogeneous targets via the cl_khr_spir extension.

Applications and Usage

In Compute Programming

The Standard Portable Intermediate Representation (SPIR) enables portable execution of OpenCL kernels in compute programming by providing a vendor-neutral, LLVM IR-based bitcode format for OpenCL 1.2 and 2.0 environments. Introduced with OpenCL 1.2 in 2012, SPIR allows developers to compile OpenCL C source into a compact, loadable module that can be submitted to the runtime via the cl_khr_spir extension, reducing compilation overhead and protecting kernel source code as intellectual property. This format maps OpenCL C constructs to a constrained subset of LLVM 3.2 IR (for SPIR 1.2) or LLVM 3.4 IR (for SPIR 2.0), using specific triples like "spir-unknown-unknown" for 32-bit targets and enforcing rules for data types, such as i32 for scalar integers and for vectors. SPIR's design supports forward compatibility with devices, efficient runtime loading of pre-compiled programs, and integration of vendor extensions without exposing source code, making it suitable for in legacy setups. Tools for generating SPIR include a modified compiler available in Khronos open-source repositories, which emits SPIR bitcode from C kernels for use in workflows targeting diverse accelerators like GPUs and CPUs. As of 2025, SPIR remains relevant primarily in legacy 1.2 and 2.0 implementations, such as older and drivers, though it has been largely superseded by SPIR-V for newer versions (2.1+) and broader ecosystems. Compute-specific features in SPIR derive from LLVM IR constraints tailored for OpenCL, including support for work-groups, barriers, and built-in functions like atomic operations and vector math, enabling parallel kernel execution without vendor-specific recompilation. These capabilities were applied in early tasks, such as scientific simulations and image processing on GPUs, where SPIR facilitated across hardware vendors. However, due to its LLVM dependency and lack of binary standardization, adoption waned after SPIR-V's introduction in 2015.

In Graphics Shaders

The original SPIR was not designed or used for shaders, as its scope was limited to OpenCL compute kernels and did not extend to APIs like or . shader portability and interchange were addressed by the successor SPIR-V, introduced in 2015, which supports shader stages in (mandatory since 2016) and optional extensions in 4.6. SPIR's LLVM IR foundation made it unsuitable for the binary format needs of graphics pipelines, leading to its exclusion from applications. For details on SPIR-V's graphics usage, see the Versions section on SPIR-V.

Cross-Compilation from High-Level Languages

Cross-compilation to SPIR from high-level languages like C enables targeting platforms without runtime source exposure, using SPIR as a portable intermediate for legacy environments. The primary workflow involves front-end compilation with a modified (based on 3.2 or 3.4) to generate SPIR bitcode, which can then be loaded via clCreateProgramWithBinary in 1.2+ runtimes supporting the cl_khr_spir extension. This process parses C into IR, applies SPIR-specific constraints (e.g., no unsupported LLVM features), and emits the bitcode module. For OpenCL C, the Khronos-provided Clang fork is the reference tool, invoked with flags like -target spir-unknown-unknown -emit-llvm to produce SPIR from kernel sources, ensuring compliance with OpenCL qualifiers and built-ins. Unlike SPIR-V, which supports multiple languages and APIs, SPIR cross-compilation is confined to OpenCL C and does not directly target graphics or other shading languages. Support for other high-level languages was limited, though experimental front-ends could map to OpenCL C before SPIR generation. Validation used LLVM tools to check the IR subset. Challenges in SPIR cross-compilation included LLVM version mismatches and the need for manual enforcement of OpenCL semantics, such as address spaces for global/ memory. As SPIR is legacy as of 2025, modern tools like have shifted to SPIR-V emission for 3.0, but archived repositories maintain SPIR support for in applications requiring 2.0 or earlier. No widespread ecosystem for decompiling SPIR exists, unlike SPIRV-Cross for SPIR-V.

References

Add your contribution
Related Hubs
User Avatar
No comments yet.