Skip to content

Development Optimization Passes List

This is an automatically generated list of all internal xls developtment optimization passes available with the default opt_main. Normal optimization passes may be found here. This is generated automatically based on comments in the header files.

Warning

These passes are mostly intended for debugging and development use cases by the XLS development team and allow for the manipulation of ir files in ways that may not be desirable for general optimization use.

If the opt level is set below 'Min opt level' the pass will act as a no-op.

If the opt level is set above 'Cap opt level' the pass (or passes within the compound pass) will be executed with the opt level capped to the specified value.

Warning

Many of these passes have descriptions generated by the Gemini LLM and may not accurately reflect the behavior of the passes. As time goes on manual verification or editing of the pass descriptions may be done to improve accuracy.

assert_and_cover_removal - Assert and cover removal

Note

What follows was generated by the Gemini LLM. Not human verified.

AssertAndCoverRemovalPass (assert_and_cover_removal) is a development pass that strips all assert and cover operations from a FunctionBase.

During IR debugging, optimization investigation, or area/delay experiments, verification operations (assert and cover) can keep condition cones and token chains alive. Removing them allows developers to inspect or synthesize the pure datapath and control logic in isolation.

How It Works

The pass iterates over all nodes in the FunctionBase: * Assert nodes (Op::kAssert): Because an assert takes an input token as operand(0) and produces a token, the pass bypasses the assertion in the token graph by replacing all uses of the assert node with n->operand(0) and then removing the assert node. * Cover nodes (Op::kCover): Because a cover returns an empty tuple () and must have no users (XLS_RET_CHECK(n->users().empty())), the pass removes the cover node directly.

Any condition logic feeding only the removed assert or cover nodes becomes dead and can be cleaned up by a subsequent DeadCodeEliminationPass.

Example

// Before
fn checked_add(tkn: token, x: bits[8], y: bits[8]) -> (token, bits[8]) {
  sum: bits[8] = add(x, y)
  no_ovf: bits[1] = uge(sum, x)
  assert_tkn: token = assert(tkn, no_ovf, message="overflow", label="ovf")
  cover_max: () = cover( eq(sum, literal(value=255)), label="max_sum")
  ret result: (token, bits[8]) = tuple(assert_tkn, sum)
}

After AssertAndCoverRemovalPass:

// After
fn checked_add(tkn: token, x: bits[8], y: bits[8]) -> (token, bits[8]) {
  sum: bits[8] = add(x, y)
  no_ovf: bits[1] = uge(sum, x)
  ret result: (token, bits[8]) = tuple(tkn, sum)
}

Header

decompose_dataflow - Decompose dataflow

Decompose and flatten all compound data type dataflow. This replaces all array and tuple operations with operations on base values except directly around externally visible values.

This pass is meant for testing and measurement.

Header

literalize_zero_bits - Literalize zero bits

Note

What follows was generated by the Gemini LLM. Not human verified.

LiteralizeZeroBits (literalize_zero_bits) replaces all non-literal, non-parameter operations that produce a zero-width bit vector (bits[0]) with a constant bits[0] literal (value=0).

Because a bits[0] type carries zero bits of runtime information, any pure computation producing bits[0] always evaluates to the unique zero-bit value. Replacing such nodes with a Literal of UBits(0, 0) severs data dependencies on their operands, simplifying the IR graph and allowing dead code elimination to remove upstream logic that only feeds zero-width values.

How It Works

The pass iterates over all nodes in the FunctionBase and checks whether each node n satisfies: * n->GetType()->IsBits() with bit_count() == 0, * !n->Is<Literal>() (already a literal), and * !n->Is<Param>() (parameters are preserved so the function signature and parameter list remain valid).

Every matching node has its uses replaced with a new Literal node holding Value(UBits(0, 0)).

Example

// Before
fn f(x: bits[8]) -> bits[8] {
  empty_slice: bits[0] = bit_slice(x, start=0, width=0)
  ret result: bits[8] = concat(empty_slice, x)
}

After LiteralizeZeroBits:

// After
fn f(x: bits[8]) -> bits[8] {
  lit_zero_bits: bits[0] = literal(value=0)
  ret result: bits[8] = concat(lit_zero_bits, x)
}

Header

proc_state_legalization_shim - Proc State Legalization Pass

Compatibility shim to use the scheduling pass 'ProcStateLegalizationPass' in the optimization pass pipeline for modernizing procs.

Note

What follows was generated by the Gemini LLM. Not human verified.

ProcStateLegalizationPassShim (proc_state_legalization_shim) is an OptimizationPass wrapper around the scheduling pass ProcStateLegalizationPass. Because ProcStateLegalizationPass inherits from SchedulingFunctionBasePass and normally executes inside the scheduling pipeline, this shim constructs a package-wide SchedulingContext (SchedulingContext::CreateForWholePackage) and default SchedulingPassOptions so proc state legalization can be invoked directly from opt_main and optimization pass pipelines when modernizing or testing procs.

What the Wrapped ProcStateLegalizationPass Does

For each Proc in the package (non-proc FunctionBases are skipped), ProcStateLegalizationPass legalizes every StateElement in three steps:

  1. Mutual Exclusion Assertions (AddMutualExclusionAsserts):

    • Write Mutual Exclusion (AddNextValueMutualExclusionAssert): If a state element has two or more next_value (Next) nodes, concatenates all of their predicates (__<state>__next_value_predicates), applies one_hot(..., lsb_prio=true) and slices off the MSB, compares equality with the concatenated predicates (__<state>__at_most_one_next_value), and emits an assert labeled __<state>__at_most_one_next_value_assert verifying that at most one next_value fires in any activation.
    • Read Mutual Exclusion (AddStateReadMutualExclusionAssert): If a state element has two or more state_read nodes, similarly concatenates their predicates (substituting literal(1) for any unpredicated read) and emits an assert labeled __<state>__at_most_one_read_assert verifying that at most one state_read is active at a time.
  2. Write-Without-Read Assertions (AddWriteWithoutReadAsserts): If a state element has one or more next_value nodes and all of its state_read nodes are predicated, computes the disjunction of all read predicates (__<state>__state_read_predicates_nary_or). For each next_value node, emits an assert labeled __<state>__next_<id>_without_read_assert checking any_read_active | !next_predicate, ensuring a state update never fires when the state read is disabled.

  3. Default next_value Generation (AddDefaultNextValues): Ensures every state element has an explicit state update under all conditions:

    • If any next_value is unconditional, or if an existing no-op next_value (IsNoOpNext) already guards nor(other_predicates) (so the pass is idempotent), no default is added. (When run in the scheduling pipeline with default_next_value_z3_rlimit set, a solver can also prove the predicates exhaustive; the shim uses default SchedulingPassOptions where this limit is unset.)
    • If no next_value nodes exist for the state element, emits a default next_value(param=state, value=state_read, predicate=read_pred) for each state_read.
    • Otherwise, computes __<state>__no_explicit_next_active = nor(predicates...) and, for each state_read, adds a default next_value holding the state unchanged (value=state_read) when no_explicit_next_active (ANDed with state_read->predicate(), if present) is true.

Example

// Before
proc counter(st: bits[32], init={0}) {
  en: bits[1] = ...
  inc: bits[32] = add(st, literal(value=1))
  next_inc: () = next_value(param=st, value=inc, predicate=en)
}

After ProcStateLegalizationPassShim, an explicit default next_value is added to retain st when en is false:

// After
proc counter(st: bits[32], init={0}) {
  en: bits[1] = ...
  inc: bits[32] = add(st, literal(value=1))
  next_inc: () = next_value(param=st, value=inc, predicate=en)
  __st__no_explicit_next_active: bits[1] = not(en)
  st_default_st: () = next_value(
      param=st, value=st, predicate=__st__no_explicit_next_active)
}

Header

remove_identifiers_unsafe - UNSAFE: Remove all identifiers

A pass version of 'remove_identifers_main'. Running this pass will rewrite the package to remove the names of all functions, procs, nodes, channels, etc.

This should only be used while investigating optimizer behavior using the ir-visualizer or other tools.

Warning

This pass does not preserve externally visible names. Once run on a design the various io-constraint and other configs which rely on naming pieces of the design will not work. This pass is only for dev/ investigatory purposes.

Note

This pass does not preserve the id number of nodes.

Header

remove_one_hot_sel - One Hot Select Removal Pass

Pass which replaces OneHotSelects with equivalent Selects without regard to profitability concerns. This pass is meant for investigation and analysis use only and should not be used for normal optimization pipelines.

To avoid extreme proliferation of nodes, we only translate OneHotSelects with a selector bitwidth <= 16.

Header

to_zero_ext - DebugPass: To ZeroExt Pass

Converts any canonicalized zero-exts back from concat(zero, ...) to zero_ext since it generally makes for easier to read/visualize graphs. This pass is not meant to be used as an optimization pass at all.

Note

What follows was generated by the Gemini LLM. Not human verified.

ToZeroExtPass (to_zero_ext) is a debugging and visualization pass that converts concat operations with leading zero operands back into explicit zero_ext operations.

During standard XLS optimization pipelines, passes such as narrowing or bit slicing often canonicalize or decompose a zero_ext into a concat of a zero literal and the narrower value. While convenient for bit-level transformations, these extra zero literals and concat nodes can clutter IR graphs in visualizers and debugging dumps. ToZeroExtPass restores them to single-operand zero_ext nodes.

How It Works

  1. Query Engine Setup: Acquires a shared PartialInfoQueryEngine for the FunctionBase and iterates over nodes in topological order (context.TopoSort(f)).

  2. Matching Zero-Extension-Like Concats (ZeroExtLike::Make): For each Concat node n, the pass checks ZeroExtLike::Make(n, *qe). A Concat matches if:

    • Its total bit width is greater than zero, and
    • Every operand except the last (least-significant) operand is proven to be all zeros (qe.IsAllZeros(op)).
  3. Replacement: Replaces the matching Concat node with a zero_ext (ExtendOp with Op::kZeroExt) that extends the last operand (ext_like->to_extend()) to the full bit width of the Concat (n->BitCountOrDie()).

Example

// Before
fn extend_val(x: bits[8]) -> bits[16] {
  zeros: bits[8] = literal(value=0)
  ret ext: bits[16] = concat(zeros, x)
}

After ToZeroExtPass:

// After
fn extend_val(x: bits[8]) -> bits[16] {
  ret ext: bits[16] = zero_ext(x, new_bit_count=16)
}

Header