CIRCT

Circuit IR Compilers and Tools

Arc Dialect

This dialect provides operations and types to represent state transfer functions in a circuit, enabling efficient scheduling of operations for simulation.

Rationale 

The main goal of the Arc dialect is to provide an intermediate representation of hardware designs that is optimized for simulation. It transforms hardware descriptions from the HW, Seq, and Comb dialects into a form where all module hierarchies have been flattened, combinational logic is represented as callable “arcs” (state transfer functions), and sequential elements are modeled explicitly.

The Arc dialect is used by the arcilator simulation tool, which compiles Arc IR to a binary object via LLVM for fast simulation.

Process and Coroutine Lowering 

LLHD distinguishes two suspendable constructs. An llhd.process defines procedural behavior inline in an hw.module; it runs once and may suspend execution at llhd.wait ops or terminate at llhd.halt. An llhd.coroutine is a separately-defined suspendable subroutine, invoked at llhd.call_coroutine sites from inside a process or another coroutine; it terminates with llhd.return.

A process is, semantically, a coroutine defined inline in a module and invoked exactly once at its definition site. Both bodies are SSACFG regions that are turned into a state machine driven by a program counter (PC), with values live across a suspension carried in persistent state. Processes and coroutines therefore share a single lowering mechanism with only minor differences.

Outlined Form 

Both constructs are rewritten into a canonical outlined form: an arc.coroutine.define definition plus one or more call sites that re-enter it. For a process, the call site is an arc.coroutine.instance placed in the enclosing hw.module. For a coroutine, each llhd.call_coroutine becomes an arc.coroutine.call inside its parent coroutine’s body. After outlining, processes and coroutines are no longer distinguished. Recursive coroutines are rejected during lowering.

Program Counter 

Every coroutine uses the same PC encoding:

NameValueMeaning
START0First entry; the body executes from its entry block.
resume1..NResume at one of the body’s suspension points.
RETURNMAX-1The body returned normally; results are valid.
HALTMAXThe body halted; no further execution.

START = 0 matches the zero-initialized layout of fresh persistent state and requires no special initialization at runtime. Resume PCs are densely packed low integers, lowering to a single switch and keeping the per-coroutine PC width small. RETURN and HALT are shared constants across all coroutines, so call sites dispatch on completion uniformly.

Persistent State 

The state carried across a suspension op corresponds to all the SSA values that are alive from that op into the resume block. It is therefore not listed in the coroutine definition explicitly, but implied by its data and control flow structure. The persistent state only becomes explicit when calling a coroutine, since the caller needs to decide how to re-enter a coroutine.

When lowering to a concrete implementation, the persisted state is a union of structs, with one variant per resume block capturing all the live values. Multiple suspension ops targeting the same resume block share a variant.

Each resume block’s first arguments must match the coroutine’s function type. These leading arguments are supplied fresh by the caller on each resumption and are therefore not part of the persistent state. Any remaining block arguments hold the values passed as destination operands from the suspension op and are part of the persistent state. The values captured into each variant are the SSA values that are live across the suspension ops into the resume block.

When a coroutine contains an arc.coroutine.call, the callee’s state and PC are SSA values returned from the call. If the call site is itself suspended – i.e. the callee did not complete in a single eval – those values are live across the parent’s “I am inside a call” suspension point and are captured into the parent’s variant like any other block argument. State allocation is therefore compositional: the size of a coroutine’s persistent state is the size of its own union plus, transitively, the size of each callee’s persistent state at each call site. Lowering proceeds bottom-up over the call graph so that callee state sizes are known by the time a parent is lowered.

Instances and Wakeup 

arc.coroutine.instance exists only inside hw.module bodies and represents the once-per-module entry into a top-level coroutine. It guards entry into the coroutine with if (now >= my_wakeup && resume_pc != HALT). The referenced coroutine must produce an i64 wakeup time as its last result, which is not returned as a result from the instance op. The model’s next_wakeup slot is reset to UINT64_MAX by LowerState at the top of every eval body. Each arc.coroutine.instance, regardless of whether it dispatched, contributes its current stored wakeup to a min-reduction into that slot. The driver reads the slot after eval to decide when next to call the model.

Types 

ArrayRefType 

Array reference

Syntax:

!arc.arrayref<
  ::mlir::Type,   # elementType
  uint64_t   # size
>

An array reference is a reference (pointer) to an array of values.

Parameters: 

ParameterC++ typeDescription
elementType::mlir::Type
sizeuint64_t

CoroutinePCType 

Program counter of a coroutine

Syntax:

!arc.coroutine_pc<
  mlir::FlatSymbolRefAttr   # coroutine
>

An opaque program counter value indicating where to resume execution of a coroutine. The concrete size is determined by lowering.

Parameters: 

ParameterC++ typeDescription
coroutinemlir::FlatSymbolRefAttr

CoroutineStateType 

State persisted between re-entries into a coroutine

Syntax:

!arc.coroutine_state<
  mlir::FlatSymbolRefAttr   # coroutine
>

An opaque value representing the local state within a coroutine that persists between re-entries into that coroutine. The concrete layout is determined by lowering and is likely a union of structs, with each struct corresponding to a unique resumption block in the coroutine.

Parameters: 

ParameterC++ typeDescription
coroutinemlir::FlatSymbolRefAttr

MemoryType 

Syntax:

!arc.memory<
  unsigned,   # numWords
  ::mlir::IntegerType,   # wordType
  ::mlir::IntegerType   # addressType
>

Parameters: 

ParameterC++ typeDescription
numWordsunsigned
wordType::mlir::IntegerType
addressType::mlir::IntegerType

SimModelInstanceType 

Syntax:

!arc.sim.instance<
  mlir::FlatSymbolRefAttr   # model
>

Parameters: 

ParameterC++ typeDescription
modelmlir::FlatSymbolRefAttr

StateType 

Syntax:

!arc.state<
  ::mlir::Type   # type
>

Parameters: 

ParameterC++ typeDescription
type::mlir::Type

StorageType 

Syntax:

!arc.storage<
  unsigned   # size
>

Parameters: 

ParameterC++ typeDescription
sizeunsigned

Attributes 

TraceTapAttr 

Metadata of a signal with trace instrumentation

Syntax:

#arc.trace_tap<
  mlir::TypeAttr,   # sigType
  uint64_t,   # stateOffset
  mlir::ArrayAttr   # names
>

Parameters: 

ParameterC++ typeDescription
sigTypemlir::TypeAttr
stateOffsetuint64_t
namesmlir::ArrayAttr

Enums 

ZeroCountPredicate 

Arc.zero_count predicate

Cases: 

SymbolValueString
leading0leading
trailing1trailing

Interfaces 

ClockedOpInterface (ClockedOpInterface

This interface should be implemented by operations that have clocked behavior. Don’t use this interface for operations that are not themselves clocked but only define a clocked region.

Methods: 

isClocked 

static bool isClocked();

Allows non-clocked counterparts to clocked operations (e.g., arc.call) to implement this interface to simplify the implementation of some passes.

NOTE: This method must be implemented by the user.

getClock 

::mlir::Value getClock();

Returns the SSA value representing the clock signal. It is valid to return a null value if the operation is inside a clocked region and thus the clock is defined by the operation with the clocked region, or if the operation is not clocked as determined by the isClocked static function.

NOTE: This method must be implemented by the user.

eraseClock 

void eraseClock();

Removes the clock value, e.g., used when moving a clocked operation into a clocked region. If the operation already does not have a clock, this should be a nop.

NOTE: This method must be implemented by the user.

getLatency 

uint32_t getLatency();

Returns the latency w.r.t. to the clock returned by the getClock function.

NOTE: This method must be implemented by the user.

Operations 

arc.alloc_memory (circt::arc::AllocMemoryOp) 

Allocate a memory

Syntax:

operation ::= `arc.alloc_memory` $storage attr-dict `:` functional-type($storage, $memory)

Interfaces: MemoryEffectOpInterface (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{MemoryEffects::Allocate on ::mlir::SideEffects::DefaultResource}

Operands: 

OperandDescription
storage

Results: 

ResultDescription
memory

arc.alloc_state (circt::arc::AllocStateOp) 

Allocate internal state

Syntax:

operation ::= `arc.alloc_state` $storage (`tap` $tap^)? attr-dict `:` functional-type($storage, $state)

Interfaces: MemoryEffectOpInterface (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{MemoryEffects::Allocate on ::mlir::SideEffects::DefaultResource}

Attributes: 

AttributeMLIR TypeDescription
tap::mlir::UnitAttrunit attribute

Operands: 

OperandDescription
storage

Results: 

ResultDescription
state

arc.alloc_storage (circt::arc::AllocStorageOp) 

Allocate contiguous storage space from a larger storage space

Syntax:

operation ::= `arc.alloc_storage` $input (`[` $offset^ `]`)? attr-dict `:` functional-type($input, $output)

Interfaces: MemoryEffectOpInterface (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{MemoryEffects::Allocate on ::mlir::SideEffects::DefaultResource}

Attributes: 

AttributeMLIR TypeDescription
offset::mlir::IntegerAttr32-bit signless integer attribute

Operands: 

OperandDescription
input

Results: 

ResultDescription
output

arc.arrayref.alloc (circt::arc::ArrayRefAllocOp) 

Array allocation

Syntax:

operation ::= `arc.arrayref.alloc` attr-dict (`init` `(` $init^ `)`)? `:` type($output)

Allocates an array on the stack. If init is set, the array is initialized to this value. Otherwise the array’s contents are undefined.

Attributes: 

AttributeMLIR TypeDescription
init::mlir::ArrayAttrarray attribute

Results: 

ResultDescription
outputarray reference

arc.arrayref.copy (circt::arc::ArrayRefCopyOp) 

Array copy

Syntax:

operation ::= `arc.arrayref.copy` $input `=` $source attr-dict `:` type($input)

Copies the content of one array into another array. Returns the input array.

Traits: SameOperandsAndResultType

Interfaces: InferTypeOpInterface, MemoryEffectOpInterface (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{MemoryEffects::Read on ::mlir::SideEffects::DefaultResource, MemoryEffects::Write on ::mlir::SideEffects::DefaultResource}

Operands: 

OperandDescription
inputarray reference
sourcearray reference

Results: 

ResultDescription
outputarray reference

arc.arrayref.create (circt::arc::ArrayRefCreateOp) 

Array create

Syntax:

operation ::= `arc.arrayref.create` $input `=` $elements attr-dict `:` type($output)

Creates an array from a list of values. The values are ordered from most significant to least significant (the same as hw.array_create).

The input array is populated with elements and is returned.

Interfaces: InferTypeOpInterface, MemoryEffectOpInterface (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{MemoryEffects::Write on ::mlir::SideEffects::DefaultResource}

Operands: 

OperandDescription
inputarray reference
elementsvariadic of any non-token type

Results: 

ResultDescription
outputarray reference

arc.arrayref.from_array (circt::arc::ArrayRefFromArrayOp) 

Arrayref from array

Syntax:

operation ::= `arc.arrayref.from_array` $input `=` $array attr-dict `:` type($output) `,` type($array)

Populates an arrayref with the elements of an array.

Interfaces: InferTypeOpInterface, MemoryEffectOpInterface (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{MemoryEffects::Write on ::mlir::SideEffects::DefaultResource}

Operands: 

OperandDescription
inputarray reference
arrayan ArrayType

Results: 

ResultDescription
outputarray reference

arc.arrayref.get (circt::arc::ArrayRefGetOp) 

Array get

Syntax:

operation ::= `arc.arrayref.get` $input `[` $index `]` attr-dict `:` type($input) `->` type($value)

Loads a single value from an arrayref.

Interfaces: InferTypeOpInterface, MemoryEffectOpInterface (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{MemoryEffects::Read on ::mlir::SideEffects::DefaultResource}

Operands: 

OperandDescription
inputarray reference
indexindex

Results: 

ResultDescription
valueany non-token type

arc.arrayref.inject (circt::arc::ArrayRefInjectOp) 

Array inject

Syntax:

operation ::= `arc.arrayref.inject` $input `[` $index `]` `,` $element attr-dict `:` type($input) `,` type($element) `->` type($output)

Stores a single value into an arrayref.

Interfaces: InferTypeOpInterface, MemoryEffectOpInterface (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{MemoryEffects::Read on ::mlir::SideEffects::DefaultResource, MemoryEffects::Write on ::mlir::SideEffects::DefaultResource}

Operands: 

OperandDescription
inputarray reference
indexindex
elementany non-token type

Results: 

ResultDescription
outputarray reference

arc.arrayref.slice (circt::arc::ArrayRefSliceOp) 

Array slice

Syntax:

operation ::= `arc.arrayref.slice` $input`[`$lowIndex`]` attr-dict `:` functional-type($input, $output)

Slices an array into a sub-array, returning a view into the original array.

Traits: AlwaysSpeculatableImplTrait

Interfaces: ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{}

Operands: 

OperandDescription
inputarray reference
lowIndexindex

Results: 

ResultDescription
outputarray reference

arc.arrayref.to_array (circt::arc::ArrayRefToArrayOp) 

Arrayref to array

Syntax:

operation ::= `arc.arrayref.to_array` $input attr-dict `:` functional-type($input, $result)

Creates an array from an arrayref.

Interfaces: InferTypeOpInterface

Operands: 

OperandDescription
inputarray reference

Results: 

ResultDescription
resultan ArrayType

arc.call (circt::arc::CallOp) 

Calls an arc

Syntax:

operation ::= `arc.call` $arc `(` $inputs `)` attr-dict `:` functional-type(operands, results)

Traits: AlwaysSpeculatableImplTrait, MemRefsNormalizable

Interfaces: ArgAndResultAttrsOpInterface, CallOpInterface, ClockedOpInterface, ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface), SymbolUserOpInterface

Effects: MemoryEffects::Effect{}

Attributes: 

AttributeMLIR TypeDescription
arc::mlir::FlatSymbolRefAttrflat symbol reference attribute

Operands: 

OperandDescription
inputsvariadic of any non-token type

Results: 

ResultDescription
outputsvariadic of any non-token type

arc.coroutine.call (circt::arc::CoroutineCallOp) 

Call a coroutine

Syntax:

operation ::= `arc.coroutine.call` $callee `(` $state `,` $pc (`,` $args^)? `)` attr-dict
              `:` functional-type(operands, results)

Call an arc.coroutine.define. The coroutine is resumed at the point indicated by the pc operand, and local state is restored from the state operand. The args are passed into the coroutine and may differ between subsequent re-entries. When the coroutine suspends or finishes, control is transferred back to the caller and the call op returns the coroutine’s resume program counter, resume state, and the values yielded back from the coroutine as results.

Passing a return or halt program counter to the callee results in undefined behavior. These program counter values indicate that the coroutine has finished or suspended forever, respectively, and the coroutine must not be re-entered in either case.

The caller is responsible for interpreting the program counter returned from the coroutine. A return indicates that the coroutine is finished and control shall continue in the parent. A halt indicates that the coroutine suspends forever, and the parent should also return halt if it is a coroutine itself. Any other value indicates that the callee has suspended and expects to be re-entered at a later point, and the caller must suspend itself and re-enter the callee if it is a coroutine itself.

Interfaces: ArgAndResultAttrsOpInterface, CallOpInterface, SymbolUserOpInterface

Attributes: 

AttributeMLIR TypeDescription
callee::mlir::FlatSymbolRefAttrflat symbol reference attribute

Operands: 

OperandDescription
stateState persisted between re-entries into a coroutine bound to the op’s callee symbol
pcProgram counter of a coroutine bound to the op’s callee symbol
argsvariadic of any non-token type

Results: 

ResultDescription
resumeStateState persisted between re-entries into a coroutine bound to the op’s callee symbol
resumePCProgram counter of a coroutine bound to the op’s callee symbol
resultsvariadic of any non-token type

arc.coroutine.define (circt::arc::CoroutineDefineOp) 

Coroutine definition

Define a coroutine. Coroutines are suspendable functions that are entered at their entry block or re-entered at a resume block. Local state and values are persisted by an opaque state held by the caller, alongside an opaque program counter value indicating at which point the coroutine should be resumed.

Coroutines can be suspended by the arc.coroutine.yield terminator, which returns control and local state back to the caller. The caller can then re-enter the coroutine by passing that control and local state back into the coroutine. Coroutines can be finished by the arc.coroutine.return and arc.coroutine.halt terminators, which return control back to the caller with a corresponding program counter indicating return or halt.

Arguments are passed to the coroutine upon each entry. The entry block’s arguments hold the values supplied by the caller on the first call. Each block targeted by a suspension op must have its first arguments match the coroutine’s function type. On resumption, those arguments are bound to the values supplied by the caller and may differ from the values passed on prior entries.

Results are returned from the coroutine upon each suspension. Each of the terminators must provide a set of values to be yielded back to the caller upon suspension.

The local state of a coroutine is represented by !arc.coroutine_state<@A>, and the program counter for resuming by !arc.coroutine_pc<@A>. These types are opaque and are expanded to a concrete union/struct of local state and a concrete integer PC via a lowering. These types are only used on coroutine calls; coroutine definitions define them implicitly by values carried across resume points.

To nest coroutines, a coroutine definition can call another coroutine and carry the !arc.coroutine_state and !arc.coroutine_pc of that call as local state across its own suspension points.

Traits: IsolatedFromAbove

Interfaces: ArgAndResultAttrsOpInterface, CallableOpInterface, FunctionOpInterface, Symbol

Attributes: 

AttributeMLIR TypeDescription
sym_name::mlir::StringAttrstring attribute
function_type::mlir::TypeAttrtype attribute of function type
arg_attrs::mlir::ArrayAttrArray of dictionary attributes
res_attrs::mlir::ArrayAttrArray of dictionary attributes

arc.coroutine.halt (circt::arc::CoroutineHaltOp) 

Halt a coroutine permanently

Syntax:

operation ::= `arc.coroutine.halt` ($yieldOperands^ `:` type($yieldOperands))? attr-dict

Halts execution of a coroutine forever. Control effectively gets stuck indefinitely at the halt operation, also preventing all callers from making progress. Yields back a special sentinel program counter value to the caller which the caller must translate into either halting itself if it is a coroutine, or arranging for the coroutine to never be re-entered again.

Traits: HasParent<CoroutineDefineOp>, Terminator

Operands: 

OperandDescription
yieldOperandsvariadic of any non-token type

arc.coroutine.instance (circt::arc::CoroutineInstanceOp) 

Continuously run a coroutine in an hw.module

Syntax:

operation ::= `arc.coroutine.instance` $callee `(` $args `)` attr-dict `:` functional-type(operands, results)

Execute a coroutine concurrently in an hw.module. The program counter and state of the coroutine are held implicitly by the instance and passed into the coroutine when executed next. The values yielded by the coroutine are produced as results of the instance. The callee must produce a wakeup time as its last result value. This wakeup time is not exposed as a result of the instance op and is instead used to schedule the next execution.

Traits: HasParent<hw::HWModuleOp>

Interfaces: ArgAndResultAttrsOpInterface, CallOpInterface, SymbolUserOpInterface

Attributes: 

AttributeMLIR TypeDescription
callee::mlir::FlatSymbolRefAttrflat symbol reference attribute

Operands: 

OperandDescription
argsvariadic of any non-token type

Results: 

ResultDescription
resultsvariadic of any non-token type

arc.coroutine.pc_is_halt (circt::arc::CoroutinePCIsHaltOp) 

Check whether a coroutine PC is a sentinel value

Syntax:

operation ::= `arc.coroutine.pc_is_halt` $pc `:` type($pc) attr-dict

Traits: AlwaysSpeculatableImplTrait

Interfaces: ConditionallySpeculatable, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{}

Operands: 

OperandDescription
pcProgram counter of a coroutine

Results: 

ResultDescription
result1-bit signless integer

arc.coroutine.pc_is_return (circt::arc::CoroutinePCIsReturnOp) 

Check whether a coroutine PC is a sentinel value

Syntax:

operation ::= `arc.coroutine.pc_is_return` $pc `:` type($pc) attr-dict

Traits: AlwaysSpeculatableImplTrait

Interfaces: ConditionallySpeculatable, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{}

Operands: 

OperandDescription
pcProgram counter of a coroutine

Results: 

ResultDescription
result1-bit signless integer

arc.coroutine.return (circt::arc::CoroutineReturnOp) 

Return from a coroutine

Syntax:

operation ::= `arc.coroutine.return` ($yieldOperands^ `:` type($yieldOperands))? attr-dict

Returns control from a coroutine to the caller, yielding back a special sentinel program counter value indicating that the coroutine has run to completion. Additionally, the yield operands are returned to the caller and must match the result types of the coroutine.

Traits: HasParent<CoroutineDefineOp>, Terminator

Operands: 

OperandDescription
yieldOperandsvariadic of any non-token type

arc.coroutine.start_pc (circt::arc::CoroutineStartPCOp) 

Create the start sentinel coroutine program counter

Syntax:

operation ::= `arc.coroutine.start_pc` `:` type($pc) attr-dict

Produces the special start sentinel !arc.coroutine_pc value. Passing this program counter to an arc.coroutine.call enters the coroutine for the first time, executing its body from the entry block. This op provides such a value to seed the very first call into a coroutine.

Traits: AlwaysSpeculatableImplTrait

Interfaces: ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{}

Results: 

ResultDescription
pcProgram counter of a coroutine

arc.coroutine.undefined_state (circt::arc::CoroutineUndefinedStateOp) 

Create an undefined initial coroutine state

Syntax:

operation ::= `arc.coroutine.undefined_state` `:` type($state) attr-dict

Produces an undefined !arc.coroutine_state value. A coroutine establishes its local state on first entry and never reads the state passed on that first call, such that any initial state value will do. This op provides such a value to seed the very first call into a coroutine.

Traits: AlwaysSpeculatableImplTrait

Interfaces: ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{}

Results: 

ResultDescription
stateState persisted between re-entries into a coroutine

arc.coroutine.yield (circt::arc::CoroutineYieldOp) 

Suspend a coroutine and request resumption at a block

Syntax:

operation ::= `arc.coroutine.yield` (` ` `(` $yieldOperands^ `:` type($yieldOperands) `)` `,`)?
              $dest (`(` $destOperands^ `:` type($destOperands) `)`)?
              attr-dict

Suspend a coroutine. Control is transferred back to the caller, alongside a program counter value indicating the destination block, and the local state needed to restore the values live across the yield op. Additionally, the yield operands are returned to the caller and must match the result types of the coroutine.

The destination block’s first arguments must match the coroutine’s function type. On resumption, those arguments are bound to the values supplied by the caller. The remaining arguments of the destination block are bound to the destination operands of the yield op.

Traits: AttrSizedOperandSegments, HasParent<CoroutineDefineOp>, Terminator

Interfaces: BranchOpInterface

Operands: 

OperandDescription
yieldOperandsvariadic of any non-token type
destOperandsvariadic of any non-token type

Successors: 

SuccessorDescription
destany successor

arc.current_time (circt::arc::CurrentTimeOp) 

Read the current simulation time

Syntax:

operation ::= `arc.current_time` $storage attr-dict `:` qualified(type($storage))

Reads the current simulation time from the model’s storage. The time is represented as an i64 value in femtoseconds.

Interfaces: InferTypeOpInterface, MemoryEffectOpInterface (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{MemoryEffects::Read on ::mlir::SideEffects::DefaultResource}

Operands: 

OperandDescription
storage

Results: 

ResultDescription
time64-bit signless integer

arc.define (circt::arc::DefineOp) 

State transfer arc definition

Traits: HasParent<mlir::ModuleOp>, IsolatedFromAbove, SingleBlockImplicitTerminator<arc::OutputOp>, SingleBlock

Interfaces: ArgAndResultAttrsOpInterface, CallableOpInterface, FunctionOpInterface, RegionKindInterface, Symbol

Attributes: 

AttributeMLIR TypeDescription
sym_name::mlir::StringAttrstring attribute
function_type::mlir::TypeAttrtype attribute of function type
arg_attrs::mlir::ArrayAttrArray of dictionary attributes
res_attrs::mlir::ArrayAttrArray of dictionary attributes

arc.execute (circt::arc::ExecuteOp) 

Execute an SSACFG region

Syntax:

operation ::= `arc.execute` (` ` `(` $inputs^ `:` type($inputs) `)`)?
              (`->` `(` type($results)^ `)`)?
              attr-dict-with-keyword $body

The arc.execute op allows an SSACFG region to be embedded in a parent graph region, or another SSACFG region. Whenever execution reaches this op, its body region is executed and the results yielded from the body are produced as the arc.execute op’s results. The op is isolated from above. Any SSA values defined outside the op that are used inside the body have to be captured as operands and then referred to as entry block arguments in the body.

Traits: IsolatedFromAbove, RecursiveMemoryEffects

Operands: 

OperandDescription
inputsvariadic of any non-token type

Results: 

ResultDescription
resultsvariadic of any non-token type

arc.final (circt::arc::FinalOp) 

Region to be executed at the end of simulation

Syntax:

operation ::= `arc.final` attr-dict-with-keyword $body

Traits: HasParent<ModelOp>, NoRegionArguments, NoTerminator, RecursiveMemoryEffects, SingleBlock

arc.get_next_wakeup (circt::arc::GetNextWakeupOp) 

Read the model’s next wakeup time

Syntax:

operation ::= `arc.get_next_wakeup` $storage attr-dict `:` qualified(type($storage))

Reads the next wakeup time from the model’s storage. The time is represented as an i64 value in femtoseconds. A value of 0 indicates “wake up immediately”; a value of UINT64_MAX indicates that no wakeup is pending.

Interfaces: InferTypeOpInterface, MemoryEffectOpInterface (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{MemoryEffects::Read on ::mlir::SideEffects::DefaultResource}

Operands: 

OperandDescription
storage

Results: 

ResultDescription
time64-bit signless integer

arc.initial (circt::arc::InitialOp) 

Region to be executed at the start of simulation

Syntax:

operation ::= `arc.initial` attr-dict-with-keyword $body

Traits: HasParent<ModelOp>, NoRegionArguments, NoTerminator, RecursiveMemoryEffects, SingleBlock

arc.lut (circt::arc::LutOp) 

A lookup-table.

Syntax:

operation ::= `arc.lut` `(` $inputs `)` `:` functional-type($inputs, $output)
              attr-dict-with-keyword $body

Represents a lookup-table as one operation. The operations that map the lookup/input values to the corresponding table-entry are collected inside the body of this operation. Note that the operation is marked to be isolated from above to guarantee that all input values have to be passed as an operand. This allows for simpler analyses and canonicalizations of the LUT as well as lowering. Only combinational operations are allowed inside the LUT, i.e., no side-effects, state, time delays, etc.

Traits: AlwaysSpeculatableImplTrait, IsolatedFromAbove, SingleBlockImplicitTerminator<arc::OutputOp>, SingleBlock

Interfaces: ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{}

Operands: 

OperandDescription
inputsvariadic of signless integer

Results: 

ResultDescription
outputsignless integer

arc.memory (circt::arc::MemoryOp) 

Memory

Syntax:

operation ::= `arc.memory` type($memory) attr-dict

Interfaces: MemoryEffectOpInterface (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{MemoryEffects::Allocate on ::mlir::SideEffects::DefaultResource}

Results: 

ResultDescription
memory

arc.memory_read (circt::arc::MemoryReadOp) 

Read a word from a memory

Syntax:

operation ::= `arc.memory_read` $memory `[` $address `]` attr-dict `:` type($memory)

Interfaces: InferTypeOpInterface, MemoryEffectOpInterface (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{MemoryEffects::Read on ::mlir::SideEffects::DefaultResource}

Operands: 

OperandDescription
memory
addressinteger

Results: 

ResultDescription
datainteger

arc.memory_read_port (circt::arc::MemoryReadPortOp) 

Read port from a memory

Syntax:

operation ::= `arc.memory_read_port` $memory `[` $address `]` attr-dict `:` type($memory)

Represents a combinatorial memory read port. No memory read side-effect trait is necessary because at the stage of the Arc lowering where this operation is legal to be present, it is guaranteed that all reads from the same address produce the same output. This is because all writes are reordered to happen at the end of the cycle in LegalizeStateUpdates (or alternatively produce the necessary temporaries).

Traits: AlwaysSpeculatableImplTrait

Interfaces: ConditionallySpeculatable, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{}

Operands: 

OperandDescription
memory
addressinteger

Results: 

ResultDescription
datainteger

arc.memory_write (circt::arc::MemoryWriteOp) 

Write a word to a memory

Syntax:

operation ::= `arc.memory_write` $memory `[` $address `]` `,` $data attr-dict `:` type($memory)

Interfaces: MemoryEffectOpInterface (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{MemoryEffects::Write on ::mlir::SideEffects::DefaultResource}

Operands: 

OperandDescription
memory
addressinteger
datainteger

arc.memory_write_port (circt::arc::MemoryWritePortOp) 

Write port to a memory

Syntax:

operation ::= `arc.memory_write_port` $memory `,` $arc  `(` $inputs `)` (`clock` $clock^)?  (`enable` $enable^)?
              (`mask` $mask^)? `latency` $latency attr-dict `:`
              type($memory) `,` type($inputs)

Traits: AttrSizedOperandSegments

Interfaces: ArgAndResultAttrsOpInterface, CallOpInterface, ClockedOpInterface, MemoryEffectOpInterface (MemoryEffectOpInterface), SymbolUserOpInterface

Effects: MemoryEffects::Effect{MemoryEffects::Write on ::mlir::SideEffects::DefaultResource}

Attributes: 

AttributeMLIR TypeDescription
arc::mlir::FlatSymbolRefAttrflat symbol reference attribute
enable::mlir::UnitAttrunit attribute
mask::mlir::UnitAttrunit attribute
latency::mlir::IntegerAttr32-bit signless integer attribute

Operands: 

OperandDescription
memory
inputsvariadic of any non-token type
clockA type for clock-carrying wires

arc.model (circt::arc::ModelOp) 

A model with stratified clocks

Syntax:

operation ::= `arc.model` $sym_name `io` $io
              (`initializer` $initialFn^)?
              (`finalizer` $finalFn^)?
              (`traceTaps` $traceTaps^)?
              attr-dict-with-keyword $body

A model with stratified clocks. The io optional attribute specifies the I/O of the module associated to this model.

Traits: IsolatedFromAbove, NoTerminator

Interfaces: SymbolUserOpInterface, Symbol

Attributes: 

AttributeMLIR TypeDescription
sym_name::mlir::StringAttrstring attribute
io::mlir::TypeAttrtype attribute of a module
initialFn::mlir::FlatSymbolRefAttrflat symbol reference attribute
finalFn::mlir::FlatSymbolRefAttrflat symbol reference attribute
traceTaps::mlir::ArrayAttrArray of trace metadata

arc.output (circt::arc::OutputOp) 

Arc terminator

Syntax:

operation ::= `arc.output` attr-dict ($outputs^ `:` qualified(type($outputs)))?

Traits: AlwaysSpeculatableImplTrait, HasParent<DefineOp, LutOp, ExecuteOp>, ReturnLike, Terminator

Interfaces: ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface), RegionBranchTerminatorOpInterface

Effects: MemoryEffects::Effect{}

Operands: 

OperandDescription
outputsvariadic of any non-token type

arc.root_input (circt::arc::RootInputOp) 

A root input

Syntax:

operation ::= `arc.root_input` $name `,` $storage attr-dict `:` functional-type($storage, $state)

Interfaces: OpAsmOpInterface

Attributes: 

AttributeMLIR TypeDescription
name::mlir::StringAttrstring attribute

Operands: 

OperandDescription
storage

Results: 

ResultDescription
state

arc.root_output (circt::arc::RootOutputOp) 

A root output

Syntax:

operation ::= `arc.root_output` $name `,` $storage attr-dict `:` functional-type($storage, $state)

Interfaces: OpAsmOpInterface

Attributes: 

AttributeMLIR TypeDescription
name::mlir::StringAttrstring attribute

Operands: 

OperandDescription
storage

Results: 

ResultDescription
state

arc.runtime.model (circt::arc::RuntimeModelOp) 

Provides static metadata of an Arc model used by the runtime

Syntax:

operation ::= `arc.runtime.model` $sym_name $name `numStateBytes` $numStateBytes
              (`traceTaps` $traceTaps^)? attr-dict

Collection of static metadata for a specific Arc model accessed by the arcilator runtime library:

  • name: Name of the model
  • numStateBytes: Number of bytes required to store the model’s internal state
  • traceTaps: Traced signal metadata

Traits: AlwaysSpeculatableImplTrait, HasParent<::mlir::ModuleOp>

Interfaces: ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface), Symbol

Effects: MemoryEffects::Effect{}

Attributes: 

AttributeMLIR TypeDescription
sym_name::mlir::StringAttrstring attribute
name::mlir::StringAttrstring attribute
numStateBytes::mlir::IntegerAttr64-bit signless integer attribute
traceTaps::mlir::ArrayAttrArray of trace metadata

arc.set_next_wakeup (circt::arc::SetNextWakeupOp) 

Set the model’s next wakeup time

Syntax:

operation ::= `arc.set_next_wakeup` $storage `,` $time attr-dict `:` qualified(type($storage))

Writes the next wakeup time into the model’s storage. The time is represented as an i64 value in femtoseconds. The model’s eval body establishes the slot at UINT64_MAX (“no wakeup pending”) on entry, and process suspension code lowers the value to the earliest scheduled wakeup over the course of an evaluation.

Interfaces: MemoryEffectOpInterface (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{MemoryEffects::Write on ::mlir::SideEffects::DefaultResource}

Operands: 

OperandDescription
storage
time64-bit signless integer

arc.sim.emit (circt::arc::SimEmitValueOp) 

Sends a value to the simulation driver

Syntax:

operation ::= `arc.sim.emit` $valueName `,` $value attr-dict `:` type($value)

Sends a named value to the simulation driver. This is notably useful for printing values during simulation.

Attributes: 

AttributeMLIR TypeDescription
valueName::mlir::StringAttrstring attribute

Operands: 

OperandDescription
valueany non-token type

arc.sim.get_next_wakeup (circt::arc::SimGetNextWakeupOp) 

Gets the next wakeup time of the model instance

Syntax:

operation ::= `arc.sim.get_next_wakeup` $instance attr-dict `:` qualified(type($instance))

Gets the next wakeup time of the given model instance. The time is represented as an i64 value in femtoseconds. A value of 0 indicates “wake up immediately”; a value of UINT64_MAX indicates that no wakeup is pending. The slot is recomputed on every evaluation, so a driver typically reads it after each arc.sim.step to decide how far to advance simulation time before the next step.

Interfaces: InferTypeOpInterface, MemoryEffectOpInterface (MemoryEffectOpInterface), SymbolUserOpInterface

Effects: MemoryEffects::Effect{MemoryEffects::Read on ::mlir::SideEffects::DefaultResource}

Operands: 

OperandDescription
instance

Results: 

ResultDescription
time64-bit signless integer

arc.sim.get_port (circt::arc::SimGetPortOp) 

Gets the value of a port of the model instance

Syntax:

operation ::= `arc.sim.get_port` $instance `,` $port attr-dict
              `:` type($value) `,` qualified(type($instance))

Gets the value of the given port in a specific instance of a model. The provided port must be of the type of the expected value.

Interfaces: MemoryEffectOpInterface (MemoryEffectOpInterface), SymbolUserOpInterface

Effects: MemoryEffects::Effect{MemoryEffects::Read on ::mlir::SideEffects::DefaultResource}

Attributes: 

AttributeMLIR TypeDescription
port::mlir::StringAttrstring attribute

Operands: 

OperandDescription
instance

Results: 

ResultDescription
valueany non-token type

arc.sim.get_time (circt::arc::SimGetTimeOp) 

Gets the current simulation time of the model instance

Syntax:

operation ::= `arc.sim.get_time` $instance attr-dict `:` qualified(type($instance))

Gets the current simulation time of the given model instance. The time is represented as an i64 value in femtoseconds.

Interfaces: InferTypeOpInterface, MemoryEffectOpInterface (MemoryEffectOpInterface), SymbolUserOpInterface

Effects: MemoryEffects::Effect{MemoryEffects::Read on ::mlir::SideEffects::DefaultResource}

Operands: 

OperandDescription
instance

Results: 

ResultDescription
time64-bit signless integer

arc.sim.instantiate (circt::arc::SimInstantiateOp) 

Instantiates an Arc model for simulation

Creates an instance of an Arc model in scope, in order to simulate it. The model can be used from within the associated region, modelling its lifetime.

Traits: NoTerminator

Interfaces: MemoryEffectOpInterface (MemoryEffectOpInterface), SymbolUserOpInterface

Effects: MemoryEffects::Effect{MemoryEffects::Allocate on ::mlir::SideEffects::DefaultResource}

Attributes: 

AttributeMLIR TypeDescription
runtimeModel::mlir::FlatSymbolRefAttrflat symbol reference attribute
runtimeArgs::mlir::StringAttrstring attribute

arc.sim.set_input (circt::arc::SimSetInputOp) 

Sets the value of an input of the model instance

Syntax:

operation ::= `arc.sim.set_input` $instance `,` $input `=` $value attr-dict
              `:` type($value) `,` qualified(type($instance))

Sets the value of an input port in a specific instance of a model. The provided input port must be of input type on the model and its type must match the type of the value operand.

Interfaces: MemoryEffectOpInterface (MemoryEffectOpInterface), SymbolUserOpInterface

Effects: MemoryEffects::Effect{MemoryEffects::Write on ::mlir::SideEffects::DefaultResource}

Attributes: 

AttributeMLIR TypeDescription
input::mlir::StringAttrstring attribute

Operands: 

OperandDescription
instance
valueany non-token type

arc.sim.set_time (circt::arc::SimSetTimeOp) 

Sets the simulation time of the model instance

Syntax:

operation ::= `arc.sim.set_time` $instance `,` $time attr-dict `:` qualified(type($instance))

Sets the simulation time of the given model instance. The time is represented as an i64 value in femtoseconds.

Interfaces: MemoryEffectOpInterface (MemoryEffectOpInterface), SymbolUserOpInterface

Effects: MemoryEffects::Effect{MemoryEffects::Write on ::mlir::SideEffects::DefaultResource}

Operands: 

OperandDescription
instance
time64-bit signless integer

arc.sim.step (circt::arc::SimStepOp) 

Evaluates one step of the simulation for the provided model instance

Syntax:

operation ::= `arc.sim.step` $instance (`by` $timePostIncrement^)? attr-dict
              `:` qualified(type($instance))

Evaluates one step of the simulation for the provided model instance, updating ports accordingly.

If timePostIncrement is provided, the instance’s simulation time will be incremented by the given number in femtoseconds after the step has been evaluated.

Example:

%cst5  = arith.constant 5  : i64
%cst10 = arith.constant 10 : i64
arc.sim.set_time %instance, %cst10 [...]
arc.sim.step %instance by %cst5 [...]       // Step at t = 10 fs
arc.sim.step %instance [...]                // Step at t = 15 fs
arc.sim.step %instance by %cst5 [...]       // Step at t = 15 fs
arc.sim.step %instance by %cst5 [...]       // Step at t = 20 fs

Interfaces: MemoryEffectOpInterface (MemoryEffectOpInterface), SymbolUserOpInterface

Effects: MemoryEffects::Effect{MemoryEffects::Read on ::mlir::SideEffects::DefaultResource, MemoryEffects::Write on ::mlir::SideEffects::DefaultResource}

Operands: 

OperandDescription
instance
timePostIncrement64-bit signless integer

arc.state (circt::arc::StateOp) 

Instantiates a state element with input from a transfer arc

Syntax:

operation ::= `arc.state` $arc `(` $inputs `)` (`clock` $clock^)? (`enable` $enable^)?
              (`reset` $reset^)?
              ( `initial` ` ` `(` $initials^ `:` type($initials) `)`)?
              `latency` $latency attr-dict `:` functional-type($inputs, results)

Traits: AttrSizedOperandSegments, MemRefsNormalizable

Interfaces: ArgAndResultAttrsOpInterface, CallOpInterface, ClockedOpInterface, SymbolUserOpInterface

Attributes: 

AttributeMLIR TypeDescription
arc::mlir::FlatSymbolRefAttrflat symbol reference attribute
latency::mlir::IntegerAttr32-bit signless integer attribute

Operands: 

OperandDescription
clockA type for clock-carrying wires
enable1-bit signless integer
reset1-bit signless integer
inputsvariadic of any non-token type
initialsvariadic of any non-token type

Results: 

ResultDescription
outputsvariadic of any non-token type

arc.state_read (circt::arc::StateReadOp) 

Read a state’s value

Syntax:

operation ::= `arc.state_read` $state attr-dict `:` type($state)

Interfaces: InferTypeOpInterface, MemoryEffectOpInterface (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{MemoryEffects::Read on ::mlir::SideEffects::DefaultResource}

Operands: 

OperandDescription
state

Results: 

ResultDescription
valueany non-token type

arc.state_write (circt::arc::StateWriteOp) 

Update a state’s value

Syntax:

operation ::= `arc.state_write` $state `=` $value
              (`tap` $traceTapModel`[`$traceTapIndex^`]` )? attr-dict `:` type($state)

Interfaces: MemoryEffectOpInterface (MemoryEffectOpInterface), SymbolUserOpInterface

Effects: MemoryEffects::Effect{MemoryEffects::Write on ::mlir::SideEffects::DefaultResource}

Attributes: 

AttributeMLIR TypeDescription
traceTapModel::mlir::FlatSymbolRefAttrflat symbol reference attribute
traceTapIndex::mlir::IntegerAttr64-bit unsigned integer attribute

Operands: 

OperandDescription
state
valueany non-token type

arc.storage.get (circt::arc::StorageGetOp) 

Access an allocated state, memory, or storage slice

Syntax:

operation ::= `arc.storage.get` $storage `[` $offset `]` attr-dict
              `:` qualified(type($storage)) `->` type($result)

Traits: AlwaysSpeculatableImplTrait

Interfaces: ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{}

Attributes: 

AttributeMLIR TypeDescription
offset::mlir::IntegerAttr32-bit signless integer attribute

Operands: 

OperandDescription
storage

Results: 

ResultDescription
resultor or or array reference

arc.tap (circt::arc::TapOp) 

A tracker op to observe a value under one or more given names

Syntax:

operation ::= `arc.tap` $value attr-dict `:` type($value)

Attributes: 

AttributeMLIR TypeDescription
names::mlir::ArrayAttrstring array attribute

Operands: 

OperandDescription
valueany non-token type

arc.terminate (circt::arc::TerminateOp) 

Request simulation termination

Syntax:

operation ::= `arc.terminate` $storage `,` $success attr-dict `:` qualified(type($storage))

Sets a termination flag in the model’s storage. It unconditionally writes 1 for success or 2 for failure. This allows the simulation to exit gracefully after the current evaluation cycle.

Interfaces: MemoryEffectOpInterface (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{MemoryEffects::Write on ::mlir::SideEffects::DefaultResource}

Attributes: 

AttributeMLIR TypeDescription
success::mlir::BoolAttrbool attribute

Operands: 

OperandDescription
storage

arc.vectorize (circt::arc::VectorizeOp) 

Isolated subgraph of operations to be vectorized

Syntax:

operation ::= `arc.vectorize` $inputs attr-dict `:` functional-type($inputs, $results) $body

This operation represents a vectorized computation DAG. It places a convenient boundary between the subgraph to be vectorized and the surrounding non-vectorizable parts of the original graph.

This allows us to split the vectorization transformations into multiple parts/passes:

  • Finding an initial set of operations to be vectorized
  • Optimizing this set by pulling in more operations into the nested block, splitting it such that the vector width does not exceed a given limit, applying a cost model and potentially reverting the decision to vectorize this subgraph (e.g., because not enough ops could be pulled in)
  • Performing the actual vectorization by lowering this operation. This operation allows to perform the lowering of the boundary and the body separately and either via 1D vector types for SIMD vectorization or plain integers for manual vectorization within a scalar register.

For each block argument of the nested block, there is a list of operands that represent the elements of the vector. If the boundary is already vectorized each list will only contain a single SSA value of either vector type or an integer representing the concatenation of all original operands of that vector.

Example:

Given the following two AND operations in the IR

%0 = arith.and %in0, %in1 : i1
%1 = arith.and %in2, %in2 : i1

they could be vectorized by putting one such AND operation in the body block of the arc.vectorize operation and forwarding the operands accordingly.

%0:2 = arc.vectorize (%in0, %in1), (%in2, %in2) :
  (i1, i1, i1, i1) -> (i1, i1) {
^bb0(%arg0: i1, %arg1: i1):
  %1 = arith.and %arg0, %arg1 : i1
  arc.output %1 : i1
}

In a next step, the boundary could be lowered/vectorized. This can happen in terms of integers for vectorization within scalar registers:

%0 = comb.concat %in0, %in1 : i1, i1
%1 = comb.replicate %in2 : (i1) -> i2
%2 = arc.vectorize (%0), (%1) : (i2, i2) -> (i2) {
^bb0(%arg0: i1, %arg1: i1):
  %1 = arith.and %arg0, %arg1 : i1
  arc.output %1 : i1
}
%3 = comb.extract %2 from 1 : (i2) -> i1
%4 = comb.extract %2 from 0 : (i2) -> i1

Or via vector types for SIMD vectorization:

%cst = arith.constant dense<0> : vector<2xi1>
%0 = vector.insert %in0, %cst[0] : i1 into vector<2xi1>
%1 = vector.insert %in1, %0[1] : i1 into vector<2xi1>
%2 = vector.broadcast %in2 : i1 to vector<2xi1>
%3 = arc.vectorize (%1), (%2) :
  (vector<2xi1>, vector<2xi1>) -> (vector<2xi1>) {
^bb0(%arg0: i1, %arg1: i1):
  %1 = arith.and %arg0, %arg1 : i1
  arc.output %1 : i1
}
%4 = vector.extract %2[0] : vector<2xi1>
%5 = vector.extract %2[1] : vector<2xi1>

Alternatively, the body could be vectorized first. Again, as integers

%0:2 = arc.vectorize (%in0, %in1), (%in2, %in2) :
  (i1, i1, i1, i1) -> (i1, i1) {
^bb0(%arg0: i2, %arg1: i2):
  %1 = arith.and %arg0, %arg1 : i2
  arc.output %1 : i2
}

or SIMD vectors.

%0:2 = arc.vectorize (%in0, %in1), (%in2, %in3) :
  (i1, i1, i1, i1) -> (i1, i1) {
^bb0(%arg0: vector<2xi1>, %arg1: vector<2xi1>):
  %1 = arith.and %arg0, %arg1 : vector<2xi1>
  arc.output %1 : vector<2xi1>
}

Once both sides are lowered, the arc.vectorize op simply becomes a passthrough for the operands and can be removed by inlining the nested block. The integer based vectorization would then look like the following:

%0 = comb.concat %in0, %in1 : i1, i1
%1 = comb.replicate %in2 : (i1) -> i2
%2 = arith.and %0, %1 : i2
%3 = comb.extract %2 from 1 : (i2) -> i1
%4 = comb.extract %2 from 0 : (i2) -> i1

The SIMD vector based lowering would result in the following IR:

%cst = arith.constant dense<0> : vector<2xi1>
%0 = vector.insert %in0, %cst[0] : i1 into vector<2xi1>
%1 = vector.insert %in1, %0[1] : i1 into vector<2xi1>
%2 = vector.broadcast %in2 : i1 to vector<2xi1>
%3 = arith.and %1, %2 : vector<2xi1>
%4 = vector.extract %3[0] : vector<2xi1>
%5 = vector.extract %3[1] : vector<2xi1>

Traits: IsolatedFromAbove, RecursiveMemoryEffects

Attributes: 

AttributeMLIR TypeDescription
inputOperandSegments::mlir::DenseI32ArrayAttri32 dense array attribute

Operands: 

OperandDescription
inputsvariadic of any non-token type

Results: 

ResultDescription
resultsvariadic of any non-token type

arc.vectorize.return (circt::arc::VectorizeReturnOp) 

Arc.vectorized terminator

Syntax:

operation ::= `arc.vectorize.return` operands attr-dict `:` qualified(type(operands))

Traits: AlwaysSpeculatableImplTrait, HasParent<VectorizeOp>, ReturnLike, Terminator

Interfaces: ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface), RegionBranchTerminatorOpInterface

Effects: MemoryEffects::Effect{}

Operands: 

OperandDescription
valueany non-token type

arc.zero_count (circt::arc::ZeroCountOp) 

Leading/trailing zero count operation

Syntax:

operation ::= `arc.zero_count` $predicate $input attr-dict `:` type($input)

Traits: AlwaysSpeculatableImplTrait, SameOperandsAndResultType

Interfaces: ConditionallySpeculatable, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{}

Attributes: 

AttributeMLIR TypeDescription
predicatecirct::arc::ZeroCountPredicateAttrarc.zero_count predicate

Operands: 

OperandDescription
inputsignless integer

Results: 

ResultDescription
outputsignless integer

Passes 

-arc-add-taps 

Add taps to ports and wires such that they remain observable

Options 

-ports        : Make module ports observable
-wires        : Make wires observable
-named-values : Make values with `sv.namehint` observable

-arc-allocate-state 

Allocate and layout the global simulation state

Options 

-trace-taps : Insert TraceTap attributes for signal tracing

-arc-canonicalizer 

Simulation centric canonicalizations

Statistics 

num-arc-args-removed : Number of arguments removed from DefineOps

-arc-dedup 

Deduplicate identical arc definitions

This pass deduplicates identical arc definitions. If two arcs differ only by constants, the constants are outlined such that the arc can be deduplicated.

Statistics 

dedupPassNumArcsDeduped : Number of arcs deduped
dedupPassTotalOps       : Total number of ops deduped

-arc-find-initial-vectors 

Find initial groups of vectorizable ops

Statistics 

vectorizedOps       : Total number of ops that were vectorized
numOfSavedOps       : Total number of ops saved after FindInitialVectors pass
biggestSeedVector   : Size of the biggest seed vector
numOfVectorsCreated : Total number of VectorizeOps the pass inserted

-arc-infer-memories 

Convert FIRRTL_Memory instances to dedicated memory ops

Options 

-tap-ports    : Make memory ports observable
-tap-memories : Make memory contents observable

-arc-infer-state-properties 

Add resets and enables explicitly to the state operations

Options 

-enables : Infer enable signals
-resets  : Infer reset signals

Statistics 

added-enables  : Enables added explicitly to a StateOp
added-resets   : Resets added explicitly to a StateOp
missed-enables : Detected enables that could not be added explicitly to a StateOp
missed-resets  : Detected resets that could not be added explicitly to a StateOp

-arc-inline 

Inline very small arcs

Options 

-into-arcs-only : Call operations to inline
-max-body-ops   : Max number of non-trivial ops in the region to be inlined

Statistics 

inlined-arcs    : Arcs inlined at a use site
removed-arcs    : Arcs removed after full inlining
trivial-arcs    : Arcs with very few ops
single-use-arcs : Arcs with a single use

-arc-insert-runtime 

Insert structs and function calls for the ArcRuntime library

Options 

-extra-args : Extra arguments passed to the runtime when creating simulation instances
-trace-file : Output file name for signal trace

-arc-latency-retiming 

Push latencies through the design

Statistics 

num-ops-removed     : Number of zero-latency passthrough states removed
latency-units-saved : Number of latency units saved by merging them in a successor state

-arc-lower-arcs-to-funcs 

Lower arc definitions into functions

-arc-lower-arrays 

Lower arrays to use bufferized arrayrefs

-arc-lower-clocks-to-funcs 

Lower clock trees into functions

-arc-lower-coroutines 

Lower coroutines to state machine functions

Convert each arc.coroutine.define into a regular function that takes an explicit state and program counter as its leading arguments, and that dispatches to its entry block or one of its resume blocks accordingly. Values that are live across suspension points are persisted in the explicit state, which is a union with one struct variant per resume block. Calls to coroutines become regular function calls, and all uses of the opaque !arc.coroutine_state and !arc.coroutine_pc types anywhere in the IR are replaced with the concrete union and integer types computed for each coroutine.

-arc-lower-lut 

Lowers arc.lut into a comb and hw only representation.

-arc-lower-state 

Split state into read and write ops grouped by clock tree

-arc-lower-vectorizations 

Lower arc.vectorize operations

This pass lowers arc.vectorize operations. By default, the operation will be fully lowered (i.e., the op disappears in the IR). Alternatively, it can be partially lowered.

The “mode” pass option allows to only lower the boundary, only the body, or only inline the body given that both the boundary and the body are already lowered.

The pass supports vectorization within scalar registers and SIMD vectorization and prioritizes vectorization by packing the vector elements into a scalar value if it can fit into 64 bits.

Example:

hw.module @example(%in0: i8, %in1: i8, %in2: i8) -> (out0: i8, out1: i8) {
  %0:2 = arc.vectorize (%in0, %in1), (%in2, %in2) :
    (i8, i8, i8, i8) -> (i8, i8) {
  ^bb0(%arg0: i8, %arg1: i8):
    %1 = comb.and %arg0, %arg1 : i8
    arc.vectorize.return %1 : i8
  }
  hw.output %0#0, %0#1 : i8, i8
}

This piece of IR is lowered to the following fully vectorized IR:

hw.module @example(%in0: i8, %in1: i8, %in2: i8) -> (out0: i8, out1: i8) {
  %0 = comb.concat %in0, %in1 : i8, i8
  %1 = comb.concat %in2, %in2 : i8, i8
  %2 = comb.and %0, %1 : i16
  %3 = comb.extract %2 from 0 : (i16) -> i8
  %4 = comb.extract %2 from 8 : (i16) -> i8
  hw.output %3, %4 : i8, i8
}

Options 

-mode : Select what should be lowered.

-arc-lower-verif-simulations 

Lower verif.simulation ops to main functions

-arc-make-tables 

Transform appropriate arc logic into lookup tables

-arc-merge-ifs 

Merge control flow structures

This pass optimizes control flow in a few ways. It moves operations closer to their earliest user, if possible sinking them into blocks if all uses are nested in the same block. It merges adjacent scf.if operations with the same condition. And it moves operations in between two scf.if operations ahead of the first if op to allow them to be merged. The pass runs on any SSACFG regions nested under the operation it is applied to.

Note that this pass assumes that !arc.state and !arc.memory values can never alias. That is, different values are assumed to never point to the same storage location in simulation memory.

Statistics 

sunk                   : Ops sunk into blocks
moved-to-user          : Ops moved to first user
ifs-merged             : Adjacent scf.if ops merged
moved-from-between-ifs : Ops moved from between ifs to enable merging
iterations             : Number of iterations until no more ops were sunk/merged

-arc-merge-taps 

Merge TapOps observing the same value in the same Block.

-arc-mux-to-control-flow 

Convert muxes with large independent fan-ins to if-statements

-arc-print-cost-model 

A dummy pass to test analysis passes

Statistics 

Operation(s)           : Number of operations in the module
Pack operations(s)     : Number of scalar to vector packking in the module
Shuffle operation(s)   : Number of shuffles done to set up the VectorizeOps
VectorizeOps Body Cost : Number of operations inside the body of the VectorizeOps
All VectorizeOps Cost  : Total Cost of all VectorizeOps in the module

-arc-remove-i0-types 

Remove i0 types completely

i0 types are tautologous and usually exist due to array<1xT> types. i0 is not valid in LLVM-IR, so we transform array<1xT> -> T and ban i0 types.

-arc-resolve-xmr 

Resolve sv.xmr.ref into direct signal references

This pass traverses hw.hierpath metadata to replace sv.xmr.ref operations with direct SSA value references, preserving observability before flattening.

Options 

-lower-blackbox-internal-to-zero : Lower unresolved references to internal blackbox symbols to constant zero.

-arc-simplify-variadic-ops 

Convert variadic ops into distributed binary ops

Statistics 

skipped-multiple-blocks : Ops skipped due to operands in different blocks
simplified              : Ops simplified into binary ops
created                 : Ops created as part of simplification
reordered               : Ops where simplification reordered operands

-arc-split-funcs 

Split large funcs into multiple smaller funcs

Options 

-split-bound : Size threshold (in ops) above which to split funcs

Statistics 

funcs-created : Number of new functions created

-arc-split-loops 

Split arcs to break zero latency loops

Statistics 

created : Arcs created during the splitting
removed : Arcs removed during the splitting

-arc-strip-sv 

Remove SV wire, reg, and assigns

Options 

-async-resets-as-sync : Treat asynchronous resets as synchronous.