CIRCT

Circuit IR Compilers and Tools

'arc' Dialect

Canonical representation of state transfer in a circuit This is the arc dialect, useful for representing state transfer functions in a circuit.

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: 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.clock_tree (circt::arc::ClockTreeOp) 

A clock tree

Syntax:

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

Traits: NoRegionArguments, NoTerminator

Operands: 

OperandDescription
clock1-bit signless integer

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

State transfer arc definition

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

Interfaces: 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.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 word from 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 word to 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: 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 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: RegionKindInterface, Symbol

Attributes: 

AttributeMLIR TypeDescription
sym_name::mlir::StringAttrstring attribute
io::mlir::TypeAttrtype attribute of a module

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

Arc terminator

Syntax:

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

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

Interfaces: ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface), RegionBranchTerminatorOpInterface

Effects: MemoryEffects::Effect{}

Operands: 

OperandDescription
outputsvariadic of any type

arc.passthrough (circt::arc::PassThroughOp) 

Clock-less logic that is on the pass-through path

Syntax:

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

Traits: NoRegionArguments, NoTerminator

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

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) 

State transfer arc

Syntax:

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

Traits: AttrSizedOperandSegments, MemRefsNormalizable

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

Results: 

ResultDescription
outputsvariadic of any type

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

Get a state’s current 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^)? attr-dict `:` type($state)

Changes the value of a state. This operation is treated as a deferred assignment by most transformation passes, which allows them to change the order of arc.state_read and arc.state_write ops on the same state without affecting the correctness of the model. The reads are always assumed to produce the current value of the state and writes to be deferred until all operations in the model have been executed for the current time step.

The only exceptions to this are the state update legalization pass, which inserts the necessary temporary variables such that writes can be performed immediately without affecting correctness. This allows later lowering passes to treat arc.state_write as an immediate assignment (without defering).

Interfaces: MemoryEffectOpInterface (MemoryEffectOpInterface)

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

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 a given name

Syntax:

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

Attributes: 

AttributeMLIR TypeDescription
name::mlir::StringAttrstring attribute

Operands: 

OperandDescription
valuesignless integer

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. Only integer types can be vectorized, no arrays or structs are allowed.

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 signless integer or vector of signless integer values of ranks 1

Results: 

ResultDescription
resultsvariadic of signless integer or vector of signless integer values of ranks 1

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
valuesignless integer or vector of signless integer values of ranks 1

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

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