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.
Types ¶
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.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 type |
Results: ¶
| Result | Description |
|---|---|
outputs | variadic of any type |
arc.clock_domain (circt::arc::ClockDomainOp) ¶
A clock domain
Syntax:
operation ::= `arc.clock_domain` ` ` `(` $inputs `)` `clock` $clock attr-dict `:`
functional-type($inputs, results) $body
Traits: IsolatedFromAbove, RecursiveMemoryEffects, SingleBlockImplicitTerminator<arc::OutputOp>, SingleBlock
Interfaces: RegionKindInterface
Operands: ¶
| Operand | Description |
|---|---|
inputs | variadic of any type |
clock | A type for clock-carrying wires |
Results: ¶
| Result | Description |
|---|---|
outputs | variadic of any type |
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 type |
Results: ¶
| Result | Description |
|---|---|
results | variadic of any 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.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 (`if` $enable^)?
attr-dict `:` type($memory)
Interfaces: MemoryEffectOpInterface (MemoryEffectOpInterface)
Effects: MemoryEffects::Effect{MemoryEffects::Write on ::mlir::SideEffects::DefaultResource}
Operands: ¶
| Operand | Description |
|---|---|
memory | |
address | integer |
enable | 1-bit signless 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 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, ClockDomainOp, ExecuteOp>, ReturnLike, Terminator
Interfaces: ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface), RegionBranchTerminatorOpInterface
Effects: MemoryEffects::Effect{}
Operands: ¶
| Operand | Description |
|---|---|
outputs | variadic of any 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.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 type |
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 type |
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 type |
arc.sim.step (circt::arc::SimStepOp) ¶
Evaluates one step of the simulation for the provided model instance
Syntax:
operation ::= `arc.sim.step` $instance attr-dict `:` qualified(type($instance))
Evaluates one step of the simulation for the provided model instance, updating ports accordingly.
Interfaces: MemoryEffectOpInterface (MemoryEffectOpInterface), SymbolUserOpInterface
Effects: MemoryEffects::Effect{MemoryEffects::Read on ::mlir::SideEffects::DefaultResource, MemoryEffects::Write on ::mlir::SideEffects::DefaultResource}
Operands: ¶
| Operand | Description |
|---|---|
instance |
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 type |
initials | variadic of any type |
Results: ¶
| Result | Description |
|---|---|
outputs | variadic of any 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 type |
arc.state_write (circt::arc::StateWriteOp) ¶
Update a state’s value
Syntax:
operation ::= `arc.state_write` $state `=` $value (`if` $condition^)?
(`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 type |
condition | 1-bit signless integer |
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 |
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 type |
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 type |
Results: ¶
| Result | Description |
|---|---|
results | variadic of any 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 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-isolate-clocks ¶
Group clocked operations into clock domains
-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-clocks-to-funcs ¶
Lower clock trees into functions
-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-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.