milkir

    Reusable Cranelift-like SSA intermediate representation

    compiler
    ir
    ssa
    optimization
    Download zip
    Author
    Version
    0.15.0
    License
    Apache-2.0
    Last updated
    17 days ago
    Downloads
    271

    Dependencies

    #MilkIR

    MilkIR is a reusable, target-independent intermediate representation for compiler middle ends. A frontend translates source operations into MilkIR, optimization passes simplify the MilkIR function, and Milky2018/milkir/native streams it directly into a native target selector.

    Target-independent means that MilkIR operations do not encode a particular instruction set or calling convention; it does not mean that every data width is target-configurable. MilkIR pointer/reference carriers are always 64-bit: Ptr, Ref, CallableRef, and OpaqueRef each have a fixed 64-bit representation. A backend for a non-64-bit target would require an explicit IR contract change rather than interpreting these types using the host pointer width.

    frontend IR or bytecode | v MilkIR SSA -- verify and optimize here | v Target VCode -- select instructions directly, then allocate registers

    #Start with one small function

    Suppose the source program contains this function:

    add_one(x: i32) -> i32 = x + 1

    The following test builds the same function in MilkIR:

    ///|
    test "build add_one" {
    let builder = FunctionBuilder::FunctionBuilder("add_one")

    let x = builder.add_param(I32)
    builder.add_result(I32)

    let one = builder.iconst_i32(1)
    let answer = builder.iadd(x, one)
    builder.return_([answer])

    let func = builder.finalize()
    inspect(func.verify(), content="()")
    inspect(
    func.print(),
    content=(
    #|function add_one(v0:i32) -> i32 {
    #|block0:
    #| v1:i32 = iconst 1
    #| v2:i32 = iadd v0, v1
    #| return v2
    #|}
    #|
    ),
    )
    }

    Read the generated IR from top to bottom:

    function add_one(v0:i32) -> i32 { function parameter v0 has type i32 block0: execution starts in block0 v1:i32 = iconst 1 create the constant 1 v2:i32 = iadd v0, v1 add x and 1, producing a new value return v2 return that value }

    finalize() verifies the current function before returning it and raises VerifyError for malformed SSA, CFG, or generic instruction contracts. For extension instructions, generic verification checks only the dialect/opcode envelope and the explicit operand/result signature; the owning adapter validates dialect semantics. Finalization is a validation checkpoint, not a freeze operation: the returned Function remains mutable and does not carry a persistent "verified" state. get_function() remains the explicit escape hatch for in-progress construction and transformation code. Callers may continue transforming either value, but every consuming adapter must verify the function after its final mutation and immediately before lowering it.

    The important detail is that x, one, and answer are not runtime integers in the MoonBit program that builds the IR. They are MilkIR Values: typed handles that name values in the function being compiled.

    #The six concepts to know

    ConceptMeaningIn add_one
    FunctionOne compilable function, including its signature and blocks.add_one
    TypeThe static type of an IR value.I32
    ValueA typed name for a function parameter, block parameter, or instruction result.v0, v1, v2
    BlockA straight-line sequence of instructions with one exit.block0
    InstAn operation that may produce one or more values.iconst, iadd
    TerminatorThe final control-flow operation in a block.return

    #DFG and layout ownership

    Function is the sole owner of instruction data and value definitions. A Block contains only the ordered InstId layout for that data; moving or deleting an instruction changes the layout without renumbering the function's instruction arena. Deleted instructions become lazy tombstones, while the verifier rejects arena instructions that are neither placed nor explicitly deleted. This keeps Value and InstId identities stable across ordinary transformations without adding work to the normal instruction-construction path.

    The instruction layout is intentionally not a mutable public array. Use block.instruction_count() and func.block_instruction_at(block, index) for individual inspection. Consumers that traverse a complete block without mutating it can use func.block_instruction_ids(block) to obtain a zero-copy, read-only view, then resolve each ID with func.instruction_by_id(id). The view remains valid only until that block's layout changes.

    This replaces the pre-0.15 interface that exposed Block.instructions : Array[Inst]. Producers should continue to place instructions through FunctionBuilder or Block::append_inst; optimization passes must use the checked layout mutation methods.

    FunctionBuilder is the normal construction API. It creates an entry block automatically and keeps track of the block that receives new instructions. For a straight-line function, the usual order is:

    1. Create a builder.
    2. Declare function parameters and result types.
    3. Emit instructions.
    4. End the current block with return_, jump, a branch, or trap.
    5. Call finalize, then verify the function.

    Lower-level producers that already have a complete Signature can use Function::with_signature. It eagerly materializes every declared function parameter in Function.params; retrieve those values with func.param(index) before emitting instructions. Function::signature() returns a snapshot derived from the same explicit parameter and result arrays. Do not recreate parameters with new_value(): that method allocates instruction results, block values, and other values after the declared function parameters.

    #Opcode families

    Every instruction belongs to one semantic family. Opcode has no source-language instruction variants or target-machine operations of its own:

    FamilyResponsibility
    Scalar(ScalarOp)Constants, arithmetic, comparisons, conversions, selection, and copies.
    Memory(MemoryOp)Full-width and narrow loads and stores over an explicit base and offset value.
    Call(CallOp)Direct external-symbol calls and function-pointer calls with explicit contracts.
    Vector(VectorOp)Language-neutral V128 lane, arithmetic, comparison, conversion, and effective-address memory operations.
    GlobalValue(GlobalValue)A function-scoped, adapter-owned context field with an explicit type, stability, and alias region.
    Ext(ExtOp, Signature)Typed operations whose semantics belong to a separately owned dialect.

    Frontends must consume source-only metadata before constructing a core instruction. For example, a WebAssembly frontend resolves a SIMD memory index, alignment hint, and immediate offset while computing the effective address; MilkIR receives that address and the vector load/store semantics. A frontend uses Ext only when the operation genuinely requires dialect-owned validation and lowering.

    #Embedding context fields

    GlobalValue models a typed value loaded from an embedding-provided context, such as a linear-memory base pointer. Its declaration is interned in the Function; the instruction names that declaration and takes the context pointer explicitly. MilkIR does not know the field offset or runtime layout. The owning dialect validates the opaque ContextField, and its native-lowering adapter resolves it to an EnvironmentField.

    Every declaration also states whether the field is Stable for the whole function invocation or Mutable across calls, plus the abstract region reached by the resulting pointer. Stable fields remain explicit and cheap to rematerialize: general GVN and LICM do not turn them into function-wide live ranges. Mutable fields may be reused across unrelated heap stores but are invalidated by calls and unknown memory writes. Targets materialize the semantic occurrences they receive rather than applying a second reuse policy. These contracts permit local redundancy elimination without pinning a physical register or hard-coding an embedding layout into MilkIR.

    #Semantic ownership

    MilkIR records the optimizer-visible facts of each built-in opcode in one semantic summary: whether it may trap, whether it reads or writes memory, and whether it has another observable effect. Dead-code elimination, loop optimization, and global value numbering derive their safety decisions from that summary instead of maintaining independent opcode lists. Unknown extension operations are conservatively treated as trapping and effectful.

    Other concerns remain with the stage that implements them. The verifier owns operand and result contracts, the printer owns textual syntax, direct acyclic rewriting owns local canonical forms, and native lowering owns instruction selection. These are different responsibilities rather than duplicate semantic facts.

    When adding a built-in opcode:

    1. Add its operand and result contract to the verifier.
    2. Classify its trap, memory, and observable-effect behavior in the opcode semantic summary.
    3. Add its textual representation to the printer.
    4. Add its instruction selection to milkir/native or the owning dialect adapter.
    5. Add a direct rewrite only when it is locally profitable and preserves the instruction's semantic contract.

    The built-in family matches in those stages are exhaustive, so adding a new family or operation leaves a compiler error until its required behavior is supplied. The direct rewriter is intentionally conservative: operations without a proven local canonicalization remain unchanged.

    #Why values are called SSA values

    SSA means static single assignment: each MilkIR Value is defined exactly once. An instruction never changes an existing value; it creates a new one.

    For example, a source-language assignment such as x = x + 1 should not overwrite the MilkIR value for the old x. The frontend emits a fresh value instead:

    v0:i32 = ... old x v1:i32 = iconst 1 v2:i32 = iadd v0, v1 new x

    This makes data dependencies explicit. An optimizer can see that v2 depends on v0 and v1 without reconstructing the history of a mutable variable.

    Every value belongs to one function and has one Type. The core types are I32, I64, F32, F64, V128, Ptr, Ref, CallableRef, and OpaqueRef. The four pointer/reference carrier types are fixed-width 64-bit values, not aliases for the host's native pointer type.

    #Comparisons do not require extra blocks

    Use select when both candidate values are already available. The next function returns the larger of two signed i32 values:

    ///|
    test "build max_i32 with select" {
    let builder = FunctionBuilder::FunctionBuilder("max_i32")
    let lhs = builder.add_param(I32)
    let rhs = builder.add_param(I32)
    builder.add_result(I32)

    let lhs_is_greater = builder.icmp_sgt(lhs, rhs)
    let result = builder.select(lhs_is_greater, lhs, rhs)
    builder.return_([result])

    let func = builder.finalize()
    inspect(func.verify(), content="()")
    inspect(func.print().contains("icmp.sgt"), content="true")
    inspect(func.print().contains("select"), content="true")
    }

    icmp_sgt produces an I32 condition. select(condition, when_nonzero, when_zero) chooses a value without changing control flow.

    #Control flow: blocks and terminators

    A block contains zero or more instructions and exactly one terminator. Once a terminator has been emitted, switch to a different block before emitting more instructions.

    The common terminators are:

    Builder methodMeaning
    return_(values)Return values to the caller.
    jump(target, args)Continue in another block and pass its arguments.
    brnz(condition, then_block, else_block)Branch to the first target when the condition is nonzero.
    brz(condition, then_block, else_block)Branch to the first target when the condition is zero.
    br_table(index, targets, default)Choose one of several targets.
    trap(reason)Stop execution abnormally.

    Here is the shape of a conditional function before considering how values cross block boundaries:

    +--------------+ | block0 | | test cond | +------+-------+ | +---------+---------+ | | v v +-----------+ +-----------+ | then_block| | else_block| +-----+-----+ +-----+-----+ | | +---------+---------+ | v +------------+ | join_block | +------------+

    #Passing values between blocks

    MilkIR uses block parameters instead of phi instructions. A block parameter is a value defined at the start of a block. Every jump to that block supplies the argument that the parameter receives on that edge.

    The following function computes input + 1 when condition is nonzero and returns input otherwise:

    ///|
    test "pass a value into a join block" {
    let builder = FunctionBuilder::FunctionBuilder("add_if")
    let input = builder.add_param(I32)
    let condition = builder.add_param(I32)
    builder.add_result(I32)

    let add_block = builder.create_block()
    let unchanged_block = builder.create_block()
    let join_block = builder.create_block()
    let result = builder.add_block_param(join_block, I32)

    builder.brnz(condition, add_block, unchanged_block)

    builder.switch_to_block(add_block)
    let one = builder.iconst_i32(1)
    let incremented = builder.iadd(input, one)
    builder.jump(join_block, [incremented])

    builder.switch_to_block(unchanged_block)
    builder.jump(join_block, [input])

    builder.switch_to_block(join_block)
    builder.return_([result])

    let func = builder.finalize()
    inspect(func.verify(), content="()")
    inspect(func.blocks.length(), content="4")
    inspect(func.blocks[3].params.length(), content="1")
    }

    Focus on the three names around the join:

    add_block --jump [incremented]--+ >-- join_block(result) -- return result unchanged_block --jump [input]----------+

    result is defined by join_block, not by either predecessor. On the add_block edge it receives incremented; on the other edge it receives input. If a join block has multiple parameters, each incoming jump must pass arguments in the same order.

    #Verification

    Always verify a function after construction and after transformations that may change its structure:

    let func = builder.finalize()
    func.verify()

    Function::verify checks core structural and local typing rules, including:

    • the function contains at least one block;
    • each referenced operand has been defined;
    • each block has a terminator;
    • core instructions have the expected operand arity;
    • operands that must agree have matching types;
    • comparison results have type I32.

    Verification is a useful construction guard, not a complete proof of every frontend, dialect, dominance, calling-convention, or embedding invariant. Frontends and dialects should perform any additional semantic checks their input requires.

    #Optimization

    Optimization lives in the Milky2018/milkir/optimize package within this module. Import it alongside Milky2018/milkir; the root package owns the IR and does not forward optimizer APIs. Optimization mutates a Function and returns an @optimize.OptResult whose changed field reports whether any pass changed the IR.

    ///|
    test "fold a constant expression" {
    let builder = FunctionBuilder::FunctionBuilder("constant_answer")
    builder.add_result(I32)
    let ten = builder.iconst_i32(10)
    let twenty = builder.iconst_i32(20)
    let answer = builder.iadd(ten, twenty)
    builder.return_([answer])

    let func = builder.finalize()
    let result = @optimize.optimize_with_level(func, O1)

    inspect(result.changed, content="true")
    inspect(@optimize.instruction_count(func), content="1")
    inspect(func.print().contains("iconst 30"), content="true")
    inspect(func.verify(), content="()")
    }

    The optimization levels are:

    LevelIntended use
    O0Minimal pipeline: removes dead code plus constant and unused block parameters.
    O1Inexpensive simplification: mandatory cleanup, constant folding, alias canonicalization, and dead-code elimination.
    O2The default pipeline: O1-style cleanup plus direct acyclic rewriting, budgeted memory GVN, unbudgeted cheap value numbering, and CFG simplification.
    O3Mandatory normalization, checked loop transformations, then one acyclic rewrite/GVN pipeline and final cleanup.

    Use @optimize.optimize(func) for the default pipeline or @optimize.optimize_with_level(func, level) when the caller chooses the level explicitly. Optimizers assume a valid SSA and block-parameter structure. Verify before optimization when the input comes from a frontend, then verify again after developing a new transformation.

    The optimizer works directly on the Function DFG. Rules are handwritten and dispatched by root opcode; there is no separate e-node graph, generated matcher or saturation loop. Staged rewrites require a strictly cheaper expression and must not rebuild shared definitions. Integer widths, vector lanes, floating NaNs/signed zero and trapping operations remain semantic constraints, not optional profitability hints. See the repository's optimizer design and rule migration ledger.

    #Counted-loop unrolling at O3

    O3 unrolls only natural loops for which analysis produces a complete plan. The supported form has one preheader, one header comparison, one body path, one latch/back edge, and one exit; body and latch blocks must not introduce additional block parameters. Initial values, bounds, and steps must resolve to constants through any loop-external copy chain. The induction value may be I32 or I64 and may use signed or unsigned <, <=, >, or >= comparisons. Reversed comparison operands and inverted conditional-branch polarity are normalized before analysis. Increasing and decreasing updates use checked arithmetic, so a loop whose final update would wrap is rejected.

    All header block parameters are treated as loop-carried state. Full unrolling is limited to at most eight source iterations, while larger proven loops use factor-two unrolling with one peeled iteration for odd trip counts. Both strategies cap newly cloned instructions at 64. Cloning assigns fresh instruction and value IDs, remaps zero-result and multi-result instructions, preserves metadata and effect order, and verifies correctly with calls, loads, stores, and traps.

    The pass leaves the function unchanged when the CFG shape is unsupported, a bound or step is dynamic, the trip count exceeds the analysis limit, an operand or latch edge cannot be mapped, arithmetic may wrap, or code growth exceeds the budget.

    #Calls, memory, and traps

    The concepts below matter when a frontend moves beyond pure arithmetic into embedding and effect semantics.

    #External calls

    ExternalSymbol names a symbol outside the IR function. CallOp::Direct(symbol, signature) and CallOp::Pointer(num_args, num_results) are treated conservatively by core optimizations because calls may observe or change state. Direct calls carry operand/result signatures that the verifier checks; pointer calls carry explicit argument and result counts.

    CallOp::Pointer has a generic operand contract:

    1. operand 0 is the function pointer;
    2. operands 1 and later are ordinary arguments;
    3. num_args counts every operand after the callee pointer.

    An embedding adapter may assign roles such as VMContext to those arguments when it chooses a calling convention, but that role is not part of the core opcode contract.

    #Traps and effects

    Trap(reason) and TrapExit(reason) terminators end execution without normal results. Stores, calls, traps, and unknown extension operations are observable or potentially observable. Optimizations must not delete or reorder them unless a stronger analysis proves that doing so preserves behavior.

    #Dialect-specific operations

    Ext(ExtOp, Signature) represents dialect-specific operations. MilkIR stores only a dialect name, opcode name, integer immediates, and an explicit operand/result contract. Validator closures are not part of Function; the dialect package owns builders, semantic validation, decoding, and lowering.

    Function::verify and FunctionBuilder::finalize validate generic IR structure without interpreting dialect semantics. At the adapter seam, Function::verify_with_dialect_validator receives the owned dialect name plus explicit instruction and global-value validators, rejects data owned by another dialect, and converts diagnostics into VerifyError::UnverifiableInstruction. Dialect lowering likewise requires explicit adapter validation, so validation behavior depends only on the adapter selected by the consumer, never on function construction history.

    ///|
    test "validate a dialect opcode descriptor" {
    let descriptor = ExtOpDescriptor::ExtOpDescriptor("demo", "checked_add", 1)
    let opcode = ExtOp::ExtOp("demo", "checked_add", FixedArray::make(1, 32))

    inspect(opcode.matches_descriptor(descriptor), content="true")
    inspect(descriptor.expected_immediate_count(), content="1")
    }

    Core optimizations treat extension operations conservatively because MilkIR cannot infer whether an unknown operation reads memory, writes memory, traps, or depends on embedding state.

    #Typical frontend workflow

    A frontend using MilkIR normally follows this sequence:

    1. Map source types to MilkIR Types.
    2. Create a FunctionBuilder, then declare the function parameters and results.
    3. Translate each source basic block, keeping a map from source values to MilkIR Values.
    4. Represent merges and loop-carried values with block parameters and jump arguments.
    5. Finalize and verify the function.
    6. Optimize it at the desired level.
    7. Stream it into a native target with Milky2018/milkir/native.

    For debugging, Function::print produces a readable textual view and CFG::to_dot produces Graphviz DOT for the control-flow graph. CFG also provides predecessors, successors, traversal orders, dominators, back edges, and loop discovery.

    MilkIR focuses on target-independent SSA construction, verification, control-flow analysis, and optimization. Other compiler stages build on it through separate packages:

    Compiler stagePackage
    SSA construction, verification, CFGs, and optimizationMilky2018/milkir
    Optional WebAssembly extension operationsMilky2018/wasm_milkir
    Direct native target loweringMilky2018/milkir/native
    Target instruction selection and ABI detailsMilky2018/aarch64_target and Milky2018/x64_target

    InstId

    type InstId = Int

    Stable index into a function's instruction arena.

    VerifyError

    pub suberror VerifyError {
    MissingTerminator(block_id~ : Int)
    EmptyFunction
    UndefinedValue(value_id~ : Int)
    ForeignValue(value_id~ : Int)
    ForeignBlock(block_id~ : Int)
    ForeignInstruction(inst_id~ : Int)
    ForeignGlobalValue(global_value_id~ : Int)
    DuplicateBlockId(block_id~ : Int)
    DuplicateValueDefinition(value_id~ : Int)
    DuplicateInstructionId(inst_id~ : Int)
    UnplacedInstruction(inst_id~ : Int)
    InstructionPlacementMismatch(inst_id~ : Int)
    UseBeforeDefinition(value_id~ : Int)
    NonDominatingUse(value_id~ : Int, defining_block~ : Int, use_block~ : Int)
    InstructionOperandMismatch(inst_id~ : Int)
    InvalidBlockTarget(block_id~ : Int)
    ArityMismatch(message~ : String)
    TypeMismatch(message~ : String)
    UnverifiableInstruction(message~ : String)
    } derive(Eq,
    Debug
    )

    impl Show for VerifyError

    VerifyError::equal

    fn VerifyError::equal(VerifyError, VerifyError) -> Bool

    VerifyError::not_equal

    fn VerifyError::not_equal(x : VerifyError, y : VerifyError) -> Bool

    VerifyError::output

    fn VerifyError::output(self : VerifyError, logger : &Logger) -> Unit

    VerifyError::to_string

    fn VerifyError::to_string(self : VerifyError) -> String

    VerifyError::unverifiable_instruction

    fn VerifyError::unverifiable_instruction(message : String) -> VerifyError

    Construct a structured error for a consumer seam that cannot validate or lower an otherwise well-formed MilkIR function.

    AliasRegion

    pub(all) enum AliasRegion {
    Heap(Int)
    Table(Int)
    Context
    Other
    } derive(Eq, Hash,
    Debug
    )

    Disjoint abstract memory regions used by alias analysis.

    AliasRegion::equal

    fn AliasRegion::equal(AliasRegion, AliasRegion) -> Bool

    AliasRegion::hash

    fn AliasRegion::hash(self : AliasRegion) -> Int

    AliasRegion::hash_combine

    fn AliasRegion::hash_combine(AliasRegion, Hasher) -> Unit

    AliasRegion::not_equal

    fn AliasRegion::not_equal(x : AliasRegion, y : AliasRegion) -> Bool

    Block

    pub struct Block {
    id : Int
    params : Array[(Value, Type)]
    terminator : Terminator?
    // private fields
    }

    Block - represents a basic block in the control flow graph A basic block is a sequence of instructions with:
    • One entry point (the first instruction)
    • One exit point (the last instruction, a terminator)

    Parameters and the terminator are exposed for zero-copy inspection. The instruction layout is private because it stores arena IDs rather than instruction data. Use Block::instruction_count and Function::block_instruction_at to inspect it.

    Block::append_inst

    fn Block::append_inst(self : Block, inst : Inst) -> Unit

    Block::clear_instructions_for_move

    fn Block::clear_instructions_for_move(self : Block) -> Unit

    Detach the complete layout for relocation without deleting instructions.

    Block::instruction_count

    fn Block::instruction_count(self : Block) -> Int

    Return the number of instructions placed in this block.

    Block::instruction_ids

    fn Block::instruction_ids(self : Block) -> ArrayView[Int]

    Borrow the stable instruction IDs. Do not retain the view across a layout mutation; the backing layout cannot be mutated through this view.

    Block::retain_instruction_ids

    fn Block::retain_instruction_ids(self : Block, keep : (Int) -> Bool) -> Unit

    Remove instructions rejected by a stable predicate and mark them deleted.

    Block::set_instructions

    fn Block::set_instructions(self : Block, instructions : Array[Inst]) -> Unit

    Replace the layout with same-function instructions without deleting them. Omitted instructions must be relocated or explicitly discarded. Verification rejects missing or duplicate placements; layout length never implies deletion.

    Block::set_terminator

    fn Block::set_terminator(self : Block, term : Terminator) -> Unit

    Set the terminator for this block

    Block::take_inst_at

    fn Block::take_inst_at(self : Block, index : Int) -> Int?

    Detach an instruction for relocation without marking it deleted.

    Block::to_repr

    CFG

    pub struct CFG {
    size : Int
    num_blocks : Int
    successors : Array[Array[Int]]
    predecessors : Array[Array[Int]]
    valid : Array[Bool]
    } derive(
    Debug
    )

    CFG::build

    fn CFG::build(func : Function) -> CFG

    CFG::compute_dominance

    fn CFG::compute_dominance(self : CFG) -> Dominance

    CFG::compute_dominators

    fn CFG::compute_dominators(self : CFG) -> Array[Int]

    CFG::dominates

    fn CFG::dominates(self : CFG, a : Int, b : Int) -> Bool

    CFG::find_back_edges

    fn CFG::find_back_edges(self : CFG) -> Array[(Int, Int)]

    CFG::find_loops

    fn CFG::find_loops(self : CFG) -> Array[Loop]

    CFG::get_loop_preheader

    fn CFG::get_loop_preheader(self : CFG, loop_ : Loop) -> Int?

    CFG::get_predecessors

    fn CFG::get_predecessors(self : CFG, block_id : Int) -> Array[Int]

    CFG::get_successors

    fn CFG::get_successors(self : CFG, block_id : Int) -> Array[Int]

    CFG::is_entry_or_unreachable

    fn CFG::is_entry_or_unreachable(self : CFG, block_id : Int) -> Bool

    CFG::is_exit_block

    fn CFG::is_exit_block(self : CFG, block_id : Int) -> Bool

    CFG::is_valid

    fn CFG::is_valid(self : CFG, block_id : Int) -> Bool

    CFG::postorder

    fn CFG::postorder(self : CFG) -> Array[Int]

    Depth-first postorder over the reachable CFG, entry block first.

    The depth of this walk is the length of a CFG path, which the input function controls, so it keeps its own stack on the heap. A chain of blocks is ordinary in generated code, and a native recursion turns one into a stack overflow inside the optimizer rather than a diagnosable error (ISS-380, ISS-401).

    CFG::reverse_postorder

    fn CFG::reverse_postorder(self : CFG) -> Array[Int]

    CFG::to_dot

    fn CFG::to_dot(self : CFG, func_name : String) -> String

    CFG::to_repr

    CallOp

    pub(all) enum CallOp {
    Direct(ExternalSymbol, Signature)
    Pointer(Int, Int)
    } derive(Eq, Hash,
    Debug
    )

    Generic call semantics. Pointer-call operands are laid out as the callee pointer followed by ordinary arguments.

    CallOp::equal

    fn CallOp::equal(CallOp, CallOp) -> Bool

    CallOp::hash

    fn CallOp::hash(self : CallOp) -> Int

    CallOp::hash_combine

    fn CallOp::hash_combine(CallOp, Hasher) -> Unit

    CallOp::not_equal

    fn CallOp::not_equal(x : CallOp, y : CallOp) -> Bool

    CallOp::to_repr

    ContextField

    pub struct ContextField {
    dialect : String
    key : Int
    parameters : FixedArray[Int]
    ty : Type
    } derive(
    Debug
    )

    Opaque embedding-context field identity.

    The dialect owns the meaning of key and parameters; MilkIR only relies on the declared type and optimizer-visible global-value contract.
    impl Eq for ContextField

    ContextField::equal

    fn ContextField::equal(self : ContextField, other : ContextField) -> Bool

    ContextField::hash

    fn ContextField::hash(self : ContextField) -> Int

    ContextField::hash_combine

    fn ContextField::hash_combine(self : ContextField, hasher : Hasher) -> Unit

    ContextField::new

    fn ContextField::new(dialect : String, key : Int, parameters : FixedArray[Int], ty : Type) -> ContextField

    ContextField::not_equal

    fn ContextField::not_equal(x : ContextField, y : ContextField) -> Bool

    ConversionOp

    pub(all) enum ConversionOp {
    IntReduce
    SignedExtend
    UnsignedExtend
    FloatPromote
    FloatDemote
    FloatToSignedInt
    FloatToUnsignedInt
    FloatToSignedIntSaturating
    FloatToUnsignedIntSaturating
    SignedIntToFloat
    UnsignedIntToFloat
    Bitcast
    } derive(Eq, Hash,
    Debug
    )

    ConversionOp::equal

    ConversionOp::hash

    fn ConversionOp::hash(self : ConversionOp) -> Int

    ConversionOp::hash_combine

    fn ConversionOp::hash_combine(ConversionOp, Hasher) -> Unit

    ConversionOp::not_equal

    fn ConversionOp::not_equal(x : ConversionOp, y : ConversionOp) -> Bool

    Dominance

    pub struct Dominance {
    idom : Array[Int]
    // private fields
    }

    Dominator facts for one CFG snapshot; rebuild after changing successors.

    Dominance::dominates

    fn Dominance::dominates(self : Dominance, a : Int, b : Int) -> Bool

    ExtOp

    pub struct ExtOp {
    dialect : String
    opcode : String
    immediates : FixedArray[Int]
    } derive(
    Debug
    )

    impl Eq for ExtOp
    impl Hash for ExtOp

    ExtOp::ExtOp

    fn ExtOp::ExtOp(dialect : String, opcode : String, immediates : FixedArray[Int]) -> ExtOp

    ExtOp::equal

    fn ExtOp::equal(self : ExtOp, other : ExtOp) -> Bool

    ExtOp::hash

    fn ExtOp::hash(self : ExtOp) -> Int

    ExtOp::hash_combine

    fn ExtOp::hash_combine(self : ExtOp, hasher : Hasher) -> Unit

    ExtOp::matches_descriptor

    fn ExtOp::matches_descriptor(self : ExtOp, descriptor : ExtOpDescriptor) -> Bool

    ExtOp::not_equal

    fn ExtOp::not_equal(x : ExtOp, y : ExtOp) -> Bool

    ExtOp::to_repr

    ExtOpDescriptor

    pub(all) struct ExtOpDescriptor {
    dialect : String
    opcode : String
    min_immediates : Int
    max_immediates : Int
    } derive(Eq, Hash,
    Debug
    )

    ExtOpDescriptor::ExtOpDescriptor

    fn ExtOpDescriptor::ExtOpDescriptor(dialect : String, opcode : String, immediate_count : Int) -> ExtOpDescriptor

    ExtOpDescriptor::accepts_immediate_count

    fn ExtOpDescriptor::accepts_immediate_count(self : ExtOpDescriptor, count : Int) -> Bool

    ExtOpDescriptor::equal

    ExtOpDescriptor::expected_immediate_count

    fn ExtOpDescriptor::expected_immediate_count(self : ExtOpDescriptor) -> String

    ExtOpDescriptor::hash

    fn ExtOpDescriptor::hash(self : ExtOpDescriptor) -> Int

    ExtOpDescriptor::hash_combine

    fn ExtOpDescriptor::hash_combine(ExtOpDescriptor, Hasher) -> Unit

    ExtOpDescriptor::not_equal

    fn ExtOpDescriptor::not_equal(x : ExtOpDescriptor, y : ExtOpDescriptor) -> Bool

    ExtOpDescriptor::with_immediate_range

    fn ExtOpDescriptor::with_immediate_range(dialect : String, opcode : String, min_immediates : Int, max_immediates : Int) -> ExtOpDescriptor

    ExtensionInstView

    pub struct ExtensionInstView {
    op : ExtOp
    operand_types : ReadOnlyArray[Type]
    result_types : ReadOnlyArray[Type]
    } derive(
    Debug
    )

    Read-only instruction shape exposed to an extension dialect validator.

    ExternalSymbol

    pub struct ExternalSymbol {
    name : String
    } derive(Eq, Hash,
    Debug
    )

    ExternalSymbol::ExternalSymbol

    fn ExternalSymbol::ExternalSymbol(name : String) -> ExternalSymbol

    ExternalSymbol::equal

    ExternalSymbol::hash

    fn ExternalSymbol::hash(self : ExternalSymbol) -> Int

    ExternalSymbol::hash_combine

    fn ExternalSymbol::hash_combine(ExternalSymbol, Hasher) -> Unit

    ExternalSymbol::not_equal

    fn ExternalSymbol::not_equal(x : ExternalSymbol, y : ExternalSymbol) -> Bool

    FloatBinaryOp

    pub(all) enum FloatBinaryOp {
    Add
    Sub
    Mul
    Div
    Min
    Max
    } derive(Eq, Hash,
    Debug
    )

    FloatBinaryOp::equal

    FloatBinaryOp::hash

    fn FloatBinaryOp::hash(self : FloatBinaryOp) -> Int

    FloatBinaryOp::hash_combine

    fn FloatBinaryOp::hash_combine(FloatBinaryOp, Hasher) -> Unit

    FloatBinaryOp::not_equal

    fn FloatBinaryOp::not_equal(x : FloatBinaryOp, y : FloatBinaryOp) -> Bool

    FloatCC

    pub(all) enum FloatCC {
    Eq
    Ne
    Lt
    Le
    Gt
    Ge
    } derive(Eq, Hash,
    Debug
    )

    Floating point comparison condition codes

    FloatCC::equal

    fn FloatCC::equal(FloatCC, FloatCC) -> Bool

    FloatCC::hash

    fn FloatCC::hash(self : FloatCC) -> Int

    FloatCC::hash_combine

    fn FloatCC::hash_combine(FloatCC, Hasher) -> Unit

    FloatCC::not_equal

    fn FloatCC::not_equal(x : FloatCC, y : FloatCC) -> Bool

    FloatCC::to_repr

    FloatUnaryOp

    pub(all) enum FloatUnaryOp {
    Neg
    Abs
    Sqrt
    Ceil
    Floor
    Trunc
    Nearest
    } derive(Eq, Hash,
    Debug
    )

    FloatUnaryOp::equal

    FloatUnaryOp::hash

    fn FloatUnaryOp::hash(self : FloatUnaryOp) -> Int

    FloatUnaryOp::hash_combine

    fn FloatUnaryOp::hash_combine(FloatUnaryOp, Hasher) -> Unit

    FloatUnaryOp::not_equal

    fn FloatUnaryOp::not_equal(x : FloatUnaryOp, y : FloatUnaryOp) -> Bool

    Function

    pub struct Function {
    name : String
    params : Array[(Value, Type)]
    results : Array[Type]
    blocks : Array[Block]
    external_symbols : Array[ExternalSymbol]
    global_values : Array[(GlobalValue, GlobalValueData)]
    next_value_id : Int
    next_inst_id : Int
    next_block_id : Int
    next_global_value_id : Int
    // private fields
    }

    Function - a complete mutable IR function.

    The public fields are exposed for zero-copy inspection. Callers must not mutate params, results, blocks, external_symbols, or the ID counters directly. Use FunctionBuilder and the checked Function/Block methods for structural changes. A function must be verified after its final mutation and immediately before optimization or lowering.

    Function::add_param

    fn Function::add_param(self : Function, ty : Type) -> Value

    Add a parameter to the function

    Function::add_result

    fn Function::add_result(self : Function, ty : Type) -> Unit

    Add a result type to the function

    Function::block_instruction_at

    fn Function::block_instruction_at(self : Function, block : Block, index : Int) -> Inst?

    Return the instruction at one position in a block's layout.

    Function::block_instruction_ids

    fn Function::block_instruction_ids(self : Function, block : Block) -> ArrayView[Int]

    Return a zero-copy view of one block's stable instruction IDs.

    The view remains valid until that block's layout is mutated.

    Function::declare_external_symbol

    fn Function::declare_external_symbol(self : Function, name : String) -> ExternalSymbol

    Function::declare_global_value

    fn Function::declare_global_value(self : Function, data : GlobalValueData) -> GlobalValue

    Declare or reuse an identical function-scoped global value.

    Function::defining_inst

    fn Function::defining_inst(self : Function, value : Value) -> Inst?

    Look up the defining instruction of a same-function SSA value, including detached definitions. Parameters and foreign values have no instruction.

    Function::discard_inst

    fn Function::discard_inst(self : Function, inst : Inst) -> Unit

    Discard a same-function instruction that was never placed or was detached. Verification rejects discarded instructions still present in a block or whose results are still used. Arena IDs are never reused.

    Function::global_value_data

    fn Function::global_value_data(self : Function, global_value : GlobalValue) -> GlobalValueData?

    Return the declaration for an owned global-value handle.

    Function::instruction_by_id

    fn Function::instruction_by_id(self : Function, inst_id : Int) -> Inst?

    Function::new_block

    fn Function::new_block(self : Function, params : Array[Value]) -> Block

    Create a new basic block

    Function::new_block0

    fn Function::new_block0(self : Function) -> Block

    Function::new_empty

    fn Function::new_empty(name : String) -> Function

    Function::new_inst

    fn Function::new_inst(self : Function, opcode : Opcode, args : Array[Value], results : Array[Value]) -> Inst

    Function::new_value

    fn Function::new_value(self : Function, ty : Type) -> Value

    Create a new value with a unique ID

    Function::param

    fn Function::param(self : Function, index : Int) -> Value?

    Return a declared function parameter by position.

    Function::print

    fn Function::print(self : Function) -> String

    Print a function

    Function::retain_block_params

    fn Function::retain_block_params(self : Function, block : Block, keep : Array[Int]) -> Unit

    Keep and reorder a block's existing parameters while updating their DFG definitions atomically with the layout mutation.

    Function::retain_blocks

    fn Function::retain_blocks(self : Function, keep : (Block) -> Bool) -> Unit

    Remove blocks rejected by a stable predicate and delete their instructions.

    Function::signature

    fn Function::signature(self : Function) -> Signature

    Return a signature snapshot derived from the explicit function contract.

    Function::to_repr

    Function::verify

    fn Function::verify(self : Function) -> Unit raise VerifyError

    Function::verify_core

    fn Function::verify_core(self : Function) -> Unit raise VerifyError

    Verify that a function is self-contained core MilkIR. Dialect-bearing instructions must be validated and lowered by their owning adapter.

    Function::verify_with_dialect_validator

    fn Function::verify_with_dialect_validator(self : Function, dialect : String, validate_extension : (ExtensionInstView) -> String?, validate_global_value : (GlobalValueData) -> String?) -> Unit raise VerifyError

    Verify generic MilkIR structure, require every extension and context global to belong to the owning dialect, and apply that adapter's validators before crossing its seam.

    Function::with_signature

    fn Function::with_signature(name : String, signature : Signature) -> Function

    Create a function whose declared signature is materialized as explicit parameter values and result types.

    FunctionBuilder

    type FunctionBuilder

    FunctionBuilder - helps construct IR functions Tracks the current block and provides methods for emitting instructions

    FunctionBuilder::FunctionBuilder

    fn FunctionBuilder::FunctionBuilder(name : String) -> FunctionBuilder

    FunctionBuilder::add_block_param

    fn FunctionBuilder::add_block_param(self : FunctionBuilder, block : Block, ty : Type) -> Value

    Add a block parameter (for SSA phi nodes)

    FunctionBuilder::add_param

    fn FunctionBuilder::add_param(self : FunctionBuilder, ty : Type) -> Value

    Add a parameter to the function

    FunctionBuilder::add_result

    fn FunctionBuilder::add_result(self : FunctionBuilder, ty : Type) -> Unit

    Add a result type to the function

    FunctionBuilder::append_block_params_for_function_params

    fn FunctionBuilder::append_block_params_for_function_params(self : FunctionBuilder, block : Block) -> Unit

    Append block parameters matching the function parameter types.

    FunctionBuilder::append_block_params_for_function_returns

    fn FunctionBuilder::append_block_params_for_function_returns(self : FunctionBuilder, block : Block) -> Unit

    Append block parameters matching the function result types.

    FunctionBuilder::band

    fn FunctionBuilder::band(self : FunctionBuilder, a : Value, b : Value) -> Value

    Bitwise and

    FunctionBuilder::bitcast

    fn FunctionBuilder::bitcast(self : FunctionBuilder, ty : Type, a : Value) -> Value

    Bitcast (reinterpret bits)

    FunctionBuilder::block_params

    fn FunctionBuilder::block_params(self : FunctionBuilder, block : Block) -> Array[Value]

    Return the SSA values used as block parameters.

    FunctionBuilder::bnot

    fn FunctionBuilder::bnot(self : FunctionBuilder, a : Value) -> Value

    Bitwise not

    FunctionBuilder::bor

    fn FunctionBuilder::bor(self : FunctionBuilder, a : Value, b : Value) -> Value

    Bitwise or

    FunctionBuilder::br_table

    fn FunctionBuilder::br_table(self : FunctionBuilder, index : Value, targets : Array[Block], default_target : Block) -> Unit

    Branch table (switch)

    FunctionBuilder::branch

    fn FunctionBuilder::branch(self : FunctionBuilder, cond : Value, then_block : Block, then_args : Array[Value], else_block : Block, else_args : Array[Value]) -> Unit

    Conditional branch with arguments for both successor blocks.

    FunctionBuilder::brnz

    fn FunctionBuilder::brnz(self : FunctionBuilder, cond : Value, then_block : Block, else_block : Block) -> Unit

    Conditional branch (branch if non-zero)

    FunctionBuilder::brz

    fn FunctionBuilder::brz(self : FunctionBuilder, cond : Value, then_block : Block, else_block : Block) -> Unit

    Conditional branch (branch if zero)

    FunctionBuilder::bxor

    fn FunctionBuilder::bxor(self : FunctionBuilder, a : Value, b : Value) -> Value

    Bitwise xor

    FunctionBuilder::call_pointer

    fn FunctionBuilder::call_pointer(self : FunctionBuilder, func_ptr : Value, args : Array[Value], result_types : Array[Type]) -> Array[Value]

    Call a function pointer with ordinary arguments.

    FunctionBuilder::call_symbol

    fn FunctionBuilder::call_symbol(self : FunctionBuilder, symbol : ExternalSymbol, result_ty : Type?, args : Array[Value]) -> Value?

    FunctionBuilder::call_symbol_multi

    fn FunctionBuilder::call_symbol_multi(self : FunctionBuilder, symbol : ExternalSymbol, result_types : Array[Type], args : Array[Value]) -> Array[Value]

    FunctionBuilder::clz

    fn FunctionBuilder::clz(self : FunctionBuilder, a : Value) -> Value

    Count leading zeros

    FunctionBuilder::copy

    fn FunctionBuilder::copy(self : FunctionBuilder, a : Value) -> Value

    Copy value (for register allocation)

    FunctionBuilder::create_block

    fn FunctionBuilder::create_block(self : FunctionBuilder) -> Block

    Create a new block.

    FunctionBuilder::ctz

    fn FunctionBuilder::ctz(self : FunctionBuilder, a : Value) -> Value

    Count trailing zeros

    FunctionBuilder::current_block

    fn FunctionBuilder::current_block(self : FunctionBuilder) -> Block

    Get the current block

    FunctionBuilder::emit_ext_inst

    fn FunctionBuilder::emit_ext_inst(self : FunctionBuilder, ty : Type, opcode : ExtOp, operands : Array[Value]) -> Value

    FunctionBuilder::emit_inst

    fn FunctionBuilder::emit_inst(self : FunctionBuilder, ty : Type, opcode : Opcode, operands : Array[Value]) -> Value

    Emit an instruction that produces a result

    FunctionBuilder::emit_multi_ext_inst

    fn FunctionBuilder::emit_multi_ext_inst(self : FunctionBuilder, result_types : Array[Type], opcode : ExtOp, operands : Array[Value]) -> Array[Value]

    FunctionBuilder::emit_multi_inst

    fn FunctionBuilder::emit_multi_inst(self : FunctionBuilder, result_types : Array[Type], opcode : Opcode, operands : Array[Value]) -> Array[Value]

    Emit an instruction with multiple results.

    FunctionBuilder::emit_void_ext_inst

    fn FunctionBuilder::emit_void_ext_inst(self : FunctionBuilder, opcode : ExtOp, operands : Array[Value]) -> Unit

    FunctionBuilder::emit_void_inst

    fn FunctionBuilder::emit_void_inst(self : FunctionBuilder, opcode : Opcode, operands : Array[Value]) -> Unit

    Emit an instruction without a result

    FunctionBuilder::fabs

    fn FunctionBuilder::fabs(self : FunctionBuilder, a : Value) -> Value

    Float absolute value

    FunctionBuilder::fadd

    fn FunctionBuilder::fadd(self : FunctionBuilder, a : Value, b : Value) -> Value

    Float add

    FunctionBuilder::fceil

    fn FunctionBuilder::fceil(self : FunctionBuilder, a : Value) -> Value

    Float ceiling

    FunctionBuilder::fcmp

    fn FunctionBuilder::fcmp(self : FunctionBuilder, cc : FloatCC, a : Value, b : Value) -> Value

    Float comparison (returns i32 0 or 1)

    FunctionBuilder::fconst_f32

    fn FunctionBuilder::fconst_f32(self : FunctionBuilder, value : Float) -> Value

    Emit an f32 constant Note: We pack the f32 bits into the Double's bit representation to preserve NaN payloads. Using value.to_double() would go through the FPU and convert signaling NaNs to quiet NaNs.

    FunctionBuilder::fconst_f64

    fn FunctionBuilder::fconst_f64(self : FunctionBuilder, value : Double) -> Value

    Emit an f64 constant

    FunctionBuilder::fcvt_to_sint

    fn FunctionBuilder::fcvt_to_sint(self : FunctionBuilder, ty : Type, a : Value) -> Value

    Float to signed int

    FunctionBuilder::fcvt_to_sint_sat

    fn FunctionBuilder::fcvt_to_sint_sat(self : FunctionBuilder, ty : Type, a : Value) -> Value

    Float to signed int (saturating - NaN->0, overflow->max/min)

    FunctionBuilder::fcvt_to_uint

    fn FunctionBuilder::fcvt_to_uint(self : FunctionBuilder, ty : Type, a : Value) -> Value

    Float to unsigned int

    FunctionBuilder::fcvt_to_uint_sat

    fn FunctionBuilder::fcvt_to_uint_sat(self : FunctionBuilder, ty : Type, a : Value) -> Value

    Float to unsigned int (saturating - NaN->0, overflow->max, negative->0)

    FunctionBuilder::fdemote

    fn FunctionBuilder::fdemote(self : FunctionBuilder, a : Value) -> Value

    Demote float (f64 -> f32)

    FunctionBuilder::fdiv

    fn FunctionBuilder::fdiv(self : FunctionBuilder, a : Value, b : Value) -> Value

    Float divide

    FunctionBuilder::ffloor

    fn FunctionBuilder::ffloor(self : FunctionBuilder, a : Value) -> Value

    Float floor

    FunctionBuilder::finalize

    fn FunctionBuilder::finalize(self : FunctionBuilder) -> Function raise VerifyError

    Verify generic MilkIR structure and return the same mutable function.

    Finalization is a validation checkpoint, not a frozen or persistent validated state. Extension semantics remain the owning adapter's responsibility. Any later mutation requires validation at the next consuming seam.

    FunctionBuilder::fmax

    fn FunctionBuilder::fmax(self : FunctionBuilder, a : Value, b : Value) -> Value

    Float maximum

    FunctionBuilder::fmin

    fn FunctionBuilder::fmin(self : FunctionBuilder, a : Value, b : Value) -> Value

    Float minimum

    FunctionBuilder::fmul

    fn FunctionBuilder::fmul(self : FunctionBuilder, a : Value, b : Value) -> Value

    Float multiply

    FunctionBuilder::fnearest

    fn FunctionBuilder::fnearest(self : FunctionBuilder, a : Value) -> Value

    Float nearest (round to nearest even)

    FunctionBuilder::fneg

    fn FunctionBuilder::fneg(self : FunctionBuilder, a : Value) -> Value

    Float negate

    FunctionBuilder::fpromote

    fn FunctionBuilder::fpromote(self : FunctionBuilder, a : Value) -> Value

    Promote float (f32 -> f64)

    FunctionBuilder::fsqrt

    fn FunctionBuilder::fsqrt(self : FunctionBuilder, a : Value) -> Value

    Float square root

    FunctionBuilder::fsub

    fn FunctionBuilder::fsub(self : FunctionBuilder, a : Value, b : Value) -> Value

    Float subtract

    FunctionBuilder::ftrunc

    fn FunctionBuilder::ftrunc(self : FunctionBuilder, a : Value) -> Value

    Float truncate

    FunctionBuilder::get_const_value

    fn FunctionBuilder::get_const_value(self : FunctionBuilder, v : Value) -> Int64?

    Get the constant value if a Value was defined by an Iconst instruction.

    FunctionBuilder::get_function

    fn FunctionBuilder::get_function(self : FunctionBuilder) -> Function

    Get the mutable function being built without validating it.

    This is an explicit construction and transformation escape hatch. Consumers must verify the function after the final mutation and before lowering it.

    FunctionBuilder::global_value

    fn FunctionBuilder::global_value(self : FunctionBuilder, global_value : GlobalValue, context : Value) -> Value

    Read a function-scoped global value from the explicit embedding context.

    FunctionBuilder::iadd

    fn FunctionBuilder::iadd(self : FunctionBuilder, a : Value, b : Value) -> Value

    Integer add

    FunctionBuilder::icmp

    fn FunctionBuilder::icmp(self : FunctionBuilder, cc : IntCC, a : Value, b : Value) -> Value

    Integer comparison (returns i32 0 or 1)

    FunctionBuilder::icmp_eq

    fn FunctionBuilder::icmp_eq(self : FunctionBuilder, a : Value, b : Value) -> Value

    Integer equal

    FunctionBuilder::icmp_ne

    fn FunctionBuilder::icmp_ne(self : FunctionBuilder, a : Value, b : Value) -> Value

    Integer not equal

    FunctionBuilder::icmp_sge

    fn FunctionBuilder::icmp_sge(self : FunctionBuilder, a : Value, b : Value) -> Value

    Signed greater than or equal

    FunctionBuilder::icmp_sgt

    fn FunctionBuilder::icmp_sgt(self : FunctionBuilder, a : Value, b : Value) -> Value

    Signed greater than

    FunctionBuilder::icmp_sle

    fn FunctionBuilder::icmp_sle(self : FunctionBuilder, a : Value, b : Value) -> Value

    Signed less than or equal

    FunctionBuilder::icmp_slt

    fn FunctionBuilder::icmp_slt(self : FunctionBuilder, a : Value, b : Value) -> Value

    Signed less than

    FunctionBuilder::icmp_uge

    fn FunctionBuilder::icmp_uge(self : FunctionBuilder, a : Value, b : Value) -> Value

    Unsigned greater than or equal

    FunctionBuilder::icmp_ugt

    fn FunctionBuilder::icmp_ugt(self : FunctionBuilder, a : Value, b : Value) -> Value

    Unsigned greater than

    FunctionBuilder::icmp_ule

    fn FunctionBuilder::icmp_ule(self : FunctionBuilder, a : Value, b : Value) -> Value

    Unsigned less than or equal

    FunctionBuilder::icmp_ult

    fn FunctionBuilder::icmp_ult(self : FunctionBuilder, a : Value, b : Value) -> Value

    Unsigned less than

    FunctionBuilder::iconst

    fn FunctionBuilder::iconst(self : FunctionBuilder, ty : Type, value : Int64) -> Value

    Emit an integer constant

    FunctionBuilder::iconst_i32

    fn FunctionBuilder::iconst_i32(self : FunctionBuilder, value : Int) -> Value

    Emit an i32 constant

    FunctionBuilder::iconst_i64

    fn FunctionBuilder::iconst_i64(self : FunctionBuilder, value : Int64) -> Value

    Emit an i64 constant

    FunctionBuilder::imul

    fn FunctionBuilder::imul(self : FunctionBuilder, a : Value, b : Value) -> Value

    Integer multiply

    FunctionBuilder::ireduce

    fn FunctionBuilder::ireduce(self : FunctionBuilder, ty : Type, a : Value) -> Value

    Reduce integer width (e.g., i64 -> i32)

    FunctionBuilder::ishl

    fn FunctionBuilder::ishl(self : FunctionBuilder, a : Value, b : Value) -> Value

    Shift left

    FunctionBuilder::isub

    fn FunctionBuilder::isub(self : FunctionBuilder, a : Value, b : Value) -> Value

    Integer subtract

    FunctionBuilder::jump

    fn FunctionBuilder::jump(self : FunctionBuilder, target : Block, args : Array[Value]) -> Unit

    Unconditional jump

    FunctionBuilder::load_ptr

    fn FunctionBuilder::load_ptr(self : FunctionBuilder, ty : Type, base : Value, offset : Value) -> Value

    Load from raw pointer (no bounds checking) For trampoline code that operates on host memory

    FunctionBuilder::load_ptr_narrow

    fn FunctionBuilder::load_ptr_narrow(self : FunctionBuilder, result_ty : Type, bits : Int, signed : Bool, base : Value, offset : Value) -> Value

    Load narrow value from raw pointer (no bounds checking) Loads 'bits' bits from memory and extends to result_ty

    FunctionBuilder::popcnt

    fn FunctionBuilder::popcnt(self : FunctionBuilder, a : Value) -> Value

    Population count (count number of 1 bits)

    FunctionBuilder::print

    fn FunctionBuilder::print(self : FunctionBuilder) -> String

    Print using FunctionBuilder

    FunctionBuilder::return_

    fn FunctionBuilder::return_(self : FunctionBuilder, values : Array[Value]) -> Unit

    Return from function

    FunctionBuilder::rotl

    fn FunctionBuilder::rotl(self : FunctionBuilder, a : Value, b : Value) -> Value

    Rotate left

    FunctionBuilder::rotr

    fn FunctionBuilder::rotr(self : FunctionBuilder, a : Value, b : Value) -> Value

    Rotate right

    FunctionBuilder::sdiv

    fn FunctionBuilder::sdiv(self : FunctionBuilder, a : Value, b : Value) -> Value

    Signed integer divide

    FunctionBuilder::select

    fn FunctionBuilder::select(self : FunctionBuilder, cond : Value, a : Value, b : Value) -> Value

    Conditional select: cond ? a : b

    FunctionBuilder::sextend

    fn FunctionBuilder::sextend(self : FunctionBuilder, ty : Type, a : Value) -> Value

    Sign extend (e.g., i32 -> i64)

    FunctionBuilder::sextend16

    fn FunctionBuilder::sextend16(self : FunctionBuilder, ty : Type, a : Value) -> Value

    Sign extend from 16 bits (in-place, keeps the same type) Similar to ireduce(I16) + sextend(ty)

    FunctionBuilder::sextend32

    fn FunctionBuilder::sextend32(self : FunctionBuilder, a : Value) -> Value

    Sign extend from 32 bits to 64 bits Similar to ireduce(I32) + sextend(I64)

    FunctionBuilder::sextend8

    fn FunctionBuilder::sextend8(self : FunctionBuilder, ty : Type, a : Value) -> Value

    Sign extend from 8 bits (in-place, keeps the same type) Similar to ireduce(I8) + sextend(ty)

    FunctionBuilder::sint_to_fcvt

    fn FunctionBuilder::sint_to_fcvt(self : FunctionBuilder, ty : Type, a : Value) -> Value

    Signed int to float

    FunctionBuilder::smulh

    fn FunctionBuilder::smulh(self : FunctionBuilder, a : Value, b : Value) -> Value

    Signed multiply high (i64 only)

    FunctionBuilder::srem

    fn FunctionBuilder::srem(self : FunctionBuilder, a : Value, b : Value) -> Value

    Signed integer remainder

    FunctionBuilder::sshr

    fn FunctionBuilder::sshr(self : FunctionBuilder, a : Value, b : Value) -> Value

    Signed shift right

    FunctionBuilder::store_ptr

    fn FunctionBuilder::store_ptr(self : FunctionBuilder, ty : Type, base : Value, value : Value, offset : Value) -> Unit

    Store to raw pointer (no bounds checking) For trampoline code that operates on host memory

    FunctionBuilder::store_ptr_narrow

    fn FunctionBuilder::store_ptr_narrow(self : FunctionBuilder, bits : Int, base : Value, value : Value, offset : Value) -> Unit

    Store narrow value to raw pointer (no bounds checking) Stores the low 'bits' bits of value to memory

    FunctionBuilder::switch_to_block

    fn FunctionBuilder::switch_to_block(self : FunctionBuilder, block : Block) -> Unit

    Switch to a different block for emitting instructions

    FunctionBuilder::trap

    fn FunctionBuilder::trap(self : FunctionBuilder, reason : String) -> Unit

    Trap/unreachable

    FunctionBuilder::udiv

    fn FunctionBuilder::udiv(self : FunctionBuilder, a : Value, b : Value) -> Value

    Unsigned integer divide

    FunctionBuilder::uextend

    fn FunctionBuilder::uextend(self : FunctionBuilder, ty : Type, a : Value) -> Value

    Zero extend (e.g., i32 -> i64)

    FunctionBuilder::uint_to_fcvt

    fn FunctionBuilder::uint_to_fcvt(self : FunctionBuilder, ty : Type, a : Value) -> Value

    Unsigned int to float

    FunctionBuilder::umulh

    fn FunctionBuilder::umulh(self : FunctionBuilder, a : Value, b : Value) -> Value

    Unsigned multiply high (i64 only)

    FunctionBuilder::urem

    fn FunctionBuilder::urem(self : FunctionBuilder, a : Value, b : Value) -> Value

    Unsigned integer remainder

    FunctionBuilder::ushr

    fn FunctionBuilder::ushr(self : FunctionBuilder, a : Value, b : Value) -> Value

    Unsigned shift right

    FunctionBuilder::v128_and

    fn FunctionBuilder::v128_and(self : FunctionBuilder, a : Value, b : Value) -> Value

    FunctionBuilder::v128_andnot

    fn FunctionBuilder::v128_andnot(self : FunctionBuilder, a : Value, b : Value) -> Value

    FunctionBuilder::v128_anytrue

    fn FunctionBuilder::v128_anytrue(self : FunctionBuilder, a : Value) -> Value

    FunctionBuilder::v128_bitselect

    fn FunctionBuilder::v128_bitselect(self : FunctionBuilder, a : Value, b : Value, c : Value) -> Value

    FunctionBuilder::v128_const

    fn FunctionBuilder::v128_const(self : FunctionBuilder, bytes : Bytes) -> Value

    v128_const - emit a V128 constant

    FunctionBuilder::v128_extract16s

    fn FunctionBuilder::v128_extract16s(self : FunctionBuilder, vec : Value, lane : Int) -> Value

    FunctionBuilder::v128_extract16u

    fn FunctionBuilder::v128_extract16u(self : FunctionBuilder, vec : Value, lane : Int) -> Value

    FunctionBuilder::v128_extract32

    fn FunctionBuilder::v128_extract32(self : FunctionBuilder, vec : Value, lane : Int) -> Value

    FunctionBuilder::v128_extract64

    fn FunctionBuilder::v128_extract64(self : FunctionBuilder, vec : Value, lane : Int) -> Value

    FunctionBuilder::v128_extract8s

    fn FunctionBuilder::v128_extract8s(self : FunctionBuilder, vec : Value, lane : Int) -> Value

    Extract a lane from a v128 value

    FunctionBuilder::v128_extract8u

    fn FunctionBuilder::v128_extract8u(self : FunctionBuilder, vec : Value, lane : Int) -> Value

    FunctionBuilder::v128_extract_f32

    fn FunctionBuilder::v128_extract_f32(self : FunctionBuilder, vec : Value, lane : Int) -> Value

    FunctionBuilder::v128_extract_f64

    fn FunctionBuilder::v128_extract_f64(self : FunctionBuilder, vec : Value, lane : Int) -> Value

    FunctionBuilder::v128_load_lane_with_addr

    fn FunctionBuilder::v128_load_lane_with_addr(self : FunctionBuilder, opcode : VectorMemoryOp, effective_addr : Value, vec : Value) -> Value

    SIMD load lane with effective address and existing vector

    FunctionBuilder::v128_load_with_addr

    fn FunctionBuilder::v128_load_with_addr(self : FunctionBuilder, opcode : VectorMemoryOp, effective_addr : Value) -> Value

    SIMD load with effective address (for complex SIMD loads)

    FunctionBuilder::v128_not

    fn FunctionBuilder::v128_not(self : FunctionBuilder, a : Value) -> Value

    Bitwise operations on v128

    FunctionBuilder::v128_or

    fn FunctionBuilder::v128_or(self : FunctionBuilder, a : Value, b : Value) -> Value

    FunctionBuilder::v128_replace16

    fn FunctionBuilder::v128_replace16(self : FunctionBuilder, vec : Value, val : Value, lane : Int) -> Value

    FunctionBuilder::v128_replace32

    fn FunctionBuilder::v128_replace32(self : FunctionBuilder, vec : Value, val : Value, lane : Int) -> Value

    FunctionBuilder::v128_replace64

    fn FunctionBuilder::v128_replace64(self : FunctionBuilder, vec : Value, val : Value, lane : Int) -> Value

    FunctionBuilder::v128_replace8

    fn FunctionBuilder::v128_replace8(self : FunctionBuilder, vec : Value, val : Value, lane : Int) -> Value

    Replace a lane in a v128 value

    FunctionBuilder::v128_replace_f32

    fn FunctionBuilder::v128_replace_f32(self : FunctionBuilder, vec : Value, val : Value, lane : Int) -> Value

    FunctionBuilder::v128_replace_f64

    fn FunctionBuilder::v128_replace_f64(self : FunctionBuilder, vec : Value, val : Value, lane : Int) -> Value

    FunctionBuilder::v128_shuffle

    fn FunctionBuilder::v128_shuffle(self : FunctionBuilder, a : Value, b : Value, lanes : FixedArray[Int]) -> Value

    Shuffle lanes from two v128 values

    FunctionBuilder::v128_splat16

    fn FunctionBuilder::v128_splat16(self : FunctionBuilder, val : Value) -> Value

    FunctionBuilder::v128_splat32

    fn FunctionBuilder::v128_splat32(self : FunctionBuilder, val : Value) -> Value

    FunctionBuilder::v128_splat64

    fn FunctionBuilder::v128_splat64(self : FunctionBuilder, val : Value) -> Value

    FunctionBuilder::v128_splat8

    fn FunctionBuilder::v128_splat8(self : FunctionBuilder, val : Value) -> Value

    v128_splat - broadcast a scalar to all lanes

    FunctionBuilder::v128_splat_f32

    fn FunctionBuilder::v128_splat_f32(self : FunctionBuilder, val : Value) -> Value

    FunctionBuilder::v128_splat_f64

    fn FunctionBuilder::v128_splat_f64(self : FunctionBuilder, val : Value) -> Value

    FunctionBuilder::v128_store_lane_with_addr

    fn FunctionBuilder::v128_store_lane_with_addr(self : FunctionBuilder, opcode : VectorMemoryOp, effective_addr : Value, vec : Value) -> Unit

    SIMD store lane with effective address and vector (void)

    FunctionBuilder::v128_swizzle

    fn FunctionBuilder::v128_swizzle(self : FunctionBuilder, a : Value, b : Value) -> Value

    Swizzle lanes using indices from another v128

    FunctionBuilder::v128_xor

    fn FunctionBuilder::v128_xor(self : FunctionBuilder, a : Value, b : Value) -> Value

    GlobalValue

    pub struct GlobalValue {
    id : Int
    // private fields
    }

    Function-owned handle to an interned global-value declaration.
    impl Eq for GlobalValue
    impl Hash for GlobalValue

    GlobalValue::equal

    fn GlobalValue::equal(self : GlobalValue, other : GlobalValue) -> Bool

    GlobalValue::hash

    fn GlobalValue::hash(self : GlobalValue) -> Int

    GlobalValue::hash_combine

    fn GlobalValue::hash_combine(self : GlobalValue, hasher : Hasher) -> Unit

    GlobalValue::not_equal

    fn GlobalValue::not_equal(x : GlobalValue, y : GlobalValue) -> Bool

    GlobalValue::to_repr

    GlobalValueData

    pub(all) enum GlobalValueData {
    ContextField(ContextField, GlobalValueStability, AliasRegion)
    } derive(Eq, Hash,
    Debug
    )

    Function-scoped global-value declaration.

    GlobalValueData::context_field

    fn GlobalValueData::context_field(field : ContextField, stability : GlobalValueStability, result_region : AliasRegion) -> GlobalValueData

    GlobalValueData::equal

    GlobalValueData::hash

    fn GlobalValueData::hash(self : GlobalValueData) -> Int

    GlobalValueData::hash_combine

    fn GlobalValueData::hash_combine(GlobalValueData, Hasher) -> Unit

    GlobalValueData::not_equal

    fn GlobalValueData::not_equal(x : GlobalValueData, y : GlobalValueData) -> Bool

    GlobalValueStability

    pub(all) enum GlobalValueStability {
    Stable
    Mutable
    } derive(Eq, Hash,
    Debug
    )

    Whether a context field may change while the function is executing.

    Stable grants reuse permission for one invocation. Mutable requires invalidation at calls and writes that may affect the context region.

    GlobalValueStability::equal

    GlobalValueStability::hash

    GlobalValueStability::hash_combine

    GlobalValueStability::not_equal

    Inst

    pub struct Inst {
    id : Int
    results : Array[Value]
    opcode : Opcode
    args : Array[Value]
    operands : Array[Value]
    metadata : Array[Metadata]
    // private fields
    }

    Instruction - an SSA instruction that produces a value.

    Result identities are fixed by the Function DFG. Transformations may update the shared args/operands array in place using same-function values, but must preserve opcode arity and type contracts. Use set_opcode to change the operation and add_metadata to attach metadata; do not mutate results. Construct instructions through FunctionBuilder or Function::new_inst.
    impl Eq for Inst
    impl Hash for Inst

    Inst::add_metadata

    fn Inst::add_metadata(self : Inst, metadata : Metadata) -> Unit

    Inst::all_results

    fn Inst::all_results(self : Inst) -> Array[Value]

    Get all results of this instruction

    Inst::equal

    fn Inst::equal(self : Inst, other : Inst) -> Bool

    Inst::first_result

    fn Inst::first_result(self : Inst) -> Value?

    Get primary result of this instruction (first result or None)

    Inst::hash

    fn Inst::hash(self : Inst) -> Int

    Inst::hash_combine

    fn Inst::hash_combine(self : Inst, hasher : Hasher) -> Unit

    Inst::not_equal

    fn Inst::not_equal(x : Inst, y : Inst) -> Bool

    Inst::set_opcode

    fn Inst::set_opcode(self : Inst, opcode : Opcode) -> Unit

    Change the operation without replacing result identities. Operand/result arity and types must match the new opcode when the function is verified.

    Inst::to_repr

    IntBinaryOp

    pub(all) enum IntBinaryOp {
    Add
    Sub
    Mul
    UnsignedMulHigh
    SignedMulHigh
    SignedDiv
    UnsignedDiv
    SignedRem
    UnsignedRem
    And
    Or
    Xor
    ShiftLeft
    SignedShiftRight
    UnsignedShiftRight
    RotateLeft
    RotateRight
    } derive(Eq, Hash,
    Debug
    )

    IntBinaryOp::equal

    fn IntBinaryOp::equal(IntBinaryOp, IntBinaryOp) -> Bool

    IntBinaryOp::hash

    fn IntBinaryOp::hash(self : IntBinaryOp) -> Int

    IntBinaryOp::hash_combine

    fn IntBinaryOp::hash_combine(IntBinaryOp, Hasher) -> Unit

    IntBinaryOp::not_equal

    fn IntBinaryOp::not_equal(x : IntBinaryOp, y : IntBinaryOp) -> Bool

    IntCC

    pub(all) enum IntCC {
    Eq
    Ne
    Slt
    Sle
    Sgt
    Sge
    Ult
    Ule
    Ugt
    Uge
    } derive(Eq, Hash,
    Debug
    )

    Integer comparison condition codes

    IntCC::equal

    fn IntCC::equal(IntCC, IntCC) -> Bool

    IntCC::hash

    fn IntCC::hash(self : IntCC) -> Int

    IntCC::hash_combine

    fn IntCC::hash_combine(IntCC, Hasher) -> Unit

    IntCC::not_equal

    fn IntCC::not_equal(x : IntCC, y : IntCC) -> Bool

    IntCC::to_repr

    IntUnaryOp

    pub(all) enum IntUnaryOp {
    Not
    CountLeadingZeros
    CountTrailingZeros
    PopulationCount
    } derive(Eq, Hash,
    Debug
    )

    IntUnaryOp::equal

    fn IntUnaryOp::equal(IntUnaryOp, IntUnaryOp) -> Bool

    IntUnaryOp::hash

    fn IntUnaryOp::hash(self : IntUnaryOp) -> Int

    IntUnaryOp::hash_combine

    fn IntUnaryOp::hash_combine(IntUnaryOp, Hasher) -> Unit

    IntUnaryOp::not_equal

    fn IntUnaryOp::not_equal(x : IntUnaryOp, y : IntUnaryOp) -> Bool

    Loop

    pub struct Loop {
    header : Int
    blocks : Array[Int]
    back_edges : Array[(Int, Int)]
    } derive(
    Debug
    )

    Loop::contains

    fn Loop::contains(self : Loop, block_id : Int) -> Bool

    Loop::to_repr

    MemoryOp

    pub(all) enum MemoryOp {
    Load(Type)
    Store(Type)
    LoadNarrow(Type, Int, Bool)
    StoreNarrow(Int)
    Vector(VectorMemoryOp)
    } derive(Eq, Hash,
    Debug
    )

    Generic memory semantics over an explicit address and offset operand.

    MemoryOp::equal

    fn MemoryOp::equal(MemoryOp, MemoryOp) -> Bool

    MemoryOp::hash

    fn MemoryOp::hash(self : MemoryOp) -> Int

    MemoryOp::hash_combine

    fn MemoryOp::hash_combine(MemoryOp, Hasher) -> Unit

    MemoryOp::not_equal

    fn MemoryOp::not_equal(x : MemoryOp, y : MemoryOp) -> Bool

    MemoryOp::to_repr

    Metadata

    pub(all) enum Metadata {
    SourceLoc(String)
    Comment(String)
    } derive(Eq,
    Debug
    )

    Metadata::equal

    fn Metadata::equal(Metadata, Metadata) -> Bool

    Metadata::not_equal

    fn Metadata::not_equal(x : Metadata, y : Metadata) -> Bool

    Metadata::to_repr

    Opcode

    pub(all) enum Opcode {
    Scalar(ScalarOp)
    Call(CallOp)
    Memory(MemoryOp)
    GlobalValue(GlobalValue)
    Ext(ExtOp, Signature)
    Vector(VectorOp)
    } derive(Eq, Hash,
    Debug
    )

    Opcode - the operation performed by an instruction
    impl Show for Opcode

    Opcode::equal

    fn Opcode::equal(Opcode, Opcode) -> Bool

    Opcode::hash

    fn Opcode::hash(self : Opcode) -> Int

    Opcode::hash_combine

    fn Opcode::hash_combine(Opcode, Hasher) -> Unit

    Opcode::not_equal

    fn Opcode::not_equal(x : Opcode, y : Opcode) -> Bool

    Opcode::output

    fn Opcode::output(self : Opcode, logger : &Logger) -> Unit

    Opcode::to_repr

    Opcode::to_string

    fn Opcode::to_string(self : Opcode) -> String

    ScalarOp

    pub(all) enum ScalarOp {
    IntConst(Int64)
    FloatConst32(UInt)
    FloatConst64(UInt64)
    IntBinary(IntBinaryOp)
    IntUnary(IntUnaryOp)
    IntCompare(IntCC)
    FloatBinary(FloatBinaryOp)
    FloatUnary(FloatUnaryOp)
    FloatCompare(FloatCC)
    Convert(ConversionOp)
    SignExtendFrom(Int)
    Select
    Copy
    } derive(Eq, Hash,
    Debug
    )

    ScalarOp::equal

    fn ScalarOp::equal(ScalarOp, ScalarOp) -> Bool

    ScalarOp::hash

    fn ScalarOp::hash(self : ScalarOp) -> Int

    ScalarOp::hash_combine

    fn ScalarOp::hash_combine(ScalarOp, Hasher) -> Unit

    ScalarOp::not_equal

    fn ScalarOp::not_equal(x : ScalarOp, y : ScalarOp) -> Bool

    ScalarOp::to_repr

    Signature

    pub struct Signature {
    params : Array[Type]
    results : Array[Type]
    } derive(Eq, Hash,
    Debug
    )

    Signature::Signature

    fn Signature::Signature(params : Array[Type], results : Array[Type]) -> Signature

    Signature::equal

    fn Signature::equal(Signature, Signature) -> Bool

    Signature::hash

    fn Signature::hash(self : Signature) -> Int

    Signature::hash_combine

    fn Signature::hash_combine(Signature, Hasher) -> Unit

    Signature::not_equal

    fn Signature::not_equal(x : Signature, y : Signature) -> Bool

    Terminator

    pub(all) enum Terminator {
    Jump(Int, Array[Value])
    Branch(Value, Int, Array[Value], Int, Array[Value])
    Brz(Value, Int, Int)
    Brnz(Value, Int, Int)
    BrTable(Value, Array[Int], Int)
    Return(Array[Value])
    Trap(String)
    TrapExit(String)
    } derive(
    Debug
    )

    Terminator - how a basic block ends

    Terminator::targets

    fn Terminator::targets(self : Terminator) -> Array[Int]

    Return successor block IDs in terminator order, including duplicate edges.

    Type

    pub(all) enum Type {
    I32
    I64
    F32
    F64
    V128
    Ptr
    Ref
    CallableRef
    OpaqueRef
    } derive(Eq, Hash,
    Debug
    )

    IR type system for scalar, vector, pointer, and reference-typed values.

    Ptr, Ref, CallableRef, and OpaqueRef are fixed-width 64-bit carriers. Their representation does not vary with the host pointer width.
    impl Show for Type

    Type::equal

    fn Type::equal(Type, Type) -> Bool

    Type::hash

    fn Type::hash(self : Type) -> Int

    Type::hash_combine

    fn Type::hash_combine(Type, Hasher) -> Unit

    Type::not_equal

    fn Type::not_equal(x : Type, y : Type) -> Bool

    Type::output

    fn Type::output(self : Type, logger : &Logger) -> Unit

    Type::to_repr

    Type::to_string

    fn Type::to_string(self : Type) -> String

    Value

    pub struct Value {
    id : Int
    ty : Type
    // private fields
    }

    IR Value - represents a virtual register in SSA form Each value is defined exactly once and can be used multiple times
    impl Eq for Value
    impl Hash for Value

    Value::equal

    fn Value::equal(self : Value, other : Value) -> Bool

    Value::hash

    fn Value::hash(self : Value) -> Int

    Value::hash_combine

    fn Value::hash_combine(self : Value, hasher : Hasher) -> Unit

    Value::not_equal

    fn Value::not_equal(x : Value, y : Value) -> Bool

    Value::to_repr

    VectorBitwiseOp

    pub(all) enum VectorBitwiseOp {
    Not
    And
    AndNot
    Or
    Xor
    Bitselect
    } derive(Eq, Hash,
    Debug
    )

    VectorBitwiseOp::equal

    VectorBitwiseOp::hash

    fn VectorBitwiseOp::hash(self : VectorBitwiseOp) -> Int

    VectorBitwiseOp::hash_combine

    fn VectorBitwiseOp::hash_combine(VectorBitwiseOp, Hasher) -> Unit

    VectorBitwiseOp::not_equal

    fn VectorBitwiseOp::not_equal(x : VectorBitwiseOp, y : VectorBitwiseOp) -> Bool

    VectorConversionOp

    pub(all) enum VectorConversionOp {
    TruncSatF32ToI32(VectorSignedness)
    TruncSatF64ToI32Zero(VectorSignedness)
    ConvertI32ToF32(VectorSignedness)
    ConvertLowI32ToF64(VectorSignedness)
    DemoteF64ToF32Zero
    PromoteLowF32ToF64
    } derive(Eq, Hash,
    Debug
    )

    VectorConversionOp::equal

    VectorConversionOp::hash

    fn VectorConversionOp::hash(self : VectorConversionOp) -> Int

    VectorConversionOp::hash_combine

    fn VectorConversionOp::hash_combine(VectorConversionOp, Hasher) -> Unit

    VectorConversionOp::not_equal

    VectorExtension

    pub(all) enum VectorExtension {
    None
    Signed
    Unsigned
    } derive(Eq, Hash,
    Debug
    )

    VectorExtension::equal

    VectorExtension::hash

    fn VectorExtension::hash(self : VectorExtension) -> Int

    VectorExtension::hash_combine

    fn VectorExtension::hash_combine(VectorExtension, Hasher) -> Unit

    VectorExtension::not_equal

    fn VectorExtension::not_equal(x : VectorExtension, y : VectorExtension) -> Bool

    VectorFloatBinaryOp

    pub(all) enum VectorFloatBinaryOp {
    Add
    Sub
    Mul
    Div
    Min
    Max
    PseudoMin
    PseudoMax
    } derive(Eq, Hash,
    Debug
    )

    VectorFloatBinaryOp::equal

    VectorFloatBinaryOp::hash

    VectorFloatBinaryOp::hash_combine

    VectorFloatBinaryOp::not_equal

    VectorFloatCompareOp

    pub(all) enum VectorFloatCompareOp {
    Eq
    Ne
    Lt
    Gt
    Le
    Ge
    } derive(Eq, Hash,
    Debug
    )

    VectorFloatCompareOp::equal

    VectorFloatCompareOp::hash

    VectorFloatCompareOp::hash_combine

    VectorFloatCompareOp::not_equal

    VectorFloatLane

    pub(all) enum VectorFloatLane {
    F32
    F64
    } derive(Eq, Hash,
    Debug
    )

    VectorFloatLane::equal

    VectorFloatLane::hash

    fn VectorFloatLane::hash(self : VectorFloatLane) -> Int

    VectorFloatLane::hash_combine

    fn VectorFloatLane::hash_combine(VectorFloatLane, Hasher) -> Unit

    VectorFloatLane::not_equal

    fn VectorFloatLane::not_equal(x : VectorFloatLane, y : VectorFloatLane) -> Bool

    VectorFloatUnaryOp

    pub(all) enum VectorFloatUnaryOp {
    Abs
    Neg
    Sqrt
    Ceil
    Floor
    Trunc
    Nearest
    } derive(Eq, Hash,
    Debug
    )

    VectorFloatUnaryOp::equal

    VectorFloatUnaryOp::hash

    fn VectorFloatUnaryOp::hash(self : VectorFloatUnaryOp) -> Int

    VectorFloatUnaryOp::hash_combine

    fn VectorFloatUnaryOp::hash_combine(VectorFloatUnaryOp, Hasher) -> Unit

    VectorFloatUnaryOp::not_equal

    VectorFmaOp

    pub(all) enum VectorFmaOp {
    Add
    NegatedAdd
    } derive(Eq, Hash,
    Debug
    )

    VectorFmaOp::equal

    fn VectorFmaOp::equal(VectorFmaOp, VectorFmaOp) -> Bool

    VectorFmaOp::hash

    fn VectorFmaOp::hash(self : VectorFmaOp) -> Int

    VectorFmaOp::hash_combine

    fn VectorFmaOp::hash_combine(VectorFmaOp, Hasher) -> Unit

    VectorFmaOp::not_equal

    fn VectorFmaOp::not_equal(x : VectorFmaOp, y : VectorFmaOp) -> Bool

    VectorHalf

    pub(all) enum VectorHalf {
    Low
    High
    } derive(Eq, Hash,
    Debug
    )

    VectorHalf::equal

    fn VectorHalf::equal(VectorHalf, VectorHalf) -> Bool

    VectorHalf::hash

    fn VectorHalf::hash(self : VectorHalf) -> Int

    VectorHalf::hash_combine

    fn VectorHalf::hash_combine(VectorHalf, Hasher) -> Unit

    VectorHalf::not_equal

    fn VectorHalf::not_equal(x : VectorHalf, y : VectorHalf) -> Bool

    VectorIntBinaryOp

    pub(all) enum VectorIntBinaryOp {
    Add
    Sub
    Mul
    AddSaturating(VectorSignedness)
    SubSaturating(VectorSignedness)
    Min(VectorSignedness)
    Max(VectorSignedness)
    AverageUnsigned
    ExtMul(VectorHalf, VectorSignedness)
    Dot16To32Signed
    Q15MulrSaturating
    } derive(Eq, Hash,
    Debug
    )

    VectorIntBinaryOp::equal

    VectorIntBinaryOp::hash

    fn VectorIntBinaryOp::hash(self : VectorIntBinaryOp) -> Int

    VectorIntBinaryOp::hash_combine

    fn VectorIntBinaryOp::hash_combine(VectorIntBinaryOp, Hasher) -> Unit

    VectorIntBinaryOp::not_equal

    fn VectorIntBinaryOp::not_equal(x : VectorIntBinaryOp, y : VectorIntBinaryOp) -> Bool

    VectorIntCompareOp

    pub(all) enum VectorIntCompareOp {
    Eq
    Ne
    Lt(VectorSignedness)
    Gt(VectorSignedness)
    Le(VectorSignedness)
    Ge(VectorSignedness)
    } derive(Eq, Hash,
    Debug
    )

    VectorIntCompareOp::equal

    VectorIntCompareOp::hash

    fn VectorIntCompareOp::hash(self : VectorIntCompareOp) -> Int

    VectorIntCompareOp::hash_combine

    fn VectorIntCompareOp::hash_combine(VectorIntCompareOp, Hasher) -> Unit

    VectorIntCompareOp::not_equal

    VectorIntLane

    pub(all) enum VectorIntLane {
    I8
    I16
    I32
    I64
    } derive(Eq, Hash,
    Debug
    )

    VectorIntLane::equal

    VectorIntLane::hash

    fn VectorIntLane::hash(self : VectorIntLane) -> Int

    VectorIntLane::hash_combine

    fn VectorIntLane::hash_combine(VectorIntLane, Hasher) -> Unit

    VectorIntLane::not_equal

    fn VectorIntLane::not_equal(x : VectorIntLane, y : VectorIntLane) -> Bool

    VectorIntShiftOp

    pub(all) enum VectorIntShiftOp {
    Left
    Right(VectorSignedness)
    } derive(Eq, Hash,
    Debug
    )

    VectorIntShiftOp::equal

    VectorIntShiftOp::hash

    fn VectorIntShiftOp::hash(self : VectorIntShiftOp) -> Int

    VectorIntShiftOp::hash_combine

    fn VectorIntShiftOp::hash_combine(VectorIntShiftOp, Hasher) -> Unit

    VectorIntShiftOp::not_equal

    fn VectorIntShiftOp::not_equal(x : VectorIntShiftOp, y : VectorIntShiftOp) -> Bool

    VectorIntUnaryOp

    pub(all) enum VectorIntUnaryOp {
    Abs
    Neg
    Popcnt
    Extend(VectorHalf, VectorSignedness)
    ExtAddPairwise(VectorSignedness)
    } derive(Eq, Hash,
    Debug
    )

    VectorIntUnaryOp::equal

    VectorIntUnaryOp::hash

    fn VectorIntUnaryOp::hash(self : VectorIntUnaryOp) -> Int

    VectorIntUnaryOp::hash_combine

    fn VectorIntUnaryOp::hash_combine(VectorIntUnaryOp, Hasher) -> Unit

    VectorIntUnaryOp::not_equal

    fn VectorIntUnaryOp::not_equal(x : VectorIntUnaryOp, y : VectorIntUnaryOp) -> Bool

    VectorLane

    pub(all) enum VectorLane {
    I8
    I16
    I32
    I64
    F32
    F64
    } derive(Eq, Hash,
    Debug
    )

    VectorLane::equal

    fn VectorLane::equal(VectorLane, VectorLane) -> Bool

    VectorLane::hash

    fn VectorLane::hash(self : VectorLane) -> Int

    VectorLane::hash_combine

    fn VectorLane::hash_combine(VectorLane, Hasher) -> Unit

    VectorLane::not_equal

    fn VectorLane::not_equal(x : VectorLane, y : VectorLane) -> Bool

    VectorMemoryOp

    pub(all) enum VectorMemoryOp {
    LoadExtend(VectorIntLane, VectorSignedness)
    LoadSplat(VectorIntLane)
    LoadZero(VectorIntLane)
    LoadLane(VectorIntLane, Int)
    StoreLane(VectorIntLane, Int)
    } derive(Eq, Hash,
    Debug
    )

    Vector memory operations consume an already checked effective address. Source-language memory indices, alignment hints, and offsets belong to the frontend.

    VectorMemoryOp::equal

    VectorMemoryOp::hash

    fn VectorMemoryOp::hash(self : VectorMemoryOp) -> Int

    VectorMemoryOp::hash_combine

    fn VectorMemoryOp::hash_combine(VectorMemoryOp, Hasher) -> Unit

    VectorMemoryOp::not_equal

    fn VectorMemoryOp::not_equal(x : VectorMemoryOp, y : VectorMemoryOp) -> Bool

    VectorOp

    pub(all) enum VectorOp {
    Const(Bytes)
    Splat(VectorLane)
    ExtractLane(VectorLane, VectorExtension, Int)
    ReplaceLane(VectorLane, Int)
    Shuffle(FixedArray[Int])
    Swizzle
    Bitwise(VectorBitwiseOp)
    Predicate(VectorPredicateOp)
    IntUnary(VectorIntUnaryOp, VectorIntLane)
    IntBinary(VectorIntBinaryOp, VectorIntLane)
    IntShift(VectorIntShiftOp, VectorIntLane)
    IntCompare(VectorIntCompareOp, VectorIntLane)
    Narrow(VectorIntLane, VectorSignedness)
    FloatUnary(VectorFloatUnaryOp, VectorFloatLane)
    FloatBinary(VectorFloatBinaryOp, VectorFloatLane)
    FloatCompare(VectorFloatCompareOp, VectorFloatLane)
    Convert(VectorConversionOp)
    Relaxed(VectorRelaxedOp)
    } derive(Eq, Hash,
    Debug
    )

    Language-neutral V128 operations normalized by semantic family.

    VectorOp::equal

    fn VectorOp::equal(VectorOp, VectorOp) -> Bool

    VectorOp::hash

    fn VectorOp::hash(self : VectorOp) -> Int

    VectorOp::hash_combine

    fn VectorOp::hash_combine(VectorOp, Hasher) -> Unit

    VectorOp::not_equal

    fn VectorOp::not_equal(x : VectorOp, y : VectorOp) -> Bool

    VectorOp::to_repr

    VectorPredicateOp

    pub(all) enum VectorPredicateOp {
    AnyTrue
    AllTrue(VectorIntLane)
    Bitmask(VectorIntLane)
    } derive(Eq, Hash,
    Debug
    )

    VectorPredicateOp::equal

    VectorPredicateOp::hash

    fn VectorPredicateOp::hash(self : VectorPredicateOp) -> Int

    VectorPredicateOp::hash_combine

    fn VectorPredicateOp::hash_combine(VectorPredicateOp, Hasher) -> Unit

    VectorPredicateOp::not_equal

    fn VectorPredicateOp::not_equal(x : VectorPredicateOp, y : VectorPredicateOp) -> Bool

    VectorRelaxedOp

    pub(all) enum VectorRelaxedOp {
    Swizzle
    TruncF32ToI32(VectorSignedness)
    TruncF64ToI32Zero(VectorSignedness)
    Fma(VectorFloatLane, VectorFmaOp)
    LaneSelect(VectorIntLane)
    Min(VectorFloatLane)
    Max(VectorFloatLane)
    Q15MulrSigned
    Dot8To16Signed
    Dot8To32AddSigned
    } derive(Eq, Hash,
    Debug
    )

    VectorRelaxedOp::equal

    VectorRelaxedOp::hash

    fn VectorRelaxedOp::hash(self : VectorRelaxedOp) -> Int

    VectorRelaxedOp::hash_combine

    fn VectorRelaxedOp::hash_combine(VectorRelaxedOp, Hasher) -> Unit

    VectorRelaxedOp::not_equal

    fn VectorRelaxedOp::not_equal(x : VectorRelaxedOp, y : VectorRelaxedOp) -> Bool

    VectorSignedness

    pub(all) enum VectorSignedness {
    Signed
    Unsigned
    } derive(Eq, Hash,
    Debug
    )

    VectorSignedness::equal

    VectorSignedness::hash

    fn VectorSignedness::hash(self : VectorSignedness) -> Int

    VectorSignedness::hash_combine

    fn VectorSignedness::hash_combine(VectorSignedness, Hasher) -> Unit

    VectorSignedness::not_equal

    fn VectorSignedness::not_equal(x : VectorSignedness, y : VectorSignedness) -> Bool

    build_dominator_tree

    fn build_dominator_tree(idom : Array[Int]) -> Array[Array[Int]]

    Build ordered child lists from an immediate-dominator table.

    visit_dominator_tree

    fn[Scope] visit_dominator_tree(domtree : Array[Array[Int]], root : Int, enter : (Int) -> (Scope, Bool), exit : (Scope) -> Unit) -> Unit

    Walk the dominator tree below root in the order a recursive pre-order descent would, without spending native stack on the depth of the tree.

    enter visits a node and returns the scope state that node introduced together with whether to descend into its children; exit receives that state once the node's whole subtree is done. Scoped passes bind on the way down and restore on the way up, so the two run in exactly the nesting a recursion gave them.

    This exists so that stack safety is settled once rather than per pass. Every dominator-tree pass here used to hand-roll its own descent, which meant "does this survive a deep function?" got answered independently at each site — and answered wrong at all but one of them (ISS-401).