How to Extend Morph and Analysis
This guide covers the extension points that are active today:
- NIR optimizer morphs
- structural trail semantic components
- root lowering components
The default NIR optimizer is Morph-backed. Trail-owned lowering is also
Morph-backed, but core only provides a structural host. Domain rules stay in
morphs/<Domain>/.
NIR Optimizer Morph
1. Keep rewrite logic in an OptimizationPass
Optimizer morphs still execute normal OptimizationPass implementations so the
rewrite logic stays directly unit-testable.
#include "OptimizerInternal.h"
class MyPass final : public OptimizationPass {
public:
bool runOnModule(Module *module) override;
const char *getName() const override { return "My Pass"; }
};
2. Register the morph in a domain library
Do not hardcode default optimizer behavior in Optimizer.cpp or
src/nir/morph/MorphPipeline.cpp.
The canonical source of truth is:
morphs/<Domain>/morph.tomlmorphs/pipelines.tomlmorphs/<Domain>/plugin/*.cppmorphs/<Domain>/host/*.cpp
Each optimizer component must declare:
- stable
component_id domain_id- phase
optimizer - artifact pair
nir -> nir - capability list in the
core.*,domain.*, ortarget.*namespaces
The plugin descriptor is metadata. The executable in-tree runtime is wired through the optimizer host registry.
3. Add the component to morphs/pipelines.toml
The default optimizer order is not hardcoded in Optimizer.cpp.
If a morph must participate in the default optimizer pipeline, add its
component_id to morphs/pipelines.toml. Missing or incompatible entries are
fatal and covered by tests.
4. Add sources to CMake
If you add new source files under src/nir/ or morphs/, register them in the
relevant CMakeLists.txt files.
5. Verify
morphc.bat test "Morph|Nir|Optimizer"
Structural Trail Lowering
Lowering is no longer authored as string-driven host callbacks and it is no
longer backed by Input-specific sema types in core.
The current canonical path is:
- raw parser AST remains generic
- core builds a structural
TrailView morphs/<Domain>/features/<Feature>/feature.tomldefines root and chain signatures- plugin semantic components build an opaque
TrailFact - the root lowering component emits NIR from that fact
Input is the first implemented slice of this model.
1. Keep core structural
Core owns only generic infrastructure:
TrailView- semantic dispatch
- lowering dispatch
- global type registry
- global builtin registry
Core does not own Input semantics, chain legality, or root-specific type
rules.
2. Define the schema in feature.toml
Describe the surface in morphs/<Domain>/features/<Feature>/feature.toml.
The active schema uses:
[<semantic.family>]- optional
[<semantic.family>.repl] - nested
[<semantic.family>.chain.<name>] - operator surfaces such as
[tensor.add]withoperator = "operator.add" - route sections such as
[tensor.add.route.hostllvm] generic_*param_*returnsbuiltin_familysourcecomponentcomponent_class
Manifest identifiers are compiled into manifest/runtime ids during load. Author code should not dispatch on those names as raw strings.
3. Put semantic ownership in plugin components
Root and chain semantic components live in domain sources such as:
morphs/Core/features/Input/Input.cppmorphs/Core/features/Input/chain/Default.cppmorphs/Core/features/Input/chain/TimeoutMs.cppmorphs/Core/features/Input/repl/Behavior.cpp
Root components live inside their own directory. That directory is the scope
anchor for related chain/ and optional repl/ components. Do not flatten
root sources back to morphs/<Domain>/Lowering/<Root>.cpp; the chain loader
assumes the root folder layout.
The root component creates the initial TrailFact. Chain components validate
and update that fact. Final NIR emission happens only in the root lowering
component.
4. Lower from typed facts, not raw syntax
Lowering code should consume:
TrailFactresultType()- global type metadata
- global builtin family resolution
These are explicitly out of bounds in lowering code:
stage.method == "Min"style dispatchInput-specific parser or sema node types in core- raw type-name string mapping such as
int -> target kind component_id-keyed lowering dispatch
5. Verify
Use the morph, NIR, parser, semantic, and REPL coverage together:
morphc.bat test "Morph|Nir|Parser|Semantic|ReplPipeline"
Legacy Notes
- Directly editing the default optimizer order in
Optimizer.cppis legacy. - Lowering host executors keyed by
component_idare removed for the trail lowering path. - New Morph-owned lowering work should follow the structural trail + plugin-owned semantic chain model.