# How Sway's Inline IR Optimization Pass Improves Performance: A Deep Dive

> Discover how Sway's inline IR optimization pass boosts performance by eliminating call overhead and enabling further optimizations. Learn more about this deep dive into FuelLabs/sway.

- Repository: [Fuel Labs/sway](https://github.com/FuelLabs/sway)
- Tags: deep-dive
- Published: 2026-03-05

---

**Sway's inline IR optimization pass improves performance by replacing function call instructions with the callee's body, eliminating call overhead and enabling downstream optimizations like constant folding and dead code elimination.**

The FuelLabs/sway compiler uses an intermediate representation (IR) to perform aggressive optimizations before generating bytecode for the FuelVM. Sway's inline IR optimization pass is a module-level transformation that directly impacts execution speed and binary size by intelligently flattening function hierarchies.

## What the Inline IR Optimization Pass Does

The inline pass operates on Sway's IR in [`sway-ir/src/optimize/inline.rs`](https://github.com/FuelLabs/sway/blob/main/sway-ir/src/optimize/inline.rs), transforming `Call` instructions into direct instruction sequences.

### Pass Registration and Call Graph Analysis

The pass is registered as a module pass via `create_fn_inline_pass` (lines [24-33](https://github.com/FuelLabs/sway/blob/master/sway-ir/src/optimize/inline.rs#L24-L33)). Before inlining, it collects call counts for every function in the module to identify frequently invoked routines (lines [80-98](https://github.com/FuelLabs/sway/blob/master/sway-ir/src/optimize/inline.rs#L80-L98)).

### The Inline Heuristic Decision Logic

The `inline_heuristic` function determines whether a specific call site should be inlined based on several criteria (lines [100-140](https://github.com/FuelLabs/sway/blob/master/sway-ir/src/optimize/inline.rs#L100-L140)):

- **Never-inline**: Functions annotated with `#[inline(never)]` are always skipped
- **Always-inline**: Functions marked `#[inline(always)]` are unconditionally inlined
- **Single-call sites**: Functions called only once are always inlined (lines [121-124](https://github.com/FuelLabs/sway/blob/master/sway-ir/src/optimize/inline.rs#L121-L124))
- **Size threshold**: Small functions with ≤12 instructions are inlined by default (lines [126-130](https://github.com/FuelLabs/sway/blob/master/sway-ir/src/optimize/inline.rs#L126-L130))

### Function Call Inlining Implementation

The pass walks the call graph in callee-first order, ensuring callees are inlined before their callers (lines [135-139](https://github.com/FuelLabs/sway/blob/master/sway-ir/src/optimize/inline.rs#L135-L139)). For each function, `inline_some_function_calls` identifies candidate call sites and forwards them to `inline_function_call` (lines [161-170](https://github.com/FuelLabs/sway/blob/master/sway-ir/src/optimize/inline.rs#L161-L170)).

The `inline_function_call` routine (starting at line [560](https://github.com/FuelLabs/sway/blob/master/sway-ir/src/optimize/inline.rs#L560)) performs the actual transformation:
- Splits the caller basic block at the call site
- Removes the `Call` instruction
- Maps callee locals and arguments to caller values
- Copies callee instructions into the caller
- Updates block and value references
- Deletes the original call instruction

## Performance Benefits of Sway's Inline IR Optimization

Eliminating function calls through Sway's inline IR optimization pass creates cascading performance improvements:

- **Removes call overhead**: Eliminates stack frame setup, argument passing, and return handling costs by replacing `Call` + `Ret` pairs with direct instruction execution
- **Enables dead code elimination**: Once inlined, unused arguments and locals become visible to the DCE pass, allowing removal of unreachable code
- **Improves constant propagation**: Constant arguments at call sites can be folded immediately into the inlined body, reducing runtime computation
- **Facilitates scalar replacement**: Exposing small function internals allows the SROA pass to decompose structs and arrays into scalar registers
- **Reduces instruction cache pressure**: Hot small functions become contiguous instruction sequences, improving locality and reducing cache misses
- **Preserves developer control**: Attributes like `#[inline(always)]` and `#[inline(never)]` allow explicit performance tuning without source modification

## Practical Examples and Usage

### Registering the Pass in a Pipeline

To include the inline pass in a custom optimization pipeline, use the `PassManager` API as demonstrated in [`test/src/ir_generation/mod.rs`](https://github.com/FuelLabs/sway/blob/main/test/src/ir_generation/mod.rs):

```rust
use sway_ir::{
    create_fn_inline_pass, PassManager, PassGroup,
};

let mut pass_mgr = PassManager::default();
let mut group = PassGroup::default();

// Register the inline pass
let inline = pass_mgr.register(create_fn_inline_pass());
group.append_pass(inline);

// Run the pass on an IR module `ir`
let _ = pass_mgr.run(&mut ir, &group);

```

This pattern mirrors the test harness implementation (lines [16-23](https://github.com/FuelLabs/sway/blob/master/test/src/ir_generation/mod.rs#L16-L23)).

### Using Inline Attributes in Sway Code

Control inlining behavior directly in Sway source using attributes parsed in [`sway-parse/src/item/item_fn.rs`](https://github.com/FuelLabs/sway/blob/main/sway-parse/src/item/item_fn.rs):

```sway
#[inline(always)]
fn add_one(x: u64) -> u64 {
    x + 1
}

#[inline(never)]
fn debug_trace() {
    // This will never be inlined, preserving call stack
}

```

The `metadata_to_inline` function (lines [43-73](https://github.com/FuelLabs/sway/blob/master/sway-ir/src/optimize/inline.rs#L43-L73)) extracts these attributes and converts them to IR metadata that the heuristic checks.

### Before and After IR Transformation

Consider a simple function call before inlining:

```ir
// Before inline pass
fn main() -> u64 {
    entry:
    %0 = call @add_one(%arg0)
    ret u64 %0
}

```

After the inline pass processes this module, the call disappears:

```ir
// After inline pass
fn main() -> u64 {
    entry:
    %0 = add %arg0, 1u64
    ret u64 %0
}

```

The `Call` instruction and associated overhead are replaced by a single `add` instruction, demonstrating how the pass eliminates abstraction penalties.

## Key Source Files and Implementation Details

Understanding the inline pass requires familiarity with these specific locations in the FuelLabs/sway repository:

- **[`sway-ir/src/optimize/inline.rs`](https://github.com/FuelLabs/sway/blob/main/sway-ir/src/optimize/inline.rs)**: Contains the complete implementation including `create_fn_inline_pass`, `inline_heuristic`, `inline_function_call`, and `metadata_to_inline` ([source](https://github.com/FuelLabs/sway/blob/master/sway-ir/src/optimize/inline.rs))
- **[`sway-ir/src/pass_manager.rs`](https://github.com/FuelLabs/sway/blob/main/sway-ir/src/pass_manager.rs)**: Defines the `PassManager` and `PassGroup` APIs used to orchestrate the inline pass within optimization pipelines ([source](https://github.com/FuelLabs/sway/blob/master/sway-ir/src/pass_manager.rs))
- **[`test/src/ir_generation/mod.rs`](https://github.com/FuelLabs/sway/blob/main/test/src/ir_generation/mod.rs)**: Demonstrates how the inline pass is enabled in tests via the `optimisation_inline` flag and `PassManager` registration ([source](https://github.com/FuelLabs/sway/blob/master/test/src/ir_generation/mod.rs))
- **[`sway-parse/src/item/item_fn.rs`](https://github.com/FuelLabs/sway/blob/main/sway-parse/src/item/item_fn.rs)**: Handles parsing of `#[inline(...)]` attributes from Sway source code into metadata consumable by the IR pass ([source](https://github.com/FuelLabs/sway/blob/master/sway-parse/src/item/item_fn.rs))

## Summary

Sway's inline IR optimization pass delivers measurable performance improvements by:

- **Eliminating call overhead** through direct instruction substitution in [`sway-ir/src/optimize/inline.rs`](https://github.com/FuelLabs/sway/blob/main/sway-ir/src/optimize/inline.rs)
- **Enabling cross-function optimizations** like constant propagation and dead code elimination
- **Applying intelligent heuristics** that balance code size against speed using call counts and instruction thresholds
- **Respecting developer intent** via `#[inline(always)]` and `#[inline(never)]` attributes
- **Processing callees before callers** to maximize inlining opportunities through callee-first call graph traversal

## Frequently Asked Questions

### What is the default size threshold for inlining in Sway?

By default, Sway's inline heuristic inlines functions containing 12 or fewer instructions. This threshold is checked in `inline_heuristic` at lines [126-130](https://github.com/FuelLabs/sway/blob/master/sway-ir/src/optimize/inline.rs#L126-L130) of [`sway-ir/src/optimize/inline.rs`](https://github.com/FuelLabs/sway/blob/main/sway-ir/src/optimize/inline.rs). Functions larger than this limit are only inlined if marked with `#[inline(always)]` or called exactly once.

### How does Sway handle recursive function inlining?

The inline pass prevents infinite recursion by tracking the call graph and avoiding inlining cycles. When `inline_function_call` processes a call site, it checks whether the callee has already been inlined into the current call stack. This protection ensures that recursive functions maintain their call structure rather than causing infinite expansion during the optimization phase.

### Can I disable inlining for specific functions?

Yes, Sway provides the `#[inline(never)]` attribute to explicitly prevent inlining. When the parser encounters this attribute in [`sway-parse/src/item/item_fn.rs`](https://github.com/FuelLabs/sway/blob/main/sway-parse/src/item/item_fn.rs), it stores the metadata that `metadata_to_inline` (lines [43-73](https://github.com/FuelLabs/sway/blob/master/sway-ir/src/optimize/inline.rs#L43-L73)) checks during the heuristic phase. Functions marked with this attribute will always retain their call instructions regardless of size or call frequency.

### Where does the inline pass fit in Sway's compilation pipeline?

The inline pass runs as a module-level optimization after initial IR generation but before backend code generation. In the test harness ([`test/src/ir_generation/mod.rs`](https://github.com/FuelLabs/sway/blob/main/test/src/ir_generation/mod.rs) lines [16-23](https://github.com/FuelLabs/sway/blob/master/test/src/ir_generation/mod.rs#L16-L23)), it is registered via `create_fn_inline_pass()` and executed through the `PassManager` alongside other optimizations. This placement ensures that inlined code can be further optimized by subsequent passes like constant propagation and dead code elimination before final bytecode emission.