Skip to content

ZJIT: Dominator-scoped canonicalize for cross-block redundant guard elimination #983

Description

@dak2

Background

PR ruby#16828 introduced a block-local HIR canonicalize pass(zjit/src/hir.rs:4977-5006).
For each block in RPO, it walks instructions and rewrites each operand through union-find plus a per-block rewrite_map keyed on the most recent Guard* result for that value.
The map is cleared at every block boundary (zjit/src/hir.rs:4980).

This covers two redundant-guard shapes:

  1. Within-block consecutive guards on the same value.
  2. CFG-join via block parameters: branch-edge args (Jump,CondBranch) get rewritten before the next block's clear(),
    so guard-narrowed values flow into merge-block parameters and infer_types + fold_constants can drop the redundant join-block guards.

Gap

A third shape is not caught: a value defined and guarded in a dominator block, then used directly in a dominated block without going through a block parameter.
ZJIT's HIR (Cranelift-style) allows direct references to values defined in any dominator, so this is a common pattern — early-return chains, nested conditionals, loops, etc.

Example:

def early_return(n)
  return -1 if n < 0    # bb0: GuardType %n (for FixnumLess)
  return  0 if n == 0   # bb2: GuardType %n (redundant)
  n * 2                 # bb4: GuardType %n (redundant)
end

In the HIR for this function, bb2 and bb4 reference %n directly (no block parameter).
I believe the block-local pass clears rewrite_map on entering them, so the %n → %3 rewrite established in bb0 is lost,
and I believe the redundant GuardType instructions in bb2 and bb4 survive all the way to machine code.

Follow-up from PR ruby#16828 review

@tekknolagi suggested this extension during PR review:

This can later (not in this PR, please) be extended to walk the dominator tree using a scoped hashmap or something so that we get cross-block results that don't correspond to block parameters.

(ruby#16828 (comment))

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions