yum-mirror/slang
Making it easier to work with shaders
git clone https://git.yummers.dev/yum-mirror/slang
f9f23cbd9
master
Slang IR Instruction Management and Versioning
This document explains how Slang's intermediate representation (IR) instructions are defined, generated, and versioned. It covers the workflow for adding or modifying instructions and the mechanisms that ensure backwards compatibility for serialized IR modules.
High-Level Concepts
The Slang IR uses a code generation approach where instruction definitions are centralized in a Lua file (slang-ir-insts.lua), and various C++ headers and source files are generated from this single source of truth. This ensures consistency across the codebase and enables sophisticated features like backwards compatibility through stable instruction naming.
Key Components
- Instruction Definitions (
slang-ir-insts.lua): The canonical source for all IR instruction definitions - Stable Names (
slang-ir-insts-stable-names.lua): Maps instruction names to permanent integer IDs for backwards compatibility - Code Generation (via Fiddle): Generates C++ enums, structs, and tables from the Lua definitions
- Module Versioning: Tracks compatibility ranges for serialized IR modules
The Instruction Definition System
Source of Truth: slang-ir-insts.lua
All IR instructions are defined in source/slang/slang-ir-insts.lua. This file contains a hierarchical table structure that defines:
- Instruction names and their organization into categories
- Struct names for the C++ representation (if different from the default)
- Flags like
hoistable,parent,global, etc. - (Optionally) Minimum operand counts
- (Optionally) The operands themselves
- Parent-child relationships in the instruction hierarchy
Here's a simplified example of how instructions are defined:
local insts ={ { nop ={} } , { Type ={ { BasicType ={ hoistable =true , { Void ={ struct_name ="VoidType" } } , { Bool ={ struct_name ="BoolType" } } , { Int ={ struct_name ="IntType" } } , -- ... more basic types } , } , -- ... more type categories } , } , -- ... more instruction categories }
The hierarchy is important: instructions inherit properties from their parent categories. For example, all BasicType instructions inherit the hoistable = true flag.
Code Generation Flow
The Fiddle tool processes slang-ir-insts.lua and generates several outputs:
-
Enum Definitions (
slang-ir-insts-enum.h):IROpenum with values likekIROp_Void,kIROp_Bool, etc.- Range markers like
kIROp_FirstBasicTypeandkIROp_LastBasicType
-
Struct Definitions (
slang-ir-insts.h):- C++ struct definitions for instruction types not manually defined
leafInst()andbaseInst()macros for RTTI support- If operands of an IR are specified in
slang-ir-insts.luain the format{ { "operand1_name", "operand1_type" }, {"operand2_name"} }and so on, Fiddle will generate getters for each of the operands as part of the IR's struct. Note that the order in which the operands are listed matters and specification of the type of the operand is optional; defaulting to "IRInst" when the type is not specified.
-
Instruction Info Table (
slang-ir-insts-info.cpp):- Maps opcodes to their string names, operand counts, and flags
- Used for debugging, printing, and validation
-
Stable Name Mappings (
slang-ir-insts-stable-names.cpp):- Bidirectional mapping between opcodes and stable IDs
- Critical for backwards compatibility
Adding or Modifying Instructions
Adding a New Instruction
To add a new IR instruction:
-
Edit
slang-ir-insts.lua: Add your instruction in the appropriate category:{ MyNewInst = { min_operands = 2 } } , -
Run the build: The build system will automatically regenerate the C++ files.
-
Update the stable names: Either
-
Run the validation script:
Note: Skip make command if lua is already built.
make -C external/lua MYCFLAGS="-DLUA_USE_POSIX" MYLIBS="" ./external/lua/lua extras/check-ir-stable-names.lua update -
Or add a new ID to the mapping in
source/slang/slang-ir-insts-stable-names.lua, this is checked for consistency in CI so it's safe to add manually.
This assigns a permanent ID to your new instruction.
-
-
Implement the instruction logic: Add handling in relevant files like:
slang-ir-insts.h(if you need a custom struct definition)slang-emit-*.cppfiles for code generationslang-ir-lower-*.cppfiles for transformations
-
Update the module version: In
slang-ir.h, incrementk_maxSupportedModuleVersion:const static UInt k_maxSupportedModuleVersion = 1 ;// was 0
Modifying an Existing Instruction
Modifications require more care:
-
Adding operands or changing semantics: This is a breaking change. You must:
- Increment both
k_minSupportedModuleVersionandk_maxSupportedModuleVersion - Document the change in the version history
- Increment both
-
Renaming: Don't rename instructions directly. Instead:
- Add the new instruction
- Mark the old one as deprecated
- Eventually remove it in a major version bump
The Stable Name System
Purpose
When Slang serializes IR modules, it needs to handle the case where the compiler version that reads a module is different from the one that wrote it. Instructions might have been added, removed, or reordered in the IROp enum.
The stable name system solves this by assigning permanent integer IDs to each instruction. These IDs never change once assigned.
How It Works
-
Assignment: When a new instruction is added, the
check-ir-stable-names.luascript assigns it the next available ID. -
Serialization: When writing a module, opcodes are converted to stable IDs:
auto stableName = getOpcodeStableName (value ); -
Deserialization: When reading, stable IDs are converted back:
value = getStableNameOpcode (stableName ); -
Validation: The CI system ensures the stable name table stays synchronized with the instruction definitions.
Maintenance
The stable name table is validated in CI:
./extras/check-ir-stable-names-gh-actions.sh
This script:
- Verifies all instructions have stable names
- Checks for duplicate IDs
- Ensures the mapping is bijective
- Can automatically fix missing entries
Module Versioning
Version Types
Slang tracks two version numbers:
-
Module Version (
IRModule::m_version): The semantic version of the IR instruction set- Range:
k_minSupportedModuleVersiontok_maxSupportedModuleVersion - Stored in each serialized module
- Range:
-
Serialization Version (
IRModuleInfo::serializationVersion): The format version- Allows changes to how data is encoded
When to Update Versions
Minor Version Bump (increment k_maxSupportedModuleVersion only):
- Adding new instructions
- Adding new instruction flags that don't affect existing code
- Adding new optional operands
Major Version Bump (increment both min and max):
- Removing instructions
- Changing instruction semantics
- Modifying minimum operand counts or types
- Any change that breaks compatibility
Version Checking
During deserialization:
if (fossilizedModuleInfo -> serializationVersion != IRModuleInfo ::kSupportedSerializationVersion )return SLANG_FAIL ;// Later, after loading instructions: if (hasUnrecognizedInsts )return SLANG_FAIL ;
Serialization Details
The Flat Representation
For efficiency, IR modules are serialized as a "flat" representation:
struct FlatInstTable {List < InstAllocInfo > instAllocInfo ;// Op + operand count List < Int64 > childCounts ;// Children per instruction List < SourceLoc > sourceLocs ;// Source locations List < Int64 > operandIndices ;// Flattened operand references List < Int64 > stringLengths ;// For string/blob constants List < uint8_t > stringChars ;// Concatenated string data List < UInt64 > literals ;// Integer/float constant values };
This representation:
- Minimizes pointer chasing during deserialization
- Groups similar data together for better cache performance
- Enables efficient bulk operations
Traversal Order
Instructions are serialized in a specific order for performance:
traverseInstsInSerializationOrder (moduleInst , [& ](IRInst * inst ) {// Process instruction });
The traversal:
- Visits instructions in preorder (parent before children)
- Optionally reorders module-level instructions to group constants together
- Maintains deterministic ordering for reproducible builds
Debugging and Validation
Available Tools
-
Module Info Inspection:
slangc -get-module-info module.slang-moduleShows module name, version, and compiler version.
-
Version Query:
slangc -get-supported-module-versions Reports the supported version range.
-
IR Dumping:
slangc -dump-ir module.slangShows the IR in human-readable form.
Common Issues
"Unrecognized instruction" errors: The module contains instructions unknown to this compiler version. Update Slang or recompile the module.
Stable name validation failures: Run the update script and commit the changes:
Note: Skip make command if lua is already built.
make -C external/lua MYCFLAGS="-DLUA_USE_POSIX" MYLIBS="" ./external/lua/lua extras/check-ir-stable-names.lua update
Version mismatch: The module was compiled with an incompatible Slang version. Check the version ranges and recompile if necessary.
Best Practices
-
Always update stable names: After adding instructions, run the validation script before committing.
-
Document version changes: When bumping module versions, add a comment explaining what changed.
-
Prefer addition over modification: When possible, add new instructions rather than changing existing ones.
-
Group related changes: If making multiple breaking changes, do them together in a single version bump.
1# Slang IR Instruction Management and Versioning 2 3This document explains how Slang's intermediate representation (IR) instructions are defined, generated, and versioned. It covers the workflow for adding or modifying instructions and the mechanisms that ensure backwards compatibility for serialized IR modules. 4 5## High-Level Concepts 6 7The Slang IR uses a code generation approach where instruction definitions are centralized in a Lua file (`slang-ir-insts.lua`), and various C++ headers and source files are generated from this single source of truth. This ensures consistency across the codebase and enables sophisticated features like backwards compatibility through stable instruction naming. 8 9### Key Components 10 11- **Instruction Definitions** (`slang-ir-insts.lua`): The canonical source for all IR instruction definitions 12- **Stable Names** (`slang-ir-insts-stable-names.lua`): Maps instruction names to permanent integer IDs for backwards compatibility 13- **Code Generation** (via Fiddle): Generates C++ enums, structs, and tables from the Lua definitions 14- **Module Versioning**: Tracks compatibility ranges for serialized IR modules 15 16## The Instruction Definition System 17 18### Source of Truth: `slang-ir-insts.lua` 19 20All IR instructions are defined in `source/slang/slang-ir-insts.lua`. This file contains a hierarchical table structure that defines: 21 22- Instruction names and their organization into categories 23- Struct names for the C++ representation (if different from the default) 24- Flags like `hoistable`, `parent`, `global`, etc. 25- (Optionally) Minimum operand counts 26- (Optionally) The operands themselves 27- Parent-child relationships in the instruction hierarchy 28 29Here's a simplified example of how instructions are defined: 30 31``` lua 32local insts = { 33{ nop = {} } , 34{ 35Type = { 36{ 37BasicType = { 38hoistable = true , 39{ Void = { struct_name = "VoidType" } } , 40{ Bool = { struct_name = "BoolType" } } , 41{ Int = { struct_name = "IntType" } } , 42-- ... more basic types 43} , 44} , 45-- ... more type categories 46} , 47} , 48-- ... more instruction categories 49} 50``` 51 52The hierarchy is important: instructions inherit properties from their parent categories. For example, all `BasicType` instructions inherit the `hoistable = true` flag. 53 54### Code Generation Flow 55 56The Fiddle tool processes `slang-ir-insts.lua` and generates several outputs: 57 581. **Enum Definitions** (`slang-ir-insts-enum.h`): 59 60- `IROp` enum with values like `kIROp_Void`, `kIROp_Bool`, etc. 61- Range markers like `kIROp_FirstBasicType` and `kIROp_LastBasicType` 62 632. **Struct Definitions** (`slang-ir-insts.h`): 64 65- C++ struct definitions for instruction types not manually defined 66- `leafInst()` and `baseInst()` macros for RTTI support 67- If operands of an IR are specified in `slang-ir-insts.lua` in the format `{ { "operand1_name", "operand1_type" }, {"operand2_name"} }` and so on, 68Fiddle will generate getters for each of the operands as part of the IR's struct. Note that the order in which the operands are listed matters and 69specification of the type of the operand is optional; defaulting to "IRInst" when the type is not specified. 70 713. **Instruction Info Table** (`slang-ir-insts-info.cpp`): 72 73- Maps opcodes to their string names, operand counts, and flags 74- Used for debugging, printing, and validation 75 764. **Stable Name Mappings** (`slang-ir-insts-stable-names.cpp`): 77- Bidirectional mapping between opcodes and stable IDs 78- Critical for backwards compatibility 79 80## Adding or Modifying Instructions 81 82### Adding a New Instruction 83 84To add a new IR instruction: 85 861. **Edit `slang-ir-insts.lua`**: Add your instruction in the appropriate category: 87 88```lua 89{ MyNewInst = { min_operands = 2 } } , 90``` 91 922. **Run the build**: The build system will automatically regenerate the C++ files. 93 943. **Update the stable names**: Either 95 96- Run the validation script: 97 98**Note**: Skip make command if lua is already built. 99```bash 100make -C external/lua MYCFLAGS= "-DLUA_USE_POSIX" MYLIBS= "" 101./external/lua/lua extras/check-ir-stable-names.lua update 102``` 103 104- Or add a new ID to the mapping in `source/slang/slang-ir-insts-stable-names.lua`, this is checked for consistency in CI so it's safe to add manually. 105 106This assigns a permanent ID to your new instruction. 107 1084. **Implement the instruction logic**: Add handling in relevant files like: 109 110- `slang-ir-insts.h` (if you need a custom struct definition) 111- `slang-emit-*.cpp` files for code generation 112- `slang-ir-lower-*.cpp` files for transformations 113 1145. **Update the module version**: In `slang-ir.h`, increment `k_maxSupportedModuleVersion`: 115```cpp 116const static UInt k_maxSupportedModuleVersion = 1 ; // was 0 117``` 118 119### Modifying an Existing Instruction 120 121Modifications require more care: 122 123- **Adding operands or changing semantics**: This is a breaking change. You must: 124 1251. Increment both `k_minSupportedModuleVersion` and `k_maxSupportedModuleVersion` 1262. Document the change in the version history 127 128- **Renaming**: Don't rename instructions directly. Instead: 129 1301. Add the new instruction 1312. Mark the old one as deprecated 1323. Eventually remove it in a major version bump 133 134## The Stable Name System 135 136### Purpose 137 138When Slang serializes IR modules, it needs to handle the case where the compiler version that reads a module is different from the one that wrote it. Instructions might have been added, removed, or reordered in the `IROp` enum. 139 140The stable name system solves this by assigning permanent integer IDs to each instruction. These IDs never change once assigned. 141 142### How It Works 143 1441. **Assignment**: When a new instruction is added, the `check-ir-stable-names.lua` script assigns it the next available ID. 145 1462. **Serialization**: When writing a module, opcodes are converted to stable IDs: 147 148```cpp 149auto stableName = getOpcodeStableName ( value ); 150``` 151 1523. **Deserialization**: When reading, stable IDs are converted back: 153 154```cpp 155value = getStableNameOpcode ( stableName ); 156``` 157 1584. **Validation**: The CI system ensures the stable name table stays synchronized with the instruction definitions. 159 160### Maintenance 161 162The stable name table is validated in CI: 163 164``` bash 165./extras/check-ir-stable-names-gh-actions.sh 166``` 167 168This script: 169 170- Verifies all instructions have stable names 171- Checks for duplicate IDs 172- Ensures the mapping is bijective 173- Can automatically fix missing entries 174 175## Module Versioning 176 177### Version Types 178 179Slang tracks two version numbers: 180 1811. **Module Version** (`IRModule::m_version`): The semantic version of the IR instruction set 182 183- Range: `k_minSupportedModuleVersion` to `k_maxSupportedModuleVersion` 184- Stored in each serialized module 185 1862. **Serialization Version** (`IRModuleInfo::serializationVersion`): The format version 187- Allows changes to how data is encoded 188 189### When to Update Versions 190 191**Minor Version Bump** (increment `k_maxSupportedModuleVersion` only): 192 193- Adding new instructions 194- Adding new instruction flags that don't affect existing code 195- Adding new optional operands 196 197**Major Version Bump** (increment both min and max): 198 199- Removing instructions 200- Changing instruction semantics 201- Modifying minimum operand counts or types 202- Any change that breaks compatibility 203 204### Version Checking 205 206During deserialization: 207 208``` cpp 209if ( fossilizedModuleInfo -> serializationVersion != IRModuleInfo :: kSupportedSerializationVersion ) 210return SLANG_FAIL ; 211 212// Later, after loading instructions: 213if ( hasUnrecognizedInsts ) 214return SLANG_FAIL ; 215``` 216 217## Serialization Details 218 219### The Flat Representation 220 221For efficiency, IR modules are serialized as a "flat" representation: 222 223``` cpp 224struct FlatInstTable 225{ 226List < InstAllocInfo > instAllocInfo ; // Op + operand count 227List < Int64 > childCounts ; // Children per instruction 228List < SourceLoc > sourceLocs ; // Source locations 229List < Int64 > operandIndices ; // Flattened operand references 230List < Int64 > stringLengths ; // For string/blob constants 231List < uint8_t > stringChars ; // Concatenated string data 232List < UInt64 > literals ; // Integer/float constant values 233}; 234``` 235 236This representation: 237 238- Minimizes pointer chasing during deserialization 239- Groups similar data together for better cache performance 240- Enables efficient bulk operations 241 242### Traversal Order 243 244Instructions are serialized in a specific order for performance: 245 246``` cpp 247traverseInstsInSerializationOrder( moduleInst , [ & ]( IRInst * inst ) { 248// Process instruction 249}); 250``` 251 252The traversal: 253 2541. Visits instructions in preorder (parent before children) 2552. Optionally reorders module-level instructions to group constants together 2563. Maintains deterministic ordering for reproducible builds 257 258## Debugging and Validation 259 260### Available Tools 261 2621. **Module Info Inspection**: 263 264```bash 265slangc -get-module-info module.slang-module 266``` 267 268Shows module name, version, and compiler version. 269 2702. **Version Query**: 271 272```bash 273slangc -get-supported-module-versions 274``` 275 276Reports the supported version range. 277 2783. **IR Dumping**: 279```bash 280slangc -dump-ir module.slang 281``` 282Shows the IR in human-readable form. 283 284### Common Issues 285 286**"Unrecognized instruction" errors**: The module contains instructions unknown to this compiler version. Update Slang or recompile the module. 287 288**Stable name validation failures**: Run the update script and commit the changes: 289 290**Note**: Skip make command if lua is already built. 291``` bash 292make -C external/lua MYCFLAGS= "-DLUA_USE_POSIX" MYLIBS= "" 293./external/lua/lua extras/check-ir-stable-names.lua update 294``` 295 296**Version mismatch**: The module was compiled with an incompatible Slang version. Check the version ranges and recompile if necessary. 297 298## Best Practices 299 3001. **Always update stable names**: After adding instructions, run the validation script before committing. 301 3022. **Document version changes**: When bumping module versions, add a comment explaining what changed. 303 3043. **Prefer addition over modification**: When possible, add new instructions rather than changing existing ones. 305 3064. **Group related changes**: If making multiple breaking changes, do them together in a single version bump.