IR and XIR Pipeline

SkillDev tools

Legacy IR and XIR compiler pipeline, AST lowering, SSA IR, and optimization passes.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the IR and XIR Pipeline skill

What this skill tells your AI

The instructions your AI receives, as published by luisagroup/luisacompute in .agents/skills/ir_pipeline/SKILL.md and read by ahel’s review.

Two IRs, both starting from AST (src/ast/), feeding into backend codegen:

IR (Legacy)XIR (Preferred)
Locationsrc/ir/, include/luisa/ir/src/xir/, include/luisa/xir/
ImplRust (src/rust/)Pure C++
Serializationast2json → Rust IRxir2json/json2xir (yyjson)
SSAYesYes (mem2reg)
StatusMaintained (compat)Active development
Basic BlocksYesYes

Pipeline Flow

DSL Tracing (src/dsl/) → AST (src/ast/)
                              │
                    ┌─────────┴─────────┐
                    ▼                   ▼
              XIR (ast2xir)       IR (ast2ir → JSON → Rust FFI)
                    │                   │
                    └─────────┬─────────┘
                              ▼
                    Backend Codegen (src/backends/<name>/)
                              │
                              ▼
                    GPU Execution (src/runtime/)

Rust IR path: src/rust/luisa_compute_ir/ does autodiff, DCE, SSA, vectorize. XIR path: Pure C++ with ast2xir translator, xir2ast round-trip, and optimization passes.

AST → IR Translation (Legacy)

File: src/ir/ast2ir.cpp

AST Function → to_json() → JSON string → Rust FFI → CArc<KernelModule/CallableModule>

FFI: luisa_compute_ir_ast_json_to_ir_kernel(), ..._callable(), ..._type().

Key IR classes (Rust, via C FFI): KernelModule, CallableModule, Node, Instruction (Local, Call, Phi, Loop, If, Switch, RayQuery, AdScope), Type, BasicBlock.

AST → XIR Translation

Files: src/xir/translators/ast2xir.cpp, xir2ast.cpp. AST2XIRContext maps AST variables to XIR Values, tracks break/continue targets, handles autodiff adjoints, caches constants.

Expression Mapping

ASTXIR
UnaryExprArithmeticOp (UNARY_MINUS, UNARY_BIT_NOT); +x is elided
BinaryExprArithmeticOp (BINARY_ADD, etc.); matrix-aware; logic ops cast to bool
MemberExprEXTRACT/SHUFFLE or GEP
AccessExprGEP + LOAD
LiteralExprConstant
ConstantExprConstant
RefExprVariable lookup or SpecialRegister
CallExprCallInst, ArithmeticOp, AtomicOp, Resource*Op, ThreadGroupOp, RayQuery*Op, Assert/Assume/Unreachable/RasterDiscard
CastExprCastInst (STATIC_CAST, BITWISE_CAST)
TypeIdExpr/StringIdExpr/FuncRefExprNot implemented

Statement Mapping

ASTXIR
IfStmtIfInst + true/false/merge
SwitchStmtSwitchInst + case/default/merge
ForStmtLoopInst (prepare/body/update/merge)
LoopStmtSimpleLoopInst (do-while)
BreakStmtBreakInst
ContinueStmtContinueInst
ReturnStmtReturnInst
AssignStmtStoreInst
ExprStmtExpression (terminator-aware, e.g. Unreachable)
AutoDiffStmtAutodiffScopeInst
RayQueryStmtRayQueryLoopInst + RayQueryDispatchInst
PrintStmtPrintInst
DebugBreakStmtDebugBreakInst
CommentStmtCollected as comment metadata on following instruction

XIR Core Architecture

Value Hierarchy

Value
├── GlobalValue
│   ├── Function → FunctionDefinition → KernelFunction (entry+block size), CallableFunction, ExternalFunction
│   ├── Constant (literals)
│   ├── Undefined
│   └── SpecialRegister (SPR_ThreadID, SPR_BlockID, SPR_DispatchID, SPR_WarpLaneID, SPR_KernelID, SPR_BlockSize, SPR_WarpSize, SPR_DispatchSize, SPR_ObjectID, SPR_Barycentrics, ...)
├── FunctionScopeValue
│   ├── BasicBlock (instruction container)
│   └── Argument → ValueArgument / ReferenceArgument / ResourceArgument
└── BlockScopeValue → Instruction
    ├── TerminatorInstruction: BranchInst, ConditionalBranchInst, IfInst, SwitchInst,
    │   LoopInst, SimpleLoopInst, ReturnInst, BreakInst, ContinueInst, UnreachableInst, RasterDiscardInst
    └── Non-terminator instructions

Key Classes

  • Module (include/luisa/xir/module.h): container for globals, unique constants via hash
  • Function / FunctionDefinition (include/luisa/xir/function.h): ArgumentList, BasicBlockList, body_block(), traversal orders (PRE_ORDER, POST_ORDER, ...). Traversal: traverse_basic_blocks(), traverse_instructions()
  • Argument (include/luisa/xir/argument.h): ValueArgument, ReferenceArgument, ResourceArgument
  • BasicBlock (include/luisa/xir/basic_block.h): InstructionList, is_terminated(), terminator(), traverse_predecessors(), traverse_successors()
  • Instruction (include/luisa/xir/instruction.h): DerivedInstruction<>, is_terminator(), control_flow_merge(), clone(), intrinsic_identifier()
  • Value & Use (include/luisa/xir/value.h, use.h): SSA UseList, replace_all_uses_with(), is_lvalue() for alloca/gep/reference args

XIR Instruction Set

Control Flow

InstructionBlocks
IfInsttrue, false, merge
SwitchInstcases, default, merge
LoopInstprepare, body, update, merge
SimpleLoopInstbody, merge
BranchInsttarget
ConditionalBranchInsttrue, false
ReturnInst
BreakInst / ContinueInsttarget (lowered before codegen)
UnreachableInst

Memory

AllocaInst (LOCAL/SHARED), LoadInst, StoreInst, GEPInst

SSA

PhiInst — (block, value) pairs

Call / Cast

CallInst (user/external functions), CastInst (STATIC_CAST, BITWISE_CAST)

Arithmetic (ArithmeticOp)

  • Unary: UNARY_MINUS, UNARY_BIT_NOT
  • Binary: ADD, SUB, MUL, DIV, MOD, BIT_AND, BIT_OR, BIT_XOR, SHIFT_LEFT/RIGHT, ROTATE_LEFT/RIGHT, comparisons
  • Logic/Selection: ALL, ANY, SELECT, STEP
  • Math: ABS, MIN, MAX, CLAMP, SATURATE, LERP, SMOOTHSTEP, trig (SIN/COS/TAN/ASIN/ACOS/ATAN/ATAN2 and hyperbolic variants), exp/log families (EXP/EXP2/EXP10/LOG/LOG2/LOG10), POW, SQRT, RSQRT, FMA, COPYSIGN, CLZ, CTZ, POPCOUNT, REVERSE, ISINF, ISNAN, CEIL, FLOOR, FRACT, TRUNC, ROUND, RINT
  • Vector: DOT, CROSS, LENGTH, LENGTH_SQUARED, NORMALIZE, FACEFORWARD, REFLECT, REDUCE_SUM/PRODUCT/MIN/MAX, OUTER_PRODUCT
  • Matrix: MATRIX_COMP_NEG/ADD/SUB/MUL/DIV, MATRIX_LINALG_MUL, MATRIX_DETERMINANT, MATRIX_TRANSPOSE, MATRIX_INVERSE
  • Aggregate: AGGREGATE, SHUFFLE, EXTRACT, INSERT

Resource

ResourceQueryOp, ResourceReadOp, ResourceWriteOp — buffer/texture/bindless ops, ray-tracing queries, indirect dispatch, device-address loads/stores

Atomic (AtomicOp)

EXCHANGE, COMPARE_EXCHANGE, FETCH_ADD/SUB/AND/OR/XOR/MIN/MAX

Thread Group & Ray Query & Autodiff

ThreadGroupOp (warp, sync, SER, quad derivatives), RayQueryLoopInst, RayQueryDispatchInst, RayQueryObjectReadInst, RayQueryObjectWriteInst, RayQueryPipelineInst, AutodiffScopeInst, AutodiffIntrinsicInst (requires_gradient, gradient, gradient_marker, accumulate_gradient, backward, detach)

Debug / Utility

PrintInst, ClockInst, DebugBreakInst, AssertInst, AssumeInst, OutlineInst, RasterDiscardInst

XIR Optimization Passes

Location: src/xir/passes/ (headers in include/luisa/xir/passes/)

Core / SSA / CFG

PassFilePurpose
DCEdce.cppDead instructions, unreachable blocks, dead allocas, static branch eval
Mem2Regmem2reg.cppAlloca→SSA via dominance tree/frontiers, PHI insertion
Dominance Treedom_tree.cppImmediate dominators + frontiers
Post-Dominance Treepost_dom_tree.cppPost-dominance analysis
Early Return Elimearly_return_elimination.cppEarly returns → structured control flow
Lower Break/Continuelower_break_continue.cppLower break/continue to explicit branches
Lower Ray Query Looplower_ray_query_loop.cppRay query loop lowering
Lower Ray Query Loop → Looplower_ray_query_loop_to_loop.cppConvert ray query loops to plain loops
Destructure CFGdestructure_cfg.cppFlatten structured CFG to basic branches
Restructure CFGrestructure_cfg.cppRecover structured control flow
Outlineoutline.cppFunction outlining
Phi Cleanupphi_cleanup.cppRemove trivial/duplicate PHI nodes
Fix Self-Referentialfix_self_referential.cppBreak self-referential PHI/value cycles
If Conversionif_conversion.cppConvert simple diamonds to select/min/max

Analysis

PassFilePurpose
Call Graphcall_graph.cppCall-graph construction
Pointer Usagepointer_usage.cppPer-field kill/touch/live analysis for pointers
Lexical Scopelex_scope_analysis.cppScope region analysis
Aggregate Field Bitmaskaggregate_field_bitmask.cppBitmask tracking for aggregate fields
Alias Analysisalias_analysis.cppMay-/must-alias queries for memory instructions
Convergence Regionconvergence_region.cppDivergence/convergence region analysis
CVPcvp.cppCorrelated value propagation via structured branches
Scalar Evolutionscalar_evolution.cppSCEV for loop induction variables
Uniformity Analysisuniformity_analysis.cppUniform/divergent value analysis

Scalar / Peephole / Global

PassFilePurpose
Algebraic Simplifyalgebraic_simplify.cppAlgebraic identities (with optional fast-math)
Const Foldconst_fold.cppConstant folding
Early CSEearly_cse.cppEarly common subexpression elimination
GVNgvn.cppGlobal value numbering
Reassociatereassociate.cppReassociate expressions for CSE/folding
SCCPsccp.cppSparse conditional constant propagation
Simplify CFGsimplify_cfg.cppRemove empty/trivial blocks
Simplify Libcallssimplify_libcalls.cppSimplify known builtin calls
Scalarizerscalarizer.cppBreak vector ops into scalar ops
Div-Rem Pairsdiv_rem_pairs.cppCombine division/remainder pairs
Dead Arg Elimdead_arg_elim.cppRemove unused arguments

Loop

PassFilePurpose
IndVar Simplifyindvar_simplify.cppSimplify induction variables
LICMlicm.cppLoop-invariant code motion
Loop Rotationloop_rotation.cppRotate loops for simpler CFG

Memory / Local

PassFilePurpose
SROAsroa.cppScalar replacement of aggregates
Reg2Memreg2mem.cppRegister → memory conversion
Promote Ref Argpromote_ref_arg.cppReference argument promotion
Transpose GEPtranspose_gep.cppTranspose GEP through loads/stores
Trace GEPtrace_gep.cppGEP analysis & tracing
Local Load Eliminationlocal_load_elimination.cppRedundant load elimination
Local Store Forwardlocal_store_forward.cppStore-to-load forwarding
Dead Store Eliminationdead_store_elimination.cppRemove dead stores

AD / Interprocedural

PassFilePurpose
Autodiffautodiff.cppAutodiff transformations
Inlineinline.cppFunction inlining
Unused Callable Removalunused_callable_removal.cppDead function elimination

Pass Pipeline Helpers

pass_pipeline.cpp / include/luisa/xir/passes/pass_pipeline.h provides PassPipeline, PassReport, and factory functions:

  • create_basic_optimization_pipeline()
  • create_post_inline_cleanup_pipeline()
  • create_ssa_optimization_pipeline()
  • create_post_restructure_cleanup_pipeline()

Control Flow Representation

XIR uses structured control flow with explicit merge blocks:

IfInst:   condition, true_block, false_block, merge_block
LoopInst: prepare_block, body_block, update_block, merge_block

Design: maintains SSA, enables structured transforms, maps well to GPU shaders, and supports PHI nodes at merges. SwitchInst is a first-class structured terminator. Only the explicit destructure_cfg boundary maps it to raw IndexedBranchInst; restructure_cfg reconstructs the switch and its merge. There is no generic switch-lowering or XIR loop-unroll pass. Autodiff's private bounded semantic expansion and SPIRV-Tools loop unrolling are separate mechanisms with separate contracts.

Metadata

Headers: include/luisa/xir/metadata.h plus include/luisa/xir/metadata/{name,location,comment,curve_basis}.h. Types: NAME, LOCATION, COMMENT, CURVE_BASIS. Applied via MetadataListMixin.

JSON Serialization / Translators

  • IR (legacy): ast2json → Rust IR, C API luisa_compute_ir_ast_json_to_ir_*
  • XIR: src/xir/translators/xir2json.cpp, json2xir.cpp, xir2ast.cpp, xir2text.cpp — yyjson-based module serialization and AST round-tripping, useful for cross-process transport and debugging

Key Design Patterns

  1. Intrusive ListsManagedIntrusiveList for node management
  2. CRTPDerivedValue<>, DerivedInstruction<>, DerivedFunction<>, DerivedArgument<>
  3. Visitortraverse_basic_blocks(), traverse_instructions()
  4. BuilderXIRBuilder for instruction creation
  5. MixinMetadataListMixin, ControlFlowMergeMixin, InstructionOpMixin, PrintMessageMixin
  6. Use-Def ChainsUse objects track value users for SSA

Adding a New XIR Pass

  1. Create src/xir/passes/<name>.cpp + header include/luisa/xir/passes/<name>.h
  2. Register in src/xir/CMakeLists.txt
  3. Convention: accept Module & or Function &, return an info struct (often with *_pass_run_on_module(Module *, PassReport *report = nullptr)), use XIRBuilder, call replace_all_uses_with() for substitution

Signals

GitHub stars
1k
Forks
108
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
ir-pipeline
Source
github.com/luisagroup/luisacompute