iridia-ir
iridia's shader IR: typed SSA values and operations, in a function body that is either a tree of structured regions or a control flow graph with block parameters, beside the interface vocabulary a program exposes (varyings, resources, stage facts, D3D provenance).
Part of iridia, which turns compiled shaders into programs other tools can read and write. Readers lift into this IR; passes (iridia-passes) rewrite it; writers (iridia-naga) lower it; iridia-exec-ir runs it by its own definitions. The design is docs/design/ir.md. This crate is no_std with alloc and does no I/O.
What it holds
- A
Program: its entryFunction,Interface,Conventions(clip depth, window and texture origin),FloatEnv(denormal controls per float type),Provenance(format, target, lifter and version, source hash, the compatibility policies the lifter chose) and notes. - A
Functionowns its values (ValueDef: aType, anOp, a sourceOrigin, aPrecisionpermission such as D3D9's_pp) and its variables (source registers until promotion). ItsBodyisRegions(If,BlockandLoopconstructs whose regions end in a yield, a labelled break or continue carrying values, a return, a kill or unreachable) orCfg(blocks with parameters, ending in a jump, a two-way branch, a return, a kill or unreachable). - Operations whose meaning differs between sources carry the rule as an enum and have no default:
MinMaxNan,SaturateNan,MulZero(IEEE or D3D9's zero-wins multiply),OnZero,FloatToInt,Rounding(with ties-away for D3D9mova),OutOfBounds,Level. - Interface inputs and outputs are read and written by lane mask (
LoadInput(slot, lanes),StoreOutput(slot, lanes, value)); a lane never stored is zero. Fixed-function state the code depends on is a declared resource (ResourceKind::PipelineState, aStateParamsuch as D3D9's projected-texture flag) read byLoadState. Node::Demotediscards a pixel and lets it go on as a helper of its quad (D3D'stexkillanddiscard, WGSL'sdiscard); a kill also ends it.validatechecks types, the dominance of every use (regions by scope, graphs by dominator tree; a block nothing reaches is dominated by every block, and its code is still checked), labels and exits, the interface each load and store names, and the stage's rules (a kill, a demotion, a derivative and an implicit-level sample are pixel-only). It returns every problem it finds.text::printwrites a function one line per node, andtext::parsereads it back: printing what it reads gives the same text. The first line names the schema version; the text is for fixtures, diffs and notes, not a storage format.
Example
use ;
let mut b = new;
let x = b.value;
let two = b.f32;
let y = b.op.unwrap;
b.store_output;
let f = b.finish.unwrap;
let text = print;
assert_eq!;
let back = parse.unwrap;
assert_eq!;
Guarantees and limits
- A program the validator accepts has one meaning for every input, including NaN, infinities, signed zero and out-of-range values, where its operations' enums say what the source does; the reference for that meaning is
iridia-exec-ir. - The text form states everything an operation means but not source origins, and a value the body never places is not written.
- The operation set is what the readers on it need so far (Direct3D 9 shader model 3); it grows with them.
Licence
GPL-3.0-or-later.