Skip to main content

NIR (Morph Intermediate Representation)

NIR is the compiler's internal representation between the AST and final code generation.


Pipeline Position

Source → Lexer → Parser → AST → Semantic Analysis → NIR → { LLVM / native codegen | container bytecode (VCON stack) }

NIR sits between semantic analysis and backend-specific lowering. The LLVM pipeline lowers NIR to native code; the VCON stack lowers NIR toward bytecode/container artifacts when you use morph vcon … (target-dependent).

Who owns NIR behavior?

Most NIR lowers, optimizers, and backend-facing hooks are not handwritten as one giant switch in src/. They are registered from morphs/<Package>/:

  • feature.toml sections name components (for example component = "nir.core.input_lowering", component_class = "…") and point at source files.
  • The package morph.toml [build] list (nir_sources, codegen_sources, …) ensures those translation units compile into the package morph library.
  • Backend emit often appears as a route subsection (for example [core.print.route.hostllvm]) with provider_id, artifact_kind, and a dedicated Emit.cpp.

So when you debug or extend a NIR node kind, start from the owning package and its manifests—see how that node is spelled in Ops (nir_kind in [operation.*]) or in a Core built-in feature. For the full picture of manifests vs ABI, read Plugin-governed pipeline.


Viewing NIR

morph nir file.mx

What NIR Contains

  • Functions — Typed, validated method representations
  • Types — Resolved type information
  • Constants — Evaluated constant expressions
  • Instructions — Simplified operations (no sugar syntax)

Why NIR?

BenefitDescription
SharedBoth LLVM and VCON paths consume NIR
OptimizableOptimizations applied at NIR level benefit both paths
PortableLanguage-level IR independent of target platform
DebuggableHuman-readable with morph nir command

Next Steps