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:
| Name | Value | Meaning |
|---|---|---|
START | 0 | First entry; the body executes from its entry block. |
| resume | 1..N | Resume at one of the body’s suspension points. |
RETURN | MAX-1 | The body returned normally; results are valid. |
HALT | MAX | The 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: ¶
| Parameter | C++ type | Description |
|---|---|---|
| elementType | ::mlir::Type | |
| size | uint64_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: ¶
| Parameter | C++ type | Description |
|---|---|---|
| coroutine | mlir::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: ¶
| Parameter | C++ type | Description |
|---|---|---|
| coroutine | mlir::FlatSymbolRefAttr |
MemoryType ¶
Syntax:
!arc.memory<
unsigned, # numWords
::mlir::IntegerType, # wordType
::mlir::IntegerType # addressType
>
Parameters: ¶
| Parameter | C++ type | Description |
|---|---|---|
| numWords | unsigned | |
| wordType | ::mlir::IntegerType | |
| addressType | ::mlir::IntegerType |
SimModelInstanceType ¶
Syntax:
!arc.sim.instance<
mlir::FlatSymbolRefAttr # model
>
Parameters: ¶
| Parameter | C++ type | Description |
|---|---|---|
| model | mlir::FlatSymbolRefAttr |
StateType ¶
Syntax:
!arc.state<
::mlir::Type # type
>
Parameters: ¶
| Parameter | C++ type | Description |
|---|---|---|
| type | ::mlir::Type |
StorageType ¶
Syntax:
!arc.storage<
unsigned # size
>
Parameters: ¶
| Parameter | C++ type | Description |
|---|---|---|
| size | unsigned |
Attributes ¶
TraceTapAttr ¶
Metadata of a signal with trace instrumentation
Syntax:
#arc.trace_tap<
mlir::TypeAttr, # sigType
uint64_t, # stateOffset
mlir::ArrayAttr # names
>
Parameters: ¶
| Parameter | C++ type | Description |
|---|---|---|
| sigType | mlir::TypeAttr | |
| stateOffset | uint64_t | |
| names | mlir::ArrayAttr |
Enums ¶
ZeroCountPredicate ¶
Arc.zero_count predicate
Cases: ¶
| Symbol | Value | String |
|---|---|---|
| leading | 0 | leading |
| trailing | 1 | trailing |
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: ¶
| Operand | Description |
|---|---|
storage |
Results: ¶
| Result | Description |
|---|---|
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: ¶
| Attribute | MLIR Type | Description |
|---|---|---|
tap | ::mlir::UnitAttr | unit attribute |
Operands: ¶
| Operand | Description |
|---|---|
storage |
Results: ¶
| Result | Description |
|---|---|
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: ¶
| Attribute | MLIR Type | Description |
|---|---|---|
offset | ::mlir::IntegerAttr | 32-bit signless integer attribute |
Operands: ¶
| Operand | Description |
|---|---|
input |
Results: ¶
| Result | Description |
|---|---|
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: ¶
| Attribute | MLIR Type | Description |
|---|---|---|
init | ::mlir::ArrayAttr | array attribute |
Results: ¶
| Result | Description |
|---|---|
output | array 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: ¶
| Operand | Description |
|---|---|
input | array reference |
source | array reference |
Results: ¶
| Result | Description |
|---|---|
output | array 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: ¶
| Operand | Description |
|---|---|
input | array reference |
elements | variadic of any non-token type |
Results: ¶
| Result | Description |
|---|---|
output | array 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: ¶
| Operand | Description |
|---|---|
input | array reference |
array | an ArrayType |
Results: ¶
| Result | Description |
|---|---|
output | array 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: ¶
| Operand | Description |
|---|---|
input | array reference |
index | index |
Results: ¶
| Result | Description |
|---|---|
value | any 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: ¶
| Operand | Description |
|---|---|
input | array reference |
index | index |
element | any non-token type |
Results: ¶
| Result | Description |
|---|---|
output | array 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: ¶
| Operand | Description |
|---|---|
input | array reference |
lowIndex | index |
Results: ¶
| Result | Description |
|---|---|
output | array 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: ¶
| Operand | Description |
|---|---|
input | array reference |
Results: ¶
| Result | Description |
|---|---|
result | an 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: ¶
| Attribute | MLIR Type | Description |
|---|---|---|
arc | ::mlir::FlatSymbolRefAttr | flat symbol reference attribute |
Operands: ¶
| Operand | Description |
|---|---|
inputs | variadic of any non-token type |
Results: ¶
| Result | Description |
|---|---|
outputs | variadic 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: ¶
| Attribute | MLIR Type | Description |
|---|---|---|
callee | ::mlir::FlatSymbolRefAttr | flat symbol reference attribute |
Operands: ¶
| Operand | Description |
|---|---|
state | State persisted between re-entries into a coroutine bound to the op’s callee symbol |
pc | Program counter of a coroutine bound to the op’s callee symbol |
args | variadic of any non-token type |
Results: ¶
| Result | Description |
|---|---|
resumeState | State persisted between re-entries into a coroutine bound to the op’s callee symbol |
resumePC | Program counter of a coroutine bound to the op’s callee symbol |
results | variadic 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: ¶
| Attribute | MLIR Type | Description |
|---|---|---|
sym_name | ::mlir::StringAttr | string attribute |
function_type | ::mlir::TypeAttr | type attribute of function type |
arg_attrs | ::mlir::ArrayAttr | Array of dictionary attributes |
res_attrs | ::mlir::ArrayAttr | Array 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: ¶
| Operand | Description |
|---|---|
yieldOperands | variadic 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: ¶
| Attribute | MLIR Type | Description |
|---|---|---|
callee | ::mlir::FlatSymbolRefAttr | flat symbol reference attribute |
Operands: ¶
| Operand | Description |
|---|---|
args | variadic of any non-token type |
Results: ¶
| Result | Description |
|---|---|
results | variadic 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: ¶
| Operand | Description |
|---|---|
pc | Program counter of a coroutine |
Results: ¶
| Result | Description |
|---|---|
result | 1-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: ¶
| Operand | Description |
|---|---|
pc | Program counter of a coroutine |
Results: ¶
| Result | Description |
|---|---|
result | 1-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: ¶
| Operand | Description |
|---|---|
yieldOperands | variadic 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: ¶
| Result | Description |
|---|---|
pc | Program 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: ¶
| Result | Description |
|---|---|
state | State 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: ¶
| Operand | Description |
|---|---|
yieldOperands | variadic of any non-token type |
destOperands | variadic of any non-token type |
Successors: ¶
| Successor | Description |
|---|---|
dest | any 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: ¶
| Operand | Description |
|---|---|
storage |
Results: ¶
| Result | Description |
|---|---|
time | 64-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: ¶
| Attribute | MLIR Type | Description |
|---|---|---|
sym_name | ::mlir::StringAttr | string attribute |
function_type | ::mlir::TypeAttr | type attribute of function type |
arg_attrs | ::mlir::ArrayAttr | Array of dictionary attributes |
res_attrs | ::mlir::ArrayAttr | Array 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: ¶
| Operand | Description |
|---|---|
inputs | variadic of any non-token type |
Results: ¶
| Result | Description |
|---|---|
results | variadic 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: ¶
| Operand | Description |
|---|---|
storage |
Results: ¶
| Result | Description |
|---|---|
time | 64-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: ¶
| Operand | Description |
|---|---|
inputs | variadic of signless integer |
Results: ¶
| Result | Description |
|---|---|
output | signless 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: ¶
| Result | Description |
|---|---|
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: ¶
| Operand | Description |
|---|---|
memory | |
address | integer |
Results: ¶
| Result | Description |
|---|---|
data | integer |
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: ¶
| Operand | Description |
|---|---|
memory | |
address | integer |
Results: ¶
| Result | Description |
|---|---|
data | integer |
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: ¶
| Operand | Description |
|---|---|
memory | |
address | integer |
data | integer |
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: ¶
| Attribute | MLIR Type | Description |
|---|---|---|
arc | ::mlir::FlatSymbolRefAttr | flat symbol reference attribute |
enable | ::mlir::UnitAttr | unit attribute |
mask | ::mlir::UnitAttr | unit attribute |
latency | ::mlir::IntegerAttr | 32-bit signless integer attribute |
Operands: ¶
| Operand | Description |
|---|---|
memory | |
inputs | variadic of any non-token type |
clock | A 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: ¶
| Attribute | MLIR Type | Description |
|---|---|---|
sym_name | ::mlir::StringAttr | string attribute |
io | ::mlir::TypeAttr | type attribute of a module |
initialFn | ::mlir::FlatSymbolRefAttr | flat symbol reference attribute |
finalFn | ::mlir::FlatSymbolRefAttr | flat symbol reference attribute |
traceTaps | ::mlir::ArrayAttr | Array 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: ¶
| Operand | Description |
|---|---|
outputs | variadic 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: ¶
| Attribute | MLIR Type | Description |
|---|---|---|
name | ::mlir::StringAttr | string attribute |
Operands: ¶
| Operand | Description |
|---|---|
storage |
Results: ¶
| Result | Description |
|---|---|
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: ¶
| Attribute | MLIR Type | Description |
|---|---|---|
name | ::mlir::StringAttr | string attribute |
Operands: ¶
| Operand | Description |
|---|---|
storage |
Results: ¶
| Result | Description |
|---|---|
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: ¶
| Attribute | MLIR Type | Description |
|---|---|---|
sym_name | ::mlir::StringAttr | string attribute |
name | ::mlir::StringAttr | string attribute |
numStateBytes | ::mlir::IntegerAttr | 64-bit signless integer attribute |
traceTaps | ::mlir::ArrayAttr | Array 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: ¶
| Operand | Description |
|---|---|
storage | |
time | 64-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: ¶
| Attribute | MLIR Type | Description |
|---|---|---|
valueName | ::mlir::StringAttr | string attribute |
Operands: ¶
| Operand | Description |
|---|---|
value | any 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: ¶
| Operand | Description |
|---|---|
instance |
Results: ¶
| Result | Description |
|---|---|
time | 64-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: ¶
| Attribute | MLIR Type | Description |
|---|---|---|
port | ::mlir::StringAttr | string attribute |
Operands: ¶
| Operand | Description |
|---|---|
instance |
Results: ¶
| Result | Description |
|---|---|
value | any 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: ¶
| Operand | Description |
|---|---|
instance |
Results: ¶
| Result | Description |
|---|---|
time | 64-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: ¶
| Attribute | MLIR Type | Description |
|---|---|---|
runtimeModel | ::mlir::FlatSymbolRefAttr | flat symbol reference attribute |
runtimeArgs | ::mlir::StringAttr | string 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: ¶
| Attribute | MLIR Type | Description |
|---|---|---|
input | ::mlir::StringAttr | string attribute |
Operands: ¶
| Operand | Description |
|---|---|
instance | |
value | any 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: ¶
| Operand | Description |
|---|---|
instance | |
time | 64-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: ¶
| Operand | Description |
|---|---|
instance | |
timePostIncrement | 64-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: ¶
| Attribute | MLIR Type | Description |
|---|---|---|
arc | ::mlir::FlatSymbolRefAttr | flat symbol reference attribute |
latency | ::mlir::IntegerAttr | 32-bit signless integer attribute |
Operands: ¶
| Operand | Description |
|---|---|
clock | A type for clock-carrying wires |
enable | 1-bit signless integer |
reset | 1-bit signless integer |
inputs | variadic of any non-token type |
initials | variadic of any non-token type |
Results: ¶
| Result | Description |
|---|---|
outputs | variadic 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: ¶
| Operand | Description |
|---|---|
state |
Results: ¶
| Result | Description |
|---|---|
value | any 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: ¶
| Attribute | MLIR Type | Description |
|---|---|---|
traceTapModel | ::mlir::FlatSymbolRefAttr | flat symbol reference attribute |
traceTapIndex | ::mlir::IntegerAttr | 64-bit unsigned integer attribute |
Operands: ¶
| Operand | Description |
|---|---|
state | |
value | any 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: ¶
| Attribute | MLIR Type | Description |
|---|---|---|
offset | ::mlir::IntegerAttr | 32-bit signless integer attribute |
Operands: ¶
| Operand | Description |
|---|---|
storage |
Results: ¶
| Result | Description |
|---|---|
result | or 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: ¶
| Attribute | MLIR Type | Description |
|---|---|---|
names | ::mlir::ArrayAttr | string array attribute |
Operands: ¶
| Operand | Description |
|---|---|
value | any 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: ¶
| Attribute | MLIR Type | Description |
|---|---|---|
success | ::mlir::BoolAttr | bool attribute |
Operands: ¶
| Operand | Description |
|---|---|
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
vectortypes 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: ¶
| Attribute | MLIR Type | Description |
|---|---|---|
inputOperandSegments | ::mlir::DenseI32ArrayAttr | i32 dense array attribute |
Operands: ¶
| Operand | Description |
|---|---|
inputs | variadic of any non-token type |
Results: ¶
| Result | Description |
|---|---|
results | variadic 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: ¶
| Operand | Description |
|---|---|
value | any 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: ¶
| Attribute | MLIR Type | Description |
|---|---|---|
predicate | circt::arc::ZeroCountPredicateAttr | arc.zero_count predicate |
Operands: ¶
| Operand | Description |
|---|---|
input | signless integer |
Results: ¶
| Result | Description |
|---|---|
output | signless 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.