Function Attributes (#[inline] / #[no_inline] / #[cold])
Status: design note / decision recorded Last updated: 2026-05-02
formalang lets source authors annotate functions with codegen
hints — inline, no_inline, cold — and preserves them
through the IR as IrFunction.attributes: Vec<FunctionAttribute>.
This document records why the WebAssembly backend doesn't honor
those attributes today and the conditions under which the
decision should be revisited.
Decision: ignore function attributes; delegate all
inlining / placement decisions to wasm-opt. Revisit if a real
consumer surfaces a workload where the attributes' intent
materially differs from binaryen's heuristics.
Why ignoring is correct today
Wasm has no first-class equivalent for any of the three attributes:
Inline/NoInline: no wasm instruction or section marks a function as "always inline" or "never inline". Inlining decisions live entirely in the optimizer (binaryen, wasm-opt).Cold: no wasm equivalent of a.text.coldsection or branch- prediction hint. Wasm engines decide hot/cold placement internally based on profile data they collect at runtime.
Three options for the backend:
Option A — pass attributes through to wasm-opt
Binaryen has no documented input format for "the source author
asked to inline this function". The closest thing — naming a
function __attribute__((always_inline))-style — works for C/C++
inputs but doesn't have a wasm-encoder side. We'd have to pre-
process the module ourselves, mark functions, then run a custom
pass; binaryen's stock pipeline ignores all of it.
The result would either be code that pre-empts binaryen's inlining heuristics (rarely the right call — binaryen has good heuristics tuned for size-vs-speed) or code that adds marker custom sections binaryen doesn't read.
Option B — emit a custom section listing attributes
Ship a formawasm.attributes custom section keyed by function
index, listing the attribute set per function. Useful for tooling
that wants to introspect the original intent (debuggers, IDE
plugins). Doesn't actually affect codegen — binaryen still ignores
the marker, the engine still ignores it.
This is plausible but speculative: nobody currently reads such a section. It's also non-standard, so different tools would read different formats.
Option C — ignore and document
The IR field stays preserved through the pipeline (closure-conv,
DCE, fold). Backends that can express the attributes (LLVM IR
emitter would have inlinehint / coldcc, JVM emitter would
have nothing) honor them. The wasm backend doesn't, and says so.
This is what we're doing. Cost: zero. Benefit: zero. Risk: the implicit user expectation that "inline" means something. Mitigated by the source-language documentation explaining that backends choose how to honor hints.
What would change the call
If any of these surface, revisit:
- Profiling shows a real workload where binaryen's inlining
heuristic underperforms a manual hint by a measurable margin
(>5%). A
--respect-inline-hintscodegen flag could then pre-process the module to bias binaryen's choice. - A wasm proposal lands defining function attributes natively
(e.g. branch-hint sections like
wasi-nn's). Honoring would become a question of emitting the right wasm-spec-defined section. - A non-codegen consumer needs the attributes. A dataflow analyzer, profile-guided optimizer, or IDE plugin might want "user said inline" data alongside the wasm bytecode. A custom section with a documented format is the natural bridge.
Until then: the IR's attributes field is passed through for
the future, but never consulted at codegen time. No diagnostic
fires when a function is annotated; the annotation is silently
preserved in the IR shape and silently ignored by the wasm
backend.