|
LLZK 2.0.0
An open-source IR for Zero Knowledge (ZK) circuits
|
LLZK array operations.
Read subarray from a multi-dimensional array
Syntax:
This operation takes an N-dimensional array and K indices and extracts the (N-K)-dimensional array by applying the given indices to the first K dimensions of the array. Error if K >= N. Use array.read instead if K == N.
Extracting a 1-D array from 3-D array by selecting the index of 2 dimensions:
Extracting 1-D arrays for subcomponents:
Traits: InferTypeOpAdaptor
Interfaces: ArrayAccessOpInterface, ArrayRefOpInterface, InferTypeOpInterface, SymbolUserOpInterface
| Operand | Description |
|---|---|
| arr_ref | n-dimensional array |
| indices | variadic of index |
| Result | Description |
|---|---|
| result | n-dimensional array |
Write subarray into a multi-dimensional array
Syntax:
This operation takes an N-dimensional array, K indices, and an (N+K)-dimensional array and inserts the N-dimensional array into the (N+K)-dimensional array at the position specified by applying the given indices to the first K dimensions of the (N+K)-dimensional array. Use array.write instead if N == 0 (LLZK array type must have at least 1 dimension so a 0-dimensional array cannot exist anyway).
Inserting 1-D arrays into a 2-D array:
Interfaces: ArrayAccessOpInterface, ArrayRefOpInterface, SymbolUserOpInterface
| Operand | Description |
|---|---|
| arr_ref | n-dimensional array |
| indices | variadic of index |
| rvalue | n-dimensional array |
Return the length of an array
Syntax:
This operation returns the size of the specified dimension of an array.
Example:
Traits: AlwaysSpeculatableImplTrait
Interfaces: ArrayRefOpInterface, ConditionallySpeculatable, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface), SymbolUserOpInterface
Effects: MemoryEffects::Effect{}
| Operand | Description |
|---|---|
| arr_ref | n-dimensional array |
| dim | index |
| Result | Description |
|---|---|
| length | index |
Create a new array
Syntax:
This operation creates a new array with the given elements. The arguments are passed as a flat array but get arranged in row-major order according the shape declared in the type.
Examples:
The values used to construct the array must have type that exactly matches the element type of the specified array type. This is true even if a tvar type is used. In other words, cannot mix tvar<@X> with tvar<@Y> or any concrete type. In such a scenario, first create an uninitialized array, as shown in the examples above, and then use array.write to write each element of the array.
Implementation note: This restriction exists due to a combination of: (1) we have chosen to infer the type of $elements from the $result ArrayType, via parseInferredArrayType(), rather than requiring the type of every element be listed in the assembly format and, (2) within the parser for an Op, there is no way to get the Value instances for the operands aside from OpAsmParser::resolveOperands() which requires the type of every operand to be known and ends up comparing the expected to actual type via operator==. Thus, there is no way for this to be successful apart from all elements having the exact type inferred in (1).
Also note that std::equal_to is used in the VariadicTypesMatchWith trait on this Op so that the verifier function mirrors the aforementioned restriction in the parser.
In some cases, the length of an uninitialized array depends on the value of the loop induction variable (i.e., each iteration creates an array with a different size/shape). In that case, an AffineMapAttr can be used to specify the dimension size in the ArrayType and the optional instantiation parameter list of this operation must be used to instatiate all AffineMap used in the array dimensions.
Examples:
Traits: AlwaysSpeculatableImplTrait, AttrSizedOperandSegments, VerifySizesForMultiAffineOps<1>
Interfaces: ConditionallySpeculatable, DestructurableAllocationOpInterface, NoMemoryEffect (MemoryEffectOpInterface), OpAsmOpInterface, PromotableAllocationOpInterface, SymbolUserOpInterface
Effects: MemoryEffects::Effect{}
| Attribute | MLIR Type | Description |
|---|---|---|
numDimsPerMap | ::mlir::DenseI32ArrayAttr | i32 dense array attribute |
mapOpGroupSizes | ::mlir::DenseI32ArrayAttr | i32 dense array attribute |
| Operand | Description |
|---|---|
| elements | variadic of a valid array element type |
| mapOperands | variadic of index |
| Result | Description |
|---|---|
| result | n-dimensional array |
Read scalar from an array
Syntax:
This operation reads the value from an array at the specified position.
Example of 1-dimensional array:
Example of 3-dimensional array:
Traits: InferTypeOpAdaptor
Interfaces: ArrayAccessOpInterface, ArrayRefOpInterface, DestructurableAccessorOpInterface, InferTypeOpInterface, PromotableMemOpInterface, SymbolUserOpInterface
| Operand | Description |
|---|---|
| arr_ref | n-dimensional array |
| indices | variadic of index |
| Result | Description |
|---|---|
| result | a valid array element type |
Write scalar to an array
Syntax:
This operation writes a value into an array at the specified position.
Example of 1-dimensional array:
Example of 2-dimensional array:
Interfaces: ArrayAccessOpInterface, ArrayRefOpInterface, DestructurableAccessorOpInterface, PromotableMemOpInterface, SymbolUserOpInterface
| Operand | Description |
|---|---|
| arr_ref | n-dimensional array |
| indices | variadic of index |
| rvalue | a valid array element type |
n-dimensional array
Syntax:
Array type with a ranked shape and homogeneous element type. It can only be instantiated with the following element types:
The dimensions of the array are specified using a list of attributes, one per dimension. Each attribute must be one of the following:
| Parameter | C++ type | Description |
|---|---|---|
| elementType | mlir::Type | Type of all elements within the array. |
| dimensionSizes | ::llvm::ArrayRef<::mlir::Attribute> | List of array dimension size specifiers. |
| shape | ::llvm::ArrayRef<int64_t> | Array shape, for ShapedTypeInterface, computed from $dimensionSizes. |
LLZK boolean operations.
Logical AND operator
Syntax:
This operation computes the logical AND (i.e., conjunction) of two i1 (i.e., boolean) values as an i1 value. The result is 1 if the operation is true and 0 otherwise.
If used in a constraint expression, this operation can be converted to a*b on felt operands.
Traits: AlwaysSpeculatableImplTrait, Commutative, NotFieldNative
Interfaces: ConditionallySpeculatable, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface)
Effects: MemoryEffects::Effect{}
| Operand | Description |
|---|---|
| lhs | 1-bit signless integer or type variable |
| rhs | 1-bit signless integer or type variable |
| Result | Description |
|---|---|
| result | 1-bit signless integer |
Assertion operation
Syntax:
This operation asserts that a given boolean value is true. Assertions are checked statically when possible. If the condition evaluates to true, the assertion is removed. If false, an error is reported. Otherwise, the assertion is preserved. All assertions that appear in constrain() functions must evaluate statically (i.e., they cannot depend on inputs to the circuit) else an error is reported.
Assertion without message:
Assertion with a message:
Interfaces: MemoryEffectOpInterface
| Attribute | MLIR Type | Description |
|---|---|---|
msg | ::mlir::StringAttr | string attribute |
| Operand | Description |
|---|---|
| condition | 1-bit signless integer |
Compare field element values
Syntax:
This operation takes two field element values and compares them according to the comparison predicate and returns an i1. The following comparisons are supported:
The result is 1 if the comparison is true and 0 otherwise.
The inequality operators (lt, gt, le, ge) for the finite field elements are defined by treating the field elements as integer values: f1 op f2 iff int(f1) op int(f2)
Example:
Traits: AlwaysSpeculatableImplTrait
Interfaces: ConditionallySpeculatable, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface)
Effects: MemoryEffects::Effect{}
| Attribute | MLIR Type | Description |
|---|---|---|
predicate | ::llzk::boolean::FeltCmpPredicateAttr | Field element comparison predicate |
| Operand | Description |
|---|---|
| lhs | finite field element |
| rhs | finite field element |
| Result | Description |
|---|---|
| result | 1-bit signless integer |
Logical NOT operator
Syntax:
This operation computes the logical NOT (i.e., negation) of an i1 (i.e., boolean) value as an i1 value. The result is 1 if the operation is true and 0 otherwise.
If used in a constraint expression, this operation can be converted to 1+a - 2*a on felt operands.
Traits: AlwaysSpeculatableImplTrait, NotFieldNative
Interfaces: ConditionallySpeculatable, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface)
Effects: MemoryEffects::Effect{}
| Operand | Description |
|---|---|
| operand | 1-bit signless integer or type variable |
| Result | Description |
|---|---|
| result | 1-bit signless integer |
Logical OR operator
Syntax:
This operation computes the logical OR (i.e., disjunction) of two i1 (i.e., boolean) values as an i1 value. The result is 1 if the operation is true and 0 otherwise.
If used in a constraint expression, this operation can be converted to a+b - a*b on felt operands.
Traits: AlwaysSpeculatableImplTrait, Commutative, NotFieldNative
Interfaces: ConditionallySpeculatable, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface)
Effects: MemoryEffects::Effect{}
| Operand | Description |
|---|---|
| lhs | 1-bit signless integer or type variable |
| rhs | 1-bit signless integer or type variable |
| Result | Description |
|---|---|
| result | 1-bit signless integer |
Logical XOR operator
Syntax:
This operation computes the logical XOR (i.e., exclusive disjunction) of two i1 (i.e., boolean) values as an i1 value. The result is 1 if the operation is true and 0 otherwise.
If used in a constraint expression, this operation can be converted to a+b - 2*a*b on felt operands.
Traits: AlwaysSpeculatableImplTrait, Commutative, NotFieldNative
Interfaces: ConditionallySpeculatable, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface)
Effects: MemoryEffects::Effect{}
| Operand | Description |
|---|---|
| lhs | 1-bit signless integer or type variable |
| rhs | 1-bit signless integer or type variable |
| Result | Description |
|---|---|
| result | 1-bit signless integer |
Field element comparison predicate
Syntax:
Enum cases:
| Parameter | C++ type | Description |
|---|---|---|
| value | llzk::boolean::FeltCmpPredicate | an enum of type FeltCmpPredicate |
Field element comparison predicate
| Symbol | Value | String |
|---|---|---|
| EQ | 0 | eq |
| NE | 1 | ne |
| LT | 2 | lt |
| LE | 3 | le |
| GT | 4 | gt |
| GE | 5 | ge |
LLZK type conversion operations.
Convert an integer into a field element
Syntax:
This operation converts a supported integer type value into a field element value.
Example:
Traits: AlwaysSpeculatableImplTrait
Interfaces: ConditionallySpeculatable, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface)
Effects: MemoryEffects::Effect{}
| Operand | Description |
|---|---|
| value | 1-bit signless integer or index |
| Result | Description |
|---|---|
| result | finite field element |
Convert a field element into an index
Syntax:
This operation converts a field element value into an index value to allow use as an array index or loop bound.
Example:
Traits: AlwaysSpeculatableImplTrait, NotFieldNative
Interfaces: ConditionallySpeculatable, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface)
Effects: MemoryEffects::Effect{}
| Operand | Description |
|---|---|
| value | finite field element |
| Result | Description |
|---|---|
| result | index |
LLZK constraint emission operations.
Emit an equality constraint
Syntax:
Emits an equality constraint between lhs and rhs.
Traits: Commutative, ConstraintGen, Elementwise, Scalarizable, Tensorizable, Vectorizable
Interfaces: ConstraintOpInterface, SymbolUserOpInterface
| Operand | Description |
|---|---|
| lhs | any LLZK type, excluding struct and string types |
| rhs | any LLZK type, excluding struct and string types |
Emit a containment constraint
Syntax:
Emits a containment constraint between lhs and rhs (rhs must be contained within lhs). If lhs is an N-dimensional array (N >= 1), rhs must be an M-dimensional array, where N >= M (M=0 means that rhs is a scalar element of type accepted by the constrain.eq operation).
Traits: ConstraintGen
Interfaces: ConstraintOpInterface, SymbolUserOpInterface
| Operand | Description |
|---|---|
| lhs | n-dimensional array |
| rhs | a valid LLZK type |
LLZK felt (Field ELemenT) type and operations.
Addition operator for field elements
Syntax:
Traits: AlwaysSpeculatableImplTrait, Commutative, InferTypeOpAdaptor
Interfaces: ConditionallySpeculatable, FeltBinaryOpInterface, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface)
Effects: MemoryEffects::Effect{}
| Operand | Description |
|---|---|
| lhs | finite field element or type variable |
| rhs | finite field element or type variable |
| Result | Description |
|---|---|
| result | a valid LLZK type |
Bitwise AND operator for field elements
Syntax:
Traits: AlwaysSpeculatableImplTrait, Commutative, InferTypeOpAdaptor, NotFieldNative
Interfaces: ConditionallySpeculatable, FeltBinaryOpInterface, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface)
Effects: MemoryEffects::Effect{}
| Operand | Description |
|---|---|
| lhs | finite field element or type variable |
| rhs | finite field element or type variable |
| Result | Description |
|---|---|
| result | a valid LLZK type |
Integer complement (bitwise-not) operator for field elements
Syntax:
Treats the operand as an integer with a bitwidth equal to the bitwidth of the prime field's modulus and compute the one's complement of the integer. The result is converted back to a field element by applying the prime modulus.
Traits: AlwaysSpeculatableImplTrait, InferTypeOpAdaptor, NotFieldNative
Interfaces: ConditionallySpeculatable, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface)
Effects: MemoryEffects::Effect{}
| Operand | Description |
|---|---|
| operand | finite field element or type variable |
| Result | Description |
|---|---|
| result | a valid LLZK type |
Bitwise OR operator for field elements
Syntax:
Traits: AlwaysSpeculatableImplTrait, Commutative, InferTypeOpAdaptor, NotFieldNative
Interfaces: ConditionallySpeculatable, FeltBinaryOpInterface, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface)
Effects: MemoryEffects::Effect{}
| Operand | Description |
|---|---|
| lhs | finite field element or type variable |
| rhs | finite field element or type variable |
| Result | Description |
|---|---|
| result | a valid LLZK type |
Bitwise XOR operator for field elements
Syntax:
Traits: AlwaysSpeculatableImplTrait, Commutative, InferTypeOpAdaptor, NotFieldNative
Interfaces: ConditionallySpeculatable, FeltBinaryOpInterface, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface)
Effects: MemoryEffects::Effect{}
| Operand | Description |
|---|---|
| lhs | finite field element or type variable |
| rhs | finite field element or type variable |
| Result | Description |
|---|---|
| result | a valid LLZK type |
Field element constant
Syntax:
This operation produces a felt-typed SSA value holding an integer constant.
Example:
Traits: AlwaysSpeculatableImplTrait, ConstantLike, InferTypeOpAdaptor
Interfaces: ConditionallySpeculatable, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface), OpAsmOpInterface
Effects: MemoryEffects::Effect{}
| Attribute | MLIR Type | Description |
|---|---|---|
value | ::llzk::felt::FeltConstAttr | finite field element |
| Result | Description |
|---|---|
| result | a valid LLZK type |
Field-division operator for field elements
Syntax:
Performs finite-field division by multiplying the dividend by the multiplicative inverse of the divisor. For a non-zero divisor b, felt.div a, b computes a * inv(b) modulo the field prime of the result prime field.
The divisor must be non-zero.
This is not integer division. Use felt.uintdiv or felt.sintdiv when the operands should be interpreted as integers.
Traits: AlwaysSpeculatableImplTrait, InferTypeOpAdaptor
Interfaces: ConditionallySpeculatable, FeltBinaryOpInterface, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface)
Effects: MemoryEffects::Effect{}
| Operand | Description |
|---|---|
| lhs | finite field element or type variable |
| rhs | finite field element or type variable |
| Result | Description |
|---|---|
| result | a valid LLZK type |
Inverse operator for field elements
Syntax:
Traits: AlwaysSpeculatableImplTrait, InferTypeOpAdaptor, NotFieldNative
Interfaces: ConditionallySpeculatable, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface)
Effects: MemoryEffects::Effect{}
| Operand | Description |
|---|---|
| operand | finite field element or type variable |
| Result | Description |
|---|---|
| result | a valid LLZK type |
Multiplication operator for field elements
Syntax:
Traits: AlwaysSpeculatableImplTrait, Commutative, InferTypeOpAdaptor
Interfaces: ConditionallySpeculatable, FeltBinaryOpInterface, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface)
Effects: MemoryEffects::Effect{}
| Operand | Description |
|---|---|
| lhs | finite field element or type variable |
| rhs | finite field element or type variable |
| Result | Description |
|---|---|
| result | a valid LLZK type |
Negation operator for field elements
Syntax:
Traits: AlwaysSpeculatableImplTrait, InferTypeOpAdaptor
Interfaces: ConditionallySpeculatable, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface)
Effects: MemoryEffects::Effect{}
| Operand | Description |
|---|---|
| operand | finite field element or type variable |
| Result | Description |
|---|---|
| result | a valid LLZK type |
Exponentiation operator for field elements
Syntax:
Raises a field element to the power of an exponent.
Traits: AlwaysSpeculatableImplTrait, InferTypeOpAdaptor, NotFieldNative
Interfaces: ConditionallySpeculatable, FeltBinaryOpInterface, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface)
Effects: MemoryEffects::Effect{}
| Operand | Description |
|---|---|
| lhs | finite field element or type variable |
| rhs | finite field element or type variable |
| Result | Description |
|---|---|
| result | a valid LLZK type |
Left shift operator for field elements
Syntax:
Traits: AlwaysSpeculatableImplTrait, InferTypeOpAdaptor, NotFieldNative
Interfaces: ConditionallySpeculatable, FeltBinaryOpInterface, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface)
Effects: MemoryEffects::Effect{}
| Operand | Description |
|---|---|
| lhs | finite field element or type variable |
| rhs | finite field element or type variable |
| Result | Description |
|---|---|
| result | a valid LLZK type |
Right shift operator for field elements
Syntax:
Traits: AlwaysSpeculatableImplTrait, InferTypeOpAdaptor, NotFieldNative
Interfaces: ConditionallySpeculatable, FeltBinaryOpInterface, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface)
Effects: MemoryEffects::Effect{}
| Operand | Description |
|---|---|
| lhs | finite field element or type variable |
| rhs | finite field element or type variable |
| Result | Description |
|---|---|
| result | a valid LLZK type |
Signed integer division operator for field elements
Syntax:
Treats the operands as if they were signed integers with bitwidth equal to that of the prime modulus (no additional sign bit is added) and performs division rounding towards zero.
The signed integer representation of felt f in prime field with modulus p follows the following formula:
signed_int(f) = f if 0 <= f < p/2 + 1
"p/2" here is unsigned integer division rounding towards 0
signed_int(f) = f-p if p/2 + 1 <= f < p
Traits: AlwaysSpeculatableImplTrait, InferTypeOpAdaptor, NotFieldNative
Interfaces: ConditionallySpeculatable, FeltBinaryOpInterface, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface)
Effects: MemoryEffects::Effect{}
| Operand | Description |
|---|---|
| lhs | finite field element or type variable |
| rhs | finite field element or type variable |
| Result | Description |
|---|---|
| result | a valid LLZK type |
Signed integer modulus/remainder operator for field elements
Syntax:
Computes the remainder that would result from the division operation performed by felt.sintdiv.
Traits: AlwaysSpeculatableImplTrait, InferTypeOpAdaptor, NotFieldNative
Interfaces: ConditionallySpeculatable, FeltBinaryOpInterface, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface)
Effects: MemoryEffects::Effect{}
| Operand | Description |
|---|---|
| lhs | finite field element or type variable |
| rhs | finite field element or type variable |
| Result | Description |
|---|---|
| result | a valid LLZK type |
Subtraction operator for field elements
Syntax:
Traits: AlwaysSpeculatableImplTrait, InferTypeOpAdaptor
Interfaces: ConditionallySpeculatable, FeltBinaryOpInterface, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface)
Effects: MemoryEffects::Effect{}
| Operand | Description |
|---|---|
| lhs | finite field element or type variable |
| rhs | finite field element or type variable |
| Result | Description |
|---|---|
| result | a valid LLZK type |
Unsigned integer division operator for field elements
Syntax:
Treats the operands as if they were unsigned integers with bitwidth equal to that of the prime modulus and performs division rounding towards zero.
Traits: AlwaysSpeculatableImplTrait, InferTypeOpAdaptor, NotFieldNative
Interfaces: ConditionallySpeculatable, FeltBinaryOpInterface, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface)
Effects: MemoryEffects::Effect{}
| Operand | Description |
|---|---|
| lhs | finite field element or type variable |
| rhs | finite field element or type variable |
| Result | Description |
|---|---|
| result | a valid LLZK type |
Unsigned integer modulus/remainder operator for field elements
Syntax:
Computes the remainder that would result from the division operation performed by felt.uintdiv.
Traits: AlwaysSpeculatableImplTrait, InferTypeOpAdaptor, NotFieldNative
Interfaces: ConditionallySpeculatable, FeltBinaryOpInterface, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface)
Effects: MemoryEffects::Effect{}
| Operand | Description |
|---|---|
| lhs | finite field element or type variable |
| rhs | finite field element or type variable |
| Result | Description |
|---|---|
| result | a valid LLZK type |
finite field element
A felt attribute represents a finite field element.
| Parameter | C++ type | Description |
|---|---|---|
| value | ::llvm::APInt | The felt constant value |
| type | FeltType |
prime field specification
A specification of a prime field for use by felt types.
These specifications are provided in the llzk.fields attribute on the root module as either a single element or a flat array, for example:
module attributes {llzk.lang, llzk.fields = field<foo, 7> { ... }
module attributes {llzk.lang, llzk.fields = [field<>]} { ... }
Specifications should not be provided for built-in fields, which include:
| Parameter | C++ type | Description |
|---|---|---|
| fieldName | ::mlir::StringAttr | |
| prime | ::llvm::APInt | The prime modulus |
finite field element
Syntax:
An element of a finite field. The field can be specified (e.g., !felt.type<"bn254">) or left unspecified (!felt.type), to be filled in by the backend consumer of LLZK IR. Leaving the field unspecified may reduce the power of certain transformation passes.
Backends are responsible for performing conversions between felts of different fields. If a circuit needs to encode translations between fields, the circuit may define a function prototype, e.g.:
and perform the lowering of calls to this conversion function in a backend-specific manner.
The field name can be chosen from one of the built-in fields or specified using felt.field specification attributes on the root module.
| Parameter | C++ type | Description |
|---|---|---|
| fieldName | ::mlir::StringAttr |
LLZK function operations.
Call operation
Syntax:
The function.call operation represents a call to another function. The operands and result types of the call must match the specified function type. The callee is encoded as a symbol reference attribute named "callee" which must be the full path to the target function from the root module (i.e., the module containing the [llzk::LANG_ATTR_NAME] attribute).
Example:
When the return StructType of a compute() function uses AffineMapAttr to express struct parameter(s) that depend on a loop variable, the optional instantiation parameter list of this operation must be used to instatiate all AffineMap used as parameters to the StructType.
Examples:
Traits: AttrSizedOperandSegments, MemRefsNormalizable, VerifySizesForMultiAffineOps<1>
Interfaces: CallOpInterface, SymbolUserOpInterface
| Attribute | MLIR Type | Description |
|---|---|---|
callee | ::mlir::SymbolRefAttr | symbol reference attribute |
templateParams | ::mlir::ArrayAttr | array attribute |
numDimsPerMap | ::mlir::DenseI32ArrayAttr | i32 dense array attribute |
mapOpGroupSizes | ::mlir::DenseI32ArrayAttr | i32 dense array attribute |
| Operand | Description |
|---|---|
| argOperands | variadic of a valid LLZK type |
| mapOperands | variadic of index |
| Result | Description |
|---|---|
| «unnamed» | variadic of a valid LLZK type |
An operation with a name containing a single SSACFG region
Operations within the function cannot implicitly capture values defined outside of the function, i.e., functions are IsolatedFromAbove. All external references must use function arguments (which are passed by value) or reference external members or globals by symbol name.
Functions appearing within a struct.def have specific semantics and must be named compute, constrain, or product. Functions appearing at the module level (i.e. not within a struct.def) have no name restrictions and their body may be elided to denote an external function declaration.
Modules and struct.def ops are not allowed to be nested within functions.
Example:
Traits: AffineScope, AutomaticAllocationScope, HasParent<::mlir::ModuleOp, llzk::component::StructDefOp, llzk::polymorphic::TemplateOp>, IsolatedFromAbove
Interfaces: CallableOpInterface, FunctionOpInterface, SymbolUserOpInterface, Symbol
| 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 |
Function return operation
Syntax:
The function.return operation represents a return operation within a function. The operation takes variable number of operands and produces no results. The operand number and types must match the signature of the function that contains the operation.
Example:
Traits: AlwaysSpeculatableImplTrait, HasParent<::llzk::function::FuncDefOp>, MemRefsNormalizable, ReturnLike, Terminator
Interfaces: ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface), RegionBranchTerminatorOpInterface
Effects: MemoryEffects::Effect{}
| Operand | Description |
|---|---|
| operands | variadic of a valid LLZK type |
LLZK support for global values, defined outside of structs.
Global value
Syntax:
Examples:
Traits: HasParent<mlir::ModuleOp>
Interfaces: SymbolUserOpInterface, Symbol
| Attribute | MLIR Type | Description |
|---|---|---|
sym_name | ::mlir::StringAttr | string attribute |
constant | ::mlir::UnitAttr | unit attribute |
type | ::mlir::TypeAttr | type attribute of any LLZK type except non-constant types |
initial_value | ::mlir::Attribute | any attribute |
Read value of a global
Syntax:
This operation reads the value of a named global.
Interfaces: GlobalRefOpInterface, SymbolUserOpInterface
| Attribute | MLIR Type | Description |
|---|---|---|
name_ref | ::mlir::SymbolRefAttr | symbol reference attribute |
| Result | Description |
|---|---|
| val | any LLZK type except non-constant types |
Write value to a global
Syntax:
This operation writes a value to a named global. Not allowed for globals declared with the "const" modifier.
Traits: WitnessGen
Interfaces: GlobalRefOpInterface, SymbolUserOpInterface
| Attribute | MLIR Type | Description |
|---|---|---|
name_ref | ::mlir::SymbolRefAttr | symbol reference attribute |
| Operand | Description |
|---|---|
| val | any LLZK type except non-constant types |
LLZK include operations.
Include operation
Syntax:
This operation imports the contents of another source file in place of itself.
Example:
Traits: HasParent<::mlir::ModuleOp>
Interfaces: Symbol
| Attribute | MLIR Type | Description |
|---|---|---|
sym_name | ::mlir::StringAttr | string attribute |
path | ::mlir::StringAttr | string attribute |
Common LLZK attributes.
Uninitialized variable
Syntax:
This operation produces an SSA variable of the specified type but with nondeterministic value.
This op can be used in @constrain() functions in place of expressions that cannot be included in constraints. It may also be generated for a frontend language that supports uninitialized variables and can also be introduced by the llzk-array-to-scalar pass if there is a read from an array index that was not dominated by an earlier write to that same index.
Example:
Note that llzk.nondet does not have the ConstantLike or NoMemoryEffect traits because different SSA variables initialized with llzk.nondet may be constrained in different ways so they cannot be treated as identical values. However, it does have the AlwaysSpeculatable trait to denote that it is free to move, hoist, and sick without changing the semantics.
Example:
In the above example, m is effectively unconstrained, as it is only constrained to the nondet value %1. However, if %0 and %1 were treated as pure constants, %0 and %1 could be combined, resulting in:
This transformation would constrain m to be exactly equal to 1, thus changing the semantics of the circuit.
Traits: AlwaysSpeculatableImplTrait
Interfaces: ConditionallySpeculatable, OpAsmOpInterface
| Result | Description |
|---|---|
| res | a valid LLZK type |
Annotation with the bounds of a loop
Syntax:
This attribute holds information useful for the analysis of loops. Holds the bounds of the loop and the step size.
Example:
| Parameter | C++ type | Description |
|---|---|---|
| lower | ::llvm::APInt | Loop variable lower bound (inclusive) |
| upper | ::llvm::APInt | Loop variable upper bound (exclusive) |
| step | ::llvm::APInt | Loop variable step/increment |
A unit attribute to mark an input/output as public
Syntax: #llzk.pub
This attribute is used on parameters of compute() and constrain() functions within a struct.def to denote the public inputs of the circuit and on struct.member ops to denote public output of the circuit.
Examples:
Dialect for working with plain-old-data structs.
Create a new plain-old-data struct
Creates a new, uninitialized, pod instance. Optionally, the user can pass a list of record names and values that initialize the records of the pod. Partial initialization is allowed. All records without an explicit initialization are initialized with nondeterministic values.
If the types of the pod records have affine map parameters the user can pass values for them similar to how array.new does it.
This operation returns one value of type PODType and, if present, the records passed for initialization must form a subset of the records in the type.
Examples:
Traits: AlwaysSpeculatableImplTrait, AttrSizedOperandSegments, VerifySizesForMultiAffineOps<1>
Interfaces: ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface), OpAsmOpInterface
Effects: MemoryEffects::Effect{}
| Attribute | MLIR Type | Description |
|---|---|---|
initializedRecords | ::mlir::ArrayAttr | string array attribute |
numDimsPerMap | ::mlir::DenseI32ArrayAttr | i32 dense array attribute |
mapOpGroupSizes | ::mlir::DenseI32ArrayAttr | i32 dense array attribute |
| Operand | Description |
|---|---|
| initialValues | variadic of a valid LLZK type |
| mapOperands | variadic of index |
| Result | Description |
|---|---|
| result | plain-old-data struct |
Reads the contents of a plain-old-data struct record
Syntax:
Reads the current value of a named record from a pod instance. Returns one value of the type of the record.
The name of the record must be a valid record name for the pod type and the result type must match the type of the record.
Example:
| Attribute | MLIR Type | Description |
|---|---|---|
record_name | ::mlir::FlatSymbolRefAttr | flat symbol reference attribute |
| Operand | Description |
|---|---|
| pod_ref | plain-old-data struct |
| Result | Description |
|---|---|
| result | a valid LLZK type |
Writes content into a plain-old-data struct record
Syntax:
Writes a value into a named record of a pod instance. This operation has no result values.
The name of the record must be a valid record name for the pod type and the source value type must match the type of the record.
Writing a record that was written or initialized earlier overwrites the previous value.
Example:
| Attribute | MLIR Type | Description |
|---|---|---|
record_name | ::mlir::FlatSymbolRefAttr | flat symbol reference attribute |
| Operand | Description |
|---|---|
| pod_ref | plain-old-data struct |
| value | a valid LLZK type |
A named entry in a plain-old-data struct
Syntax:
| Parameter | C++ type | Description |
|---|---|---|
| name | ::mlir::StringAttr | Name of the record entry. |
| type | mlir::Type | Type of the record entry. |
plain-old-data struct
Syntax:
Contains a list of RecordAttr defining structure of a pod. The order of records in the pod matters and each record must have a unique name. It is possible to have a pod with no records.
Two PODType types unify if they have the same record names, defined in the same order, and if the types of corresponding records unify.
This type can be used in the struct.member op, and it can be declared as a column, in which case that annotation is also implied on the inner records (except those that cannot be column such as string.type records).
| Parameter | C++ type | Description |
|---|---|---|
| records | ::llvm::ArrayRef<::llzk::pod::RecordAttr> | List of records in the pod |
LLZK types and operations to support templated/parameterized structs.
Apply an AffineMap
Syntax:
This operation applies an AffineMap to a list of SSA values, yielding a single SSA value. The number of dimension and symbol arguments must be equal to the respective number of dimensional and symbolic inputs to the AffineMap; the AffineMap has to be one-dimensional, and so this operation always returns one value. The input operands and result all have index type.
Named map example:
Inline example:
Traits: AlwaysSpeculatableImplTrait
Interfaces: ConditionallySpeculatable, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface)
Effects: MemoryEffects::Effect{}
| Attribute | MLIR Type | Description |
|---|---|---|
map | ::mlir::AffineMapAttr | AffineMap attribute |
numDims | mlir::IntegerAttr | index attribute |
| Operand | Description |
|---|---|
| mapOperands | variadic of index |
| Result | Description |
|---|---|
| «unnamed» | index |
Declares a named expression in a polymorphic template
Syntax:
Declares an expression over parameters of a poly.template that can be used just like a parameter within the function and/or struct definitions within the template.
The body of a poly.expr cannot contain any symbols defined via poly.expr to prevent cyclic initialization. The body must also have no side effects to ensure it can be safely duplicated if needed. This means operations such as read/write to globals or function calls are not allowed in the body of a poly.expr.
Example:
Traits: HasParent<::llzk::polymorphic::TemplateOp>, IsolatedFromAbove, NoRegionArguments, SingleBlock
Interfaces: SymbolUserOpInterface, Symbol, TemplateSymbolBindingOpInterface
| Attribute | MLIR Type | Description |
|---|---|---|
sym_name | ::mlir::StringAttr | string attribute |
Declares a parameter of a polymorphic template
Syntax:
Declares a parameter of a poly.template that can be used by the function and/or struct definitions within the template. Each parameter can have an optional type restriction.
Example:
Traits: HasParent<::llzk::polymorphic::TemplateOp>
Interfaces: SymbolUserOpInterface, Symbol, TemplateSymbolBindingOpInterface
| Attribute | MLIR Type | Description |
|---|---|---|
sym_name | ::mlir::StringAttr | string attribute |
type_opt | ::mlir::TypeAttr | type attribute of integral, felt, or typevar type |
Read value of a struct parameter
Syntax:
This operation reads the value from the named constant parameter of the struct/component in which this op appears. The op itself puts some restriction on the type of this value, but leaves it to a later type-checking pass to ensure the struct parameters are instantiated with types matching the uses of the parameter within the struct.
Examples:
Traits: AlwaysSpeculatableImplTrait
Interfaces: ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface), SymbolUserOpInterface
Effects: MemoryEffects::Effect{}
| Attribute | MLIR Type | Description |
|---|---|---|
const_name | ::mlir::FlatSymbolRefAttr | flat symbol reference attribute |
| Result | Description |
|---|---|
| val | integral, felt, or typevar type |
Defines polymorphic functions or structs
Syntax:
The poly.template allows defining polymorphic templated functions and structs. The body contains the definitions of the template's parameters along with the function and/or struct definitions that utilize those parameters in their bodies.
Example:
The order of poly.param definitions in the template body determines the order that template parameters must be listed in the parameter list of a struct.type refering to a struct nested within the template. In the example above, the type of @StructName is !struct.type<@TemplateName::@StructName<[@N, @T]>>.
Traits: HasParent<::mlir::ModuleOp>, IsolatedFromAbove, LLZKSymbolTableImplTrait, NoRegionArguments, NoTerminator, SingleBlock, SymbolTable
Interfaces: Symbol
| Attribute | MLIR Type | Description |
|---|---|---|
sym_name | ::mlir::StringAttr | string attribute |
Cast between two unifiable types
Syntax:
This operation reinterprets a value as a different type with the restriction that the input and output types of the cast are unifiable.
Most ops that accept LLZK types accept unifiable types as input and thus there is no need for casting between types. This op is meant to be used in situations where is not possible to modify the given or the target type and they are different but unifiable. For example, inside a conversion pattern the driver may introduce unrealized_conversion_cast operations if the types are not equal. This will happen regardless of whether the two types unify. This cast can be introduced instead of the default cast operation to satisfy MLIR's assumptions on type equality.
Example:
Traits: AlwaysSpeculatableImplTrait
Interfaces: ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface)
Effects: MemoryEffects::Effect{}
| Operand | Description |
|---|---|
| input | a valid LLZK type |
| Result | Description |
|---|---|
| result | a valid LLZK type |
Expr initialization yield and termination operation
Syntax:
This operation yields an SSA value from a poly.expr initialization region and terminates the region.
Traits: HasParent<::llzk::polymorphic::TemplateExprOp>, ReturnLike, Terminator
Interfaces: RegionBranchTerminatorOpInterface
| Operand | Description |
|---|---|
| val | integral, felt, or typevar type |
type variable
Syntax:
This type serves as a placeholder for a type that is instantiated via a parameter of the struct.
For example, we can define a struct that holds a size-2 array where the type of the elements in the array is specified by a parameter of the struct and instantiated with a specific type at the uses of the struct.
| Parameter | C++ type | Description |
|---|---|---|
| nameRef | ::mlir::FlatSymbolRefAttr | reference to the struct parameter |
LLZK string type and operations.
Literal string
Syntax:
Traits: AlwaysSpeculatableImplTrait, ConstantLike
Interfaces: ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface)
Effects: MemoryEffects::Effect{}
| Attribute | MLIR Type | Description |
|---|---|---|
value | ::mlir::StringAttr | string attribute |
| Result | Description |
|---|---|
| result | string type |
string type
Syntax: !string.type
LLZK component/struct operations.
Circuit component definition
Syntax:
This operation describes a component in a circuit. It can contain any number of members that hold inputs, outputs, intermediate values, and subcomponents of the defined component. It also contains a compute() function that holds the witness generation code for the component and a constrain() function that holds that constraint generation code for the component.
Example:
The optional llzk.main = !struct.type<...> attribute on the top-level module in an LLZK IR program expresses the main entry point of the circuit as a concrete instantiation of a specific struct. For example, llzk.main = !struct.type<@Top<[52,12]>> or llzk.main = !struct.type<@Main<[i1, !felt.type, 256]>>. This struct has additional restrictions:
Example of a Main component:
Traits: HasOnlyGraphRegion, HasParent<::mlir::ModuleOp, llzk::polymorphic::TemplateOp>, IsolatedFromAbove, LLZKSymbolTableImplTrait, NoRegionArguments, NoTerminator, SetFuncAllowAttrs, SingleBlock, SymbolTable
Interfaces: RegionKindInterface, Symbol
| Attribute | MLIR Type | Description |
|---|---|---|
sym_name | ::mlir::StringAttr | string attribute |
Struct member definition
Syntax:
This operation describes a member in a struct/component.
Example:
Further restrictions on the signal attribute:
Traits: HasParent<::llzk::component::StructDefOp>
Interfaces: SymbolUserOpInterface, Symbol
| Attribute | MLIR Type | Description |
|---|---|---|
sym_name | ::mlir::StringAttr | string attribute |
type | ::mlir::TypeAttr | type attribute of a valid LLZK type |
column | ::mlir::UnitAttr | unit attribute |
signal | ::mlir::UnitAttr | unit attribute |
Create a new struct
Syntax:
This operation creates a new, uninitialized instance of a struct.
Example:
Traits: WitnessGen
Interfaces: OpAsmOpInterface, SymbolUserOpInterface
| Result | Description |
|---|---|
| result | circuit component |
Read value of a struct member
Syntax:
This operation reads the value of a named member in a struct/component.
A struct can read its own members regardless of whether they are marked as public (i.e., with the llzk.pub attribute) or private (members without the llzk.pub attribute). However, when reading members of other components, only public members can be accessed. Free functions may also only read public members.
The value can be read from the signals table, in which case it can be offset by a constant value. A negative value represents reading a value backwards and a positive value represents reading a value forward. Only members marked as columns can be read in this manner.
Traits: VerifySizesForMultiAffineOps<1>
Interfaces: MemberRefOpInterface, SymbolUserOpInterface
| Attribute | MLIR Type | Description |
|---|---|---|
member_name | ::mlir::FlatSymbolRefAttr | flat symbol reference attribute |
tableOffset | ::mlir::Attribute | symbol reference attribute or index attribute or AffineMap attribute |
numDimsPerMap | ::mlir::DenseI32ArrayAttr | i32 dense array attribute |
mapOpGroupSizes | ::mlir::DenseI32ArrayAttr | i32 dense array attribute |
| Operand | Description |
|---|---|
| component | circuit component |
| mapOperands | variadic of index |
| Result | Description |
|---|---|
| val | a valid LLZK type |
Write value to a struct member
Syntax:
This operation writes a value to a named member in a struct/component.
A struct can write its own members. However, writing members of other components is not allowed.
Traits: WitnessGen
Interfaces: MemberRefOpInterface, SymbolUserOpInterface
| Attribute | MLIR Type | Description |
|---|---|---|
member_name | ::mlir::FlatSymbolRefAttr | flat symbol reference attribute |
| Operand | Description |
|---|---|
| component | circuit component |
| val | a valid LLZK type |
circuit component
Syntax:
Type of a struct op instance. For structs that contain template parameters, the type must contain a list of attributes that instantiate the template parameters, one per parameter. Each attribute must be one of the following:
| Parameter | C++ type | Description |
|---|---|---|
| nameRef | ::mlir::SymbolRefAttr | Fully-qualified name of the struct definition. |
| params | ::mlir::ArrayAttr | Struct parameters |