CIRCT

Circuit IR Compilers and Tools

Arc Dialect

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

Rationale 

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

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

Types 

MemoryType 

Syntax:

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

Parameters: 

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

SimModelInstanceType 

Syntax:

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

Parameters: 

ParameterC++ typeDescription
modelmlir::FlatSymbolRefAttr

StateType 

Syntax:

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

Parameters: 

ParameterC++ typeDescription
type::mlir::Type

StorageType 

Syntax:

!arc.storage<
  unsigned   # size
>

Parameters: 

ParameterC++ typeDescription
sizeunsigned

Attributes 

TraceTapAttr 

Metadata of a signal with trace instrumentation

Syntax:

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

Parameters: 

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

Enums 

ZeroCountPredicate 

Arc.zero_count predicate

Cases: 

SymbolValueString
leading0leading
trailing1trailing

Interfaces 

ClockedOpInterface (ClockedOpInterface

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

Methods: 

isClocked 

static bool isClocked();

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

NOTE: This method must be implemented by the user.

getClock 

::mlir::Value getClock();

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

NOTE: This method must be implemented by the user.

eraseClock 

void eraseClock();

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

NOTE: This method must be implemented by the user.

getLatency 

uint32_t getLatency();

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

NOTE: This method must be implemented by the user.

Operations 

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

Allocate a memory

Syntax:

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

Interfaces: MemoryEffectOpInterface (MemoryEffectOpInterface)

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

Operands: 

OperandDescription
storage

Results: 

ResultDescription
memory

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

Allocate internal state

Syntax:

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

Interfaces: MemoryEffectOpInterface (MemoryEffectOpInterface)

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

Attributes: 

AttributeMLIR TypeDescription
tap::mlir::UnitAttrunit attribute

Operands: 

OperandDescription
storage

Results: 

ResultDescription
state

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

Allocate contiguous storage space from a larger storage space

Syntax:

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

Interfaces: MemoryEffectOpInterface (MemoryEffectOpInterface)

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

Attributes: 

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

Operands: 

OperandDescription
input

Results: 

ResultDescription
output

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

Calls an arc

Syntax:

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

Traits: AlwaysSpeculatableImplTrait, MemRefsNormalizable

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

Effects: MemoryEffects::Effect{}

Attributes: 

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

Operands: 

OperandDescription
inputsvariadic of any type

Results: 

ResultDescription
outputsvariadic 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: 

OperandDescription
inputsvariadic of any type
clockA type for clock-carrying wires

Results: 

ResultDescription
outputsvariadic 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: 

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

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

Execute an SSACFG region

Syntax:

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

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

Traits: IsolatedFromAbove, RecursiveMemoryEffects

Operands: 

OperandDescription
inputsvariadic of any type

Results: 

ResultDescription
resultsvariadic 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: 

OperandDescription
inputsvariadic of signless integer

Results: 

ResultDescription
outputsignless integer

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

Memory

Syntax:

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

Interfaces: MemoryEffectOpInterface (MemoryEffectOpInterface)

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

Results: 

ResultDescription
memory

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

Read a word from a memory

Syntax:

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

Interfaces: InferTypeOpInterface, MemoryEffectOpInterface (MemoryEffectOpInterface)

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

Operands: 

OperandDescription
memory
addressinteger

Results: 

ResultDescription
datainteger

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

Read port from a memory

Syntax:

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

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

Traits: AlwaysSpeculatableImplTrait

Interfaces: ConditionallySpeculatable, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{}

Operands: 

OperandDescription
memory
addressinteger

Results: 

ResultDescription
datainteger

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

Write a word to a memory

Syntax:

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

Interfaces: MemoryEffectOpInterface (MemoryEffectOpInterface)

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

Operands: 

OperandDescription
memory
addressinteger
enable1-bit signless integer
datainteger

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

Write port to a memory

Syntax:

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

Traits: AttrSizedOperandSegments

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

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

Attributes: 

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

Operands: 

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

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

A model with stratified clocks

Syntax:

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

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

Traits: IsolatedFromAbove, NoTerminator

Interfaces: SymbolUserOpInterface, Symbol

Attributes: 

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

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

Arc terminator

Syntax:

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

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

Interfaces: ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface), RegionBranchTerminatorOpInterface

Effects: MemoryEffects::Effect{}

Operands: 

OperandDescription
outputsvariadic 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: 

AttributeMLIR TypeDescription
name::mlir::StringAttrstring attribute

Operands: 

OperandDescription
storage

Results: 

ResultDescription
state

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

A root output

Syntax:

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

Interfaces: OpAsmOpInterface

Attributes: 

AttributeMLIR TypeDescription
name::mlir::StringAttrstring attribute

Operands: 

OperandDescription
storage

Results: 

ResultDescription
state

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

Provides static metadata of an Arc model used by the runtime

Syntax:

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

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

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

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

Interfaces: ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface), Symbol

Effects: MemoryEffects::Effect{}

Attributes: 

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

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

Sends a value to the simulation driver

Syntax:

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

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

Attributes: 

AttributeMLIR TypeDescription
valueName::mlir::StringAttrstring attribute

Operands: 

OperandDescription
valueany 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: 

AttributeMLIR TypeDescription
port::mlir::StringAttrstring attribute

Operands: 

OperandDescription
instance

Results: 

ResultDescription
valueany 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: 

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

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

Sets the value of an input of the model instance

Syntax:

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

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

Interfaces: MemoryEffectOpInterface (MemoryEffectOpInterface), SymbolUserOpInterface

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

Attributes: 

AttributeMLIR TypeDescription
input::mlir::StringAttrstring attribute

Operands: 

OperandDescription
instance
valueany 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: 

OperandDescription
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: 

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

Operands: 

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

Results: 

ResultDescription
outputsvariadic 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: 

OperandDescription
state

Results: 

ResultDescription
valueany 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: 

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

Operands: 

OperandDescription
state
valueany type
condition1-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: 

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

Operands: 

OperandDescription
storage

Results: 

ResultDescription
resultor 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: 

AttributeMLIR TypeDescription
names::mlir::ArrayAttrstring array attribute

Operands: 

OperandDescription
valueany 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 vector types for SIMD vectorization or plain integers for manual vectorization within a scalar register.

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

Example:

Given the following two AND operations in the IR

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

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

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

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

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

Or via vector types for SIMD vectorization:

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

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

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

or SIMD vectors.

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

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

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

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

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

Traits: IsolatedFromAbove, RecursiveMemoryEffects

Attributes: 

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

Operands: 

OperandDescription
inputsvariadic of any type

Results: 

ResultDescription
resultsvariadic 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: 

OperandDescription
valueany type

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

Leading/trailing zero count operation

Syntax:

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

Traits: AlwaysSpeculatableImplTrait, SameOperandsAndResultType

Interfaces: ConditionallySpeculatable, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{}

Attributes: 

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

Operands: 

OperandDescription
inputsignless integer

Results: 

ResultDescription
outputsignless integer

Passes 

-arc-add-taps 

Add taps to ports and wires such that they remain observable

Options 

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

-arc-allocate-state 

Allocate and layout the global simulation state

Options 

-trace-taps : Insert TraceTap attributes for signal tracing

-arc-canonicalizer 

Simulation centric canonicalizations

Statistics 

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

-arc-dedup 

Deduplicate identical arc definitions

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

Statistics 

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

-arc-find-initial-vectors 

Find initial groups of vectorizable ops

Statistics 

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

-arc-infer-memories 

Convert FIRRTL_Memory instances to dedicated memory ops

Options 

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

-arc-infer-state-properties 

Add resets and enables explicitly to the state operations

Options 

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

Statistics 

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

-arc-inline 

Inline very small arcs

Options 

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

Statistics 

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

-arc-insert-runtime 

Insert structs and function calls for the ArcRuntime library

Options 

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

-arc-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.