An Intermediate Representation (IR) is an abstract, platform-independent data structure or code used within a compiler to represent a program between its source form and its final executable form. For machine learning, the IR typically encodes the model's computational graph, including operations (ops), tensors, and data dependencies. This abstraction enables a suite of hardware-agnostic graph optimizations—like constant folding, operator fusion, and dead code elimination—before targeting specific silicon.
Glossary
Intermediate Representation (IR)

What is Intermediate Representation (IR)?
An intermediate representation (IR) is the core data structure used by compilers to represent a program, such as a neural network's computational graph, between its source form and its final executable machine code.
The IR serves as the central interface between the frontend (framework like PyTorch) and backend (hardware like an NPU). High-level IRs are expressive and close to the source, while lower-level IRs are more restrictive and closer to machine code through a process called graph lowering. This multi-stage design allows compilers like TVM, XLA, and MLIR to perform sophisticated transformations for optimal execution, separating logical model design from physical deployment constraints.
Key Characteristics of an IR
An Intermediate Representation (IR) is the core data structure within a compiler that bridges a high-level model definition and low-level machine code. Its design dictates the efficiency and portability of the entire compilation pipeline.
Abstraction Level
An IR's abstraction level defines its distance from source code or hardware. High-Level IRs (HIR) preserve rich semantic information (e.g., loops, function calls) for architecture-agnostic optimizations. Mid-Level IRs (MIR) are often control-flow graphs suitable for analysis and transformation. Low-Level IRs (LIR) resemble assembly, exposing hardware details like registers and instruction selection. Compilers like LLVM use a tiered system, progressively lowering from a HIR to a LIR.
Graph Structure
Most modern ML IRs represent computation as a Directed Acyclic Graph (DAG). Nodes represent operations (ops) or tensors, and edges represent data dependencies. This structure enables:
- Topological sorting for correct execution order.
- Easy analysis of data flow and operator lifetimes for static memory planning.
- Efficient application of transformations like operator fusion and dead code elimination.
- Frameworks like TensorFlow (GraphDef), PyTorch (TorchScript), and ONNX use this graph paradigm.
Platform Independence
A core purpose of an IR is to decouple model definition from hardware execution. A platform-independent IR (e.g., ONNX, MLIR's dialect-ir) allows:
- A single model definition to target CPUs, GPUs, NPUs, and microcontrollers.
- Hardware vendors to develop specialized compiler backends that lower the IR to their native instructions.
- Enables retargeting without modifying the original model, a key tenet of frameworks like Apache TVM and Google's XLA.
Extensibility & Dialects
Advanced IRs provide mechanisms to model diverse computational domains. MLIR pioneered the dialect system, where the IR can be extended with custom operations, types, and attributes. This allows:
- A Neural Network dialect for high-level ML ops.
- An LLVM dialect for low-level CPU/GPU code.
- Domain-specific dialects for linear algebra, affine loops (polyhedral model), or quantum computing.
- Dialects can be progressively lowered and legalized to a final target, enabling complex, multi-stage compilation.
Optimization Passes
The IR is the substrate for compiler passes—transformations that analyze and rewrite the graph. Common passes include:
- Canonicalization: Simplifies patterns to a standard form.
- Constant Folding: Pre-computes operations on constant values.
- Common Subexpression Elimination (CSE): Removes redundant calculations.
- Algebraic Simplification: Applies mathematical identities (e.g., x*1 => x). Passes can be greedy, pattern-based, or guided by a cost model, and are often applied in a specific pipeline.
Static vs. Dynamic
This axis defines when graph structure is finalized. Static IR (e.g., TensorFlow 1.x, ONNX) fixes the graph topology and tensor shapes ahead-of-time (AOT), enabling aggressive optimization. Dynamic IR (e.g., PyTorch's eager mode) is built at runtime, offering flexibility. Modern systems use tracing or scripting to capture dynamic execution into a static graph, or employ just-in-time (JIT) compilation to specialize a dynamic graph based on runtime shapes (shape inference).
How an IR Works in an ML Compiler Pipeline
An intermediate representation (IR) is the core data structure that enables the transformation of a neural network from a high-level framework definition into optimized, executable code for target hardware.
An Intermediate Representation (IR) is an abstract, platform-independent data structure or code used within a compiler to represent a program, such as a neural network's computational graph, between its source form and its final executable form. It serves as the central artifact for all graph optimizations, including operator fusion, constant folding, and dead code elimination, allowing transformations to be applied uniformly before targeting specific hardware.
The IR undergoes a process of graph lowering, where high-level operations are decomposed into sequences of lower-level, hardware-specific instructions. This multi-level abstraction enables hardware-agnostic optimizations at higher IR levels and performance-tuning at lower levels, facilitating efficient code generation for diverse backends like CPUs, GPUs, and specialized Neural Processing Units (NPUs) through mechanisms like hardware delegates.
Common IRs in Machine Learning Systems
An Intermediate Representation (IR) is the abstract, platform-independent data structure used by compilers to represent a program between its source form and final executable code. In ML systems, different IRs serve distinct roles in the optimization pipeline.
High-Level Graph IR
A High-Level Graph IR represents a neural network as a computational graph of abstract operators, independent of hardware specifics. This is the initial representation after importing a model from frameworks like PyTorch or TensorFlow.
- Purpose: Enables hardware-agnostic graph-level optimizations like constant folding, dead code elimination, and operator fusion.
- Examples: ONNX (Open Neural Network Exchange) Graph, TensorFlow GraphDef, PyTorch FX Graph, and Relay IR (from Apache TVM).
- Characteristics: Nodes represent coarse-grained operations (e.g.,
Conv2D,MatMul); tensors have abstract data types and symbolic shapes.
Lowered IR / Kernel IR
A Lowered IR is the result of graph lowering, where high-level operators are decomposed into primitive, target-specific operations. It bridges the gap between abstract computation and hardware execution.
- Purpose: Exposes low-level details for backend-specific optimizations like loop tiling, vectorization, and efficient memory layout planning.
- Examples: MLIR's Affine Dialect for loop nests, Halide IR for image processing pipelines, TensorFlow XLA's HLO IR after target-specific lowering, and LLVM IR for CPU targets.
- Characteristics: Includes memory buffers, explicit loops, and address calculations. It is often linear or in Static Single Assignment (SSA) form.
Hardware-Specific IR
A Hardware-Specific IR is a final, vendor-optimized representation that maps directly to the execution model of a particular accelerator, such as a GPU, NPU, or DSP.
- Purpose: Encodes instructions, memory hierarchies, and parallelism models (e.g., warps, workgroups) unique to the target silicon. Enables kernel auto-tuning and optimal operator dispatch.
- Examples: NVIDIA PTX (Parallel Thread Execution), CUDA Graphs, Google TPU's HLO (after further TPU lowering), ARM Compute Library's Graph IR, and proprietary NPU compiler IRs (e.g., Qualcomm SNPE, Apple Core ML).
- Characteristics: Contains explicit parallel constructs, synchronization primitives, and hardware intrinsic functions.
Quantized & Sparse IRs
Specialized IRs encode optimizations for model compression, representing networks after quantization or pruning. They are crucial for efficient on-device inference.
- Purpose: Explicitly represents low-precision data types (e.g.,
int8,uint4) and sparse tensor encoding formats (e.g., CSR, Block-Sparse) to enable integer-only kernels and skip zero computations. - Examples: TFLite's FlatBuffer schema with quantized operators, ONNX with
QuantizeLinear/DequantizeLinearnodes, MLIR's TOSA dialect with quantization attributes, and frameworks using the Sparse Tensor Core extension in LLVM. - Optimization: Quantization folding is a key pass that merges fake quantization nodes with linear ops in these IRs.
Polyhedral IR
A Polyhedral IR uses mathematical polyhedra to model loop nests and their data dependencies, enabling powerful automatic transformations for compute-intensive, regular kernels like convolutions and matrix multiplications.
- Purpose: Provides a unified model for complex loop optimizations such as loop tiling, fusion, skewing, and interchange while preserving correctness.
- Examples: MLIR's Affine Dialect, the ISL (Integer Set Library) framework, and the core IR of the Polyhedral Model used in compilers like Pluto.
- Use Case: Critical for optimizing linear algebra operations on CPUs and GPUs, guided by a cost model and the Roofline model to achieve peak performance.
Runtime IR / JIT IR
A Runtime IR or JIT IR is an intermediate representation that is constructed, optimized, and compiled during program execution. It enables dynamic optimizations based on runtime information.
- Purpose: Allows for Just-In-Time (JIT) compilation where optimizations like shape specialization, kernel selection, and graph rewriting can use concrete tensor shapes and values known only at runtime.
- Examples: PyTorch's TorchScript IR (for JIT), TensorFlow's XLA JIT compilation path, Apache TVM's runtime module, and Triton's JIT IR for GPU programming.
- Advantage: Balances optimization potential with flexibility, avoiding the need for Ahead-Of-Time (AOT) compilation to handle all possible input variations.
Comparing Levels of Intermediate Representation
This table compares the characteristics of high-level, mid-level, and low-level intermediate representations (IRs) used in machine learning compilers and inference frameworks.
| Characteristic | High-Level IR (HIR) | Mid-Level IR (MIR) | Low-Level IR (LIR) |
|---|---|---|---|
Abstraction Level | Graph-Level | Operator-Level | Instruction-Level |
Primary Representation | Computational Graph (e.g., ONNX, TensorFlow GraphDef) | Three-Address Code / Static Single Assignment (SSA) | Machine-Specific Instructions / Assembly |
Hardware Dependence | Fully Hardware-Agnostic | Partially Hardware-Aware | Highly Hardware-Specific |
Optimizations Performed | Graph Transformations (Fusion, Pruning), Algebraic Simplifications | Loop Optimizations, Memory Planning, Common Subexpression Elimination | Register Allocation, Instruction Scheduling, Peephole Optimizations |
Typical Input | Framework-Specific Model (PyTorch, TensorFlow) | High-Level IR (HIR) | Mid-Level IR (MIR) |
Typical Output | Mid-Level IR (MIR) | Low-Level IR (LIR) | Machine Code / Executable Binary |
Key Compiler Passes | Canonicalization, Shape Inference, Constant Folding | Vectorization, Loop Tiling, Static Memory Planning | Instruction Selection, Software Pipelining |
Human Readability | High (Semantic, close to source) | Moderate (Structured, but verbose) | Low (Machine-oriented, terse) |
Example Formats/Systems | ONNX, Relay (TVM), XLA HLO, TorchScript | LLVM IR, MLIR (affine, linalg dialects), TIR (TVM) | LLVM Machine IR, SPIR-V, Vendor-Specific ISA (e.g., CUDA PTX, NPU VLIW) |
Frequently Asked Questions
An intermediate representation (IR) is the abstract, platform-independent data structure used by compilers to represent a program—like a neural network's computational graph—between its source form and its final executable code. This section answers key questions about its role in machine learning compilation and optimization.
An Intermediate Representation (IR) is an abstract, platform-independent data structure or code used within a compiler to represent a program, such as a neural network's computational graph, between its source form (e.g., a PyTorch model) and its final executable form for a target hardware (e.g., CPU, GPU, NPU). It serves as the central, canonical format for performing analyses and transformations, enabling optimizations like operator fusion, constant folding, and dead code elimination without being tied to a specific frontend framework or backend hardware. In ML compilers like TVM, XLA, or MLIR, the IR captures the model's operations, data dependencies, and tensor shapes in a graph or hierarchical structure that is amenable to automated rewriting and lowering.
Enabling Efficiency, Speed & Accuracy
Intelligent Analysis, Decision & Execution
We build AI systems for teams that need search across company data, workflow automation across tools, or AI features inside products and internal software.
Talk to Us
Search across company data
Give teams answers from docs, tickets, runbooks, and product data with sources and permissions.
Useful when people spend too long searching or get different answers from different systems.

Automate internal workflows
Use AI to route work, draft outputs, trigger actions, and keep approvals and logs in place.
Useful when repetitive work moves across multiple tools and teams.

Add AI to products and internal tools
Build assistants, guided actions, or decision support into the software your team or customers already use.
Useful when AI needs to be part of the product, not a separate tool.
Related Terms
Intermediate Representation (IR) is the core data structure enabling compiler transformations. These related terms describe the specific optimization passes and analyses that operate on the IR to produce efficient executable code.
Graph Lowering
The process of progressively transforming a high-level, hardware-agnostic IR into a lower-level, target-specific IR or machine code. This involves mapping abstract operations to concrete hardware instructions and memory layouts.
- High-Level IR (HIR): Captures model architecture and operator semantics.
- Mid-Level IR (MIR): Platform-independent, optimized for analysis and transformations.
- Low-Level IR (LIR): Close to machine code, includes register allocation and instruction selection.
- Target: Enables a single model definition (e.g., PyTorch) to run on diverse backends like CPU, GPU, or NPU.
Operator Fusion
A compiler optimization that combines multiple sequential operations in a computational graph into a single, compound kernel. This reduces intermediate memory writes and kernel launch overhead.
- Fusion Patterns: Common fusions include element-wise operations (e.g., ReLU, Add) fused into a preceding convolution or matrix multiplication.
- Memory Bandwidth: Primary benefit is reduced DRAM traffic by keeping intermediate results in faster cache or registers.
- Kernel Launch Overhead: Eliminates the latency of scheduling multiple small GPU or NPU kernels.
- Example: Fusing a Conv2D, BatchNorm, and ReLU layer into one
ConvBNReLUkernel.
Constant Folding
A compile-time optimization that evaluates and replaces subgraphs comprised entirely of constants with their precomputed result tensor. This eliminates runtime computation.
- Static Computation: Moves work from runtime to compile-time.
- Graph Simplification: Removes nodes, making subsequent optimizations like dead code elimination more effective.
- Common Targets: Fixed tensor manipulations, shape calculations, and static control flow conditions.
- Example: The expression
add(constant(5), constant(3))is replaced withconstant(8)in the IR.
Dead Code Elimination
An optimization pass that identifies and removes operations whose outputs do not contribute to the final output of the computational graph. This reduces both computation and memory usage.
- Liveness Analysis: Determines if a tensor's value is ever read by a node that reaches the graph's output.
- Side Effects: Operations with side effects (e.g., printing, variable updates) are typically not eliminated.
- Prunes Unused Branches: Can remove entire subgraphs from unused conditional branches.
- Impact: Critical for cleaning graphs after other transformations like constant folding.
Common Subexpression Elimination
An optimization that identifies redundant calculations of identical expressions and replaces duplicate computations with a reference to a single cached result.
- Expression Matching: The compiler must prove two subgraphs are functionally identical.
- Memory vs. Compute Trade-off: Stores the result of the first computation, trading memory for reduced FLOPs.
- Scope: Can be applied locally within a basic block or globally across the entire graph.
- Example: If
sin(x)is computed in two separate branches of a conditional, CSE computes it once and reuses the value.
Shape Inference
The process of propagating and determining the dimensions (shape) and data types (dtype) of all tensors in a computational graph based on the known properties of input nodes.
- Forward Propagation: Rules for each operator define output shape given input shapes (e.g., Conv2D output channels).
- Prerequisite for Optimization: Enables static memory planning, efficient kernel selection, and validation.
- Dynamic Shapes: Some frameworks support partial shape inference where dimensions can be dynamic (symbolic).
- Error Detection: Catches shape mismatches early (e.g., attempting to multiply incompatible matrices).

About the author
Prasad Kumkar
CEO & MD, Inference Systems
Prasad Kumkar is the CEO & MD of Inference Systems and writes about AI systems architecture, LLM infrastructure, model serving, evaluation, and production deployment. Over 5+ years, he has worked across computer vision models, L5 autonomous vehicle systems, and LLM research, with a focus on taking complex AI ideas into real-world engineering systems.
His work and writing cover AI systems, large language models, AI agents, multimodal systems, autonomous systems, inference optimization, RAG, evaluation, and production AI engineering.
Partnered with leading AI, data, and software stack.
How We Work
Custom AI workflows for your Business
One-fit-all AI don't work for modern businesses. At Inferensys, we aim to understand your business & custom requirements; which we use to define most efficient agentic workflows, the data, and the tools for your business.
01
Review the use case
We understand the task, the users, and where AI can actually help.
Read more02
Pick the right approach
We define what needs search, automation, or product integration.
Read more03
Build the first useful version
We implement the part that proves the value first.
Read more04
Improve from there
We add the checks and visibility needed to keep it useful.
Read moreThe first call is a practical review of your use case and the right next step.
Talk to Us