1//===-- Ops.td ---------------------------------------------*- tablegen -*-===//
3// Part of the LLZK Project, under the Apache License v2.0.
4// See LICENSE.txt for license information.
5// Copyright 2026 Project LLZK
6// SPDX-License-Identifier: Apache-2.0
8//===----------------------------------------------------------------------===//
13include "llzk/Dialect/Verif/IR/Dialect.td"
14include "llzk/Dialect/Verif/IR/OpInterfaces.td"
15include "llzk/Dialect/SMT/IR/SMTTypes.td"
16include "mlir/Interfaces/CallInterfaces.td"
17include "mlir/Interfaces/FunctionInterfaces.td"
18include "llzk/Dialect/Shared/OpTraits.td"
19include "llzk/Dialect/Shared/Types.td"
20include "llzk/Dialect/Felt/IR/Types.td"
22include "mlir/IR/OpAsmInterface.td"
23include "mlir/IR/OpBase.td"
24include "mlir/IR/SymbolInterfaces.td"
26class VerifDialectOp<string mnemonic, list<Trait> traits = []>
27 : Op<VerifDialect, mnemonic, traits>;
29class ConditionOp<string mnemonic, list<Trait> traits = []>
30 : VerifDialectOp<mnemonic, traits#[ConditionOpInterface]> {
32 let arguments = (ins I1:$condition);
35 let assemblyFormat = [{ $condition attr-dict }];
37 let extraClassDefinition = [{
38 // This side effect models "program termination". Based on
39 // https://github.com/llvm/llvm-project/blob/f325e4b2d836d6e65a4d0cf3efc6b0996ccf3765/mlir/lib/Dialect/ControlFlow/IR/ControlFlowOps.cpp#L92-L97
40 void $cppClass::getEffects(
41 ::mlir::SmallVectorImpl<::mlir::SideEffects::EffectInstance<::mlir::MemoryEffects::Effect>> &effects
43 effects.emplace_back(::mlir::MemoryEffects::Write::get());
48class ContractConditionOp<string mnemonic, list<Trait> traits = []>
49 : ConditionOp<mnemonic, traits#[HasAncestor<"::llzk::verif::ContractOp">]>;
51//===------------------------------------------------------------------===//
52// Struct verification condition operations
53//===------------------------------------------------------------------===//
55def VerifAssertOp : ConditionOp<"assert", []> {
56 let summary = "An assertion in the target of a contract, derived from one of "
57 "the contract's specifications";
58 let description = summary;
61def VerifProveOp : ConditionOp<"prove", []> {
62 let summary = "A proof obligation in the target of a contract, derived from"
63 "one of the contract's specifications";
64 let description = summary;
67def VerifSMTProveOp : Op<VerifDialect, "smt_prove", []> {
68 let arguments = (ins BoolType:$condition);
69 let summary = "A lowered proof obligation over an SMT expression";
70 let assemblyFormat = [{ $condition attr-dict }];
73//===------------------------------------------------------------------===//
74// Precondition operations
75//===------------------------------------------------------------------===//
77class RequireOpBase<string mnemonic, list<Trait> traits = []>
78 : ContractConditionOp<mnemonic, traits#[PreconditionOpInterface]> {
80 Base of an operation that encodes a precondition.
83 - May not depend on values transitively derived from struct member reads
84 or from function return values.
85 - May not be present on the `llzk.main` entry struct.
89def RequireComputeOp : RequireOpBase<"require_compute"> {
90 let summary = "witness computation precondition";
93def RequireConstrainOp : RequireOpBase<"require_constrain"> {
94 let summary = "constraint generation precondition";
97//===------------------------------------------------------------------===//
98// Postcondition operations
99//===------------------------------------------------------------------===//
101class EnsureOpBase<string mnemonic, list<Trait> traits = []>
102 : ContractConditionOp<mnemonic, traits#[PostconditionOpInterface]>;
104def EnsureComputeOp : EnsureOpBase<"ensure_compute"> {
105 let summary = "witness computation postcondition";
108def EnsureConstrainOp : EnsureOpBase<"ensure_constrain"> {
109 let summary = "constraint generation postcondition";
112//===------------------------------------------------------------------===//
114//===------------------------------------------------------------------===//
119 [ParentOneOf<["::mlir::ModuleOp", "::llzk::polymorphic::TemplateOp"]>,
120 DeclareOpInterfaceMethods<SymbolUserOpInterface>, AffineScope,
121 AutomaticAllocationScope, FunctionOpInterface,
122 SingleBlockImplicitTerminator<"::llzk::verif::ContractEndOp">,
123 IsolatedFromAbove]> {
124 let summary = "defines a specification contract for a given symbol";
126 A specification contract for a given function or struct in the circuit.
127 Contracts are "function-like", as they have arguments matching their targets,
128 and are "called" by the `verif.include` operation so that contracts can require
129 other contracts to hold on subcomponents, etc.
131 Contract argument rules:
132 - For function targets, the contract accepts arguments matching the function's arguments.
133 - For struct targets, the contract accepts arguments matching the argument list of the
134 struct's `@constrain` function.
136 Contracts do not return values. For function targets with return values, the
137 return values are appended to the end of the contract's argument list.
139 Contracts may not directly target struct functions; they must target the structs themselves.
140 Contracts targeting the module's `llzk.main` struct may contain `verif.require_compute`
141 or `verif.require_constrain` operations. A circuit whose main-level contract contains such
142 preconditions is valid only for inputs that satisfy them.
144 For templated targets (i.e., functions or structs within a `poly.template`), the contract
145 body may reference the target template's parameters and expressions. If a contract is nested
146 inside a `poly.template`, then the target must be in that same template.
153 function.def @compute(%in : !felt.type) -> !struct.type<@Bar> { ... }
154 function.def @constrain(%self : !struct.type<@Bar>, %in : !felt.type) { ... }
157 verif.contract @FooContract for @Bar (%self : !struct.type<@Bar>, %in : !felt.type) {
161 function.def @free_func (%a : !felt.type) -> !felt.type { ... }
163 verif.contract @BarContract for @free_func (%a : !felt.type, %ret_value : !felt.type) {
170 let arguments = (ins SymbolNameAttr:$sym_name, SymbolRefAttr:$target,
171 TypeAttrOf<FunctionType>:$function_type,
172 OptionalAttr<DictArrayAttr>:$arg_attrs);
174 let regions = (region AnyRegion:$body);
176 let hasCustomAssemblyFormat = 1;
178 let hasRegionVerifier = 1;
180 // Define builders manually so builder-created contracts always contain a
181 // complete single-block body with the implicit `verif.contract_end`.
182 let skipDefaultBuilders = 1;
183 let builders = [OpBuilder<(ins "::mlir::StringAttr":$sym_name,
184 "::mlir::SymbolRefAttr":$target,
185 "::mlir::TypeAttr":$function_type,
186 CArg<"::mlir::ArrayAttr", "{}">:$arg_attrs),
188 build($_builder, $_state, ::mlir::TypeRange {}, sym_name, target, function_type, arg_attrs);
190 OpBuilder<(ins "::mlir::TypeRange":$resultTypes,
191 "::mlir::StringAttr":$sym_name,
192 "::mlir::SymbolRefAttr":$target,
193 "::mlir::TypeAttr":$function_type,
194 CArg<"::mlir::ArrayAttr", "{}">:$arg_attrs),
196 $_state.getOrAddProperties<Properties>().sym_name = sym_name;
197 $_state.getOrAddProperties<Properties>().target = target;
198 $_state.getOrAddProperties<Properties>().function_type = function_type;
200 $_state.getOrAddProperties<Properties>().arg_attrs = arg_attrs;
203 $_builder, $_state, ::mlir::cast<::mlir::FunctionType>(function_type.getValue())
205 assert(resultTypes.size() == 0u && "mismatched number of results");
206 $_state.addTypes(resultTypes);
208 OpBuilder<(ins "::llvm::StringRef":$sym_name,
209 "::mlir::SymbolRefAttr":$target,
210 "::mlir::FunctionType":$function_type,
211 CArg<"::mlir::ArrayAttr", "{}">:$arg_attrs),
213 build($_builder, $_state, ::mlir::TypeRange {}, sym_name, target, function_type, arg_attrs);
215 OpBuilder<(ins "::mlir::TypeRange":$resultTypes,
216 "::llvm::StringRef":$sym_name,
217 "::mlir::SymbolRefAttr":$target,
218 "::mlir::FunctionType":$function_type,
219 CArg<"::mlir::ArrayAttr", "{}">:$arg_attrs),
221 $_state.getOrAddProperties<Properties>().sym_name = $_builder.getStringAttr(sym_name);
222 $_state.getOrAddProperties<Properties>().target = target;
223 $_state.getOrAddProperties<Properties>().function_type = ::mlir::TypeAttr::get(function_type);
225 $_state.getOrAddProperties<Properties>().arg_attrs = arg_attrs;
227 initializeEmptyBody($_builder, $_state, function_type);
228 assert(resultTypes.size() == 0u && "mismatched number of results");
229 $_state.addTypes(resultTypes);
231 OpBuilder<(ins "::llvm::StringRef":$name,
232 "llvm::StringRef":$target)>,
233 OpBuilder<(ins "::llvm::StringRef":$name,
234 "::mlir::SymbolRefAttr":$target)>];
236 let extraClassDeclaration = [{
237 /// Create a deep copy of this contract and all of its blocks, remapping any
238 /// operands that use values outside of the contract using the map that is
239 /// provided (leaving them alone if no entry is present). If the mapper
240 /// contains entries for contract arguments, these arguments are not
241 /// included in the new contract. Replaces references to cloned sub-values
242 /// with the corresponding value that is copied, and adds those mappings to
244 ContractOp clone(::mlir::IRMapping &mapper);
247 /// Clone the internal blocks and attributes from this contract into dest.
248 /// Any cloned blocks are appended to the back of dest. This contract
249 /// asserts that the attributes of the current contract and dest are
251 void cloneInto(ContractOp dest, ::mlir::IRMapping &mapper);
253 /// Return `true` iff the argument at the given index has `pub` attribute.
254 bool hasArgPublicAttr(unsigned index);
256 /// Return `true` iff the argument at the given index has a `function.arg_name` attribute.
257 bool hasArgName(unsigned index);
259 /// Return the `function.arg_name` attribute for the argument at the given index.
260 ::std::optional<::mlir::StringAttr> getArgNameAttr(unsigned index);
262 /// Set the `function.arg_name` attribute for the argument at the given index.
263 void setArgNameAttr(unsigned index, const ::mlir::StringAttr &attr);
265 /// Set the `function.arg_name` attribute for the argument at the given index from a string.
266 void setArgName(unsigned index, ::llvm::StringRef name);
268 /// Required by FunctionOpInterface.
269 /// Returns the region on the current operation that is callable.
270 ::mlir::Region *getCallableRegion() { return &getBody(); }
272 /// Required by FunctionOpInterface.
273 /// Returns the argument types of this contract.
274 ::llvm::ArrayRef<::mlir::Type> getArgumentTypes() { return getFunctionType().getInputs(); }
276 /// Required by FunctionOpInterface.
277 /// Returns the result types of this contract. Since contracts don't have
278 /// return values, the returned ArrayRef will always be empty.
279 ::llvm::ArrayRef<::mlir::Type> getResultTypes() { return getFunctionType().getResults(); }
281 /// Required by SymbolOpInterface.
282 bool isDeclaration() { return false; }
284 /// Return the full name for this contract from the root module, including
285 /// all surrounding symbol table names (i.e., modules and structs).
286 ::mlir::SymbolRefAttr getFullyQualifiedName(bool requireParent = true);
288 /// Return `true` iff the contract targets a struct type.
289 bool hasStructTarget() { return succeeded(getStructTarget()); }
291 /// Return the StructDefOp that this contract targets, or failure if it does not
292 /// target a struct or the struct is not found.
293 ::mlir::FailureOr<SymbolLookupResult<component::StructDefOp>> getStructTarget(::mlir::SymbolTableCollection &tables);
295 ::mlir::FailureOr<SymbolLookupResult<component::StructDefOp>> getStructTarget() {
296 ::mlir::SymbolTableCollection tables;
297 return getStructTarget(tables);
300 /// Return the "self" value (i.e. the first parameter) from the contract, or
301 /// failure if the contract does not target a struct.
302 ::mlir::FailureOr<::mlir::Value> getSelfValue();
304 /// Return `true` iff the contract targets a function.
305 bool hasFuncTarget() { return succeeded(getFuncTarget()); }
307 /// Return the FuncDefOp that this contract targets, or failure if it does not
308 /// target a function or the function is not found.
309 ::mlir::FailureOr<SymbolLookupResult<function::FuncDefOp>> getFuncTarget(::mlir::SymbolTableCollection &tables);
311 ::mlir::FailureOr<SymbolLookupResult<function::FuncDefOp>> getFuncTarget() {
312 ::mlir::SymbolTableCollection tables;
313 return getFuncTarget(tables);
316 /// Return the operation that this contract targets, or failure if it does not
317 /// target an operation that implements the `ContractTarget` op interface or is not found.
318 ::mlir::FailureOr<SymbolLookupResult<::llzk::verif::ContractTargetOpInterface>> getTargetOp(::mlir::SymbolTableCollection &tables);
320 ::mlir::FailureOr<SymbolLookupResult<::llzk::verif::ContractTargetOpInterface>> getTargetOp() {
321 ::mlir::SymbolTableCollection tables;
322 return getTargetOp(tables);
326 /// Populate a builder-created contract body with one entry block matching
327 /// the function signature and insert the implicit `verif.contract_end`.
328 static void initializeEmptyBody(
329 ::mlir::OpBuilder &builder, ::mlir::OperationState &state,
330 ::mlir::FunctionType functionType
335//===------------------------------------------------------------------===//
337//===------------------------------------------------------------------===//
340 : VerifDialectOp<"contract_end", [Pure, Terminator,
341 HasParent<"::llzk::verif::ContractOp">]> {
342 let summary = "terminates a verif.contract body";
344 Implicit terminator for `verif.contract` regions. This operation is inserted
345 automatically by the parser and omitted by the custom printer so contract
346 syntax remains terminator-free.
348 A terminator is expected by certain MLIR utilities (e.g., the data-flow analysis
349 framework), which is why this op is added.
352 let assemblyFormat = "attr-dict";
355//===------------------------------------------------------------------===//
357//===------------------------------------------------------------------===//
361 "include", [MemRefsNormalizable, AttrSizedOperandSegments,
362 VerifySizesForMultiAffineOps<1>,
363 DeclareOpInterfaceMethods<CallOpInterface>,
364 DeclareOpInterfaceMethods<SymbolUserOpInterface>,
365 HasAncestor<"::llzk::verif::ContractOp">]> {
366 let summary = "contract inclusion operation";
368 Invokes another specification contract from another contract, effectively including the
369 specifications from another specification into the current contract.
372 // See `VerifySizesForMultiAffineOps` for more explanation of these arguments.
374 // Call target contract reference.
375 SymbolRefAttr:$callee,
376 // List of arguments to call the target contract.
377 Variadic<AnyLLZKType>:$argOperands,
378 // List of parameters to instantiate all `poly.param` symbols when the
379 // callee is a contract inside a `poly.template` region.
380 OptionalAttr<ArrayAttr>:$templateParams,
381 // List of AffineMap operand groups where each group provides the
382 // arguments to instantiate the next (left-to-right) AffineMap used as a
383 // struct parameter in the result StructType.
384 VariadicOfVariadic<Index, "mapOpGroupSizes">:$mapOperands,
385 // Within each group in '$mapOperands', denotes the number of values that
386 // are AffineMap "dimensional" arguments with the remaining values being
387 // AffineMap "symbolic" arguments.
388 DefaultValuedAttr<DenseI32ArrayAttr, "{}">:$numDimsPerMap,
389 // Denotes the size of each variadic group in '$mapOperands'.
390 DenseI32ArrayAttr:$mapOpGroupSizes);
392 let assemblyFormat = [{
394 ( `<` custom<TemplateParams>($templateParams)^ `>` )?
395 `` `(` $argOperands `)`
396 ( `{` custom<MultiDimAndSymbolList>($mapOperands, $numDimsPerMap)^ `}` )?
397 `:` functional-type($argOperands, results)
398 custom<AttrDictWithWarnings>(attr-dict, prop-dict)
401 // Define builders manually so inference of operand layout attributes is not
403 let skipDefaultBuilders = 1;
405 [OpBuilder<(ins "::mlir::SymbolRefAttr":$callee,
406 CArg<"::mlir::ValueRange", "{}">:$argOperands,
407 CArg<"::llvm::ArrayRef<::mlir::Attribute>", "{}">:$templateParams)>,
408 OpBuilder<(ins "::mlir::SymbolRefAttr":$callee,
409 "::llvm::ArrayRef<::mlir::ValueRange>":$mapOperands,
410 "::mlir::DenseI32ArrayAttr":$numDimsPerMap,
411 CArg<"::mlir::ValueRange", "{}">:$argOperands,
412 CArg<"::llvm::ArrayRef<::mlir::Attribute>", "{}">:$templateParams)>,
413 OpBuilder<(ins "::mlir::SymbolRefAttr":$callee,
414 "::llvm::ArrayRef<::mlir::ValueRange>":$mapOperands,
415 "::llvm::ArrayRef<int32_t>":$numDimsPerMap,
416 CArg<"::mlir::ValueRange", "{}">:$argOperands,
417 CArg<"::llvm::ArrayRef<::mlir::Attribute>",
418 "{}">:$templateParams),
420 build($_builder, $_state, callee, mapOperands,
421 $_builder.getDenseI32ArrayAttr(numDimsPerMap),
422 argOperands, templateParams);
424 OpBuilder<(ins "::llzk::verif::ContractOp":$callee,
425 CArg<"::mlir::ValueRange", "{}">:$argOperands,
426 CArg<"::llvm::ArrayRef<::mlir::Attribute>",
427 "{}">:$templateParams),
429 build($_builder, $_state,
430 callee.getFullyQualifiedName(false),
431 argOperands, templateParams);
433 OpBuilder<(ins "::llzk::verif::ContractOp":$callee,
434 "::llvm::ArrayRef<::mlir::ValueRange>":$mapOperands,
435 "::mlir::DenseI32ArrayAttr":$numDimsPerMap,
436 CArg<"::mlir::ValueRange", "{}">:$argOperands,
437 CArg<"::llvm::ArrayRef<::mlir::Attribute>",
438 "{}">:$templateParams),
440 build($_builder, $_state,
441 callee.getFullyQualifiedName(false), mapOperands, numDimsPerMap,
442 argOperands, templateParams);
444 OpBuilder<(ins "::llzk::verif::ContractOp":$callee,
445 "::llvm::ArrayRef<::mlir::ValueRange>":$mapOperands,
446 "::llvm::ArrayRef<int32_t>":$numDimsPerMap,
447 CArg<"::mlir::ValueRange", "{}">:$argOperands,
448 CArg<"::llvm::ArrayRef<::mlir::Attribute>",
449 "{}">:$templateParams),
451 build($_builder, $_state, callee, mapOperands,
452 $_builder.getDenseI32ArrayAttr(numDimsPerMap),
453 argOperands, templateParams);
456 let extraClassDeclaration = [{
457 /// Required by CallOpInterface
458 ::mlir::Operation *resolveCallableInTable(::mlir::SymbolTableCollection *symbolTable);
460 /// Required by CallOpInterface
461 ::mlir::Operation *resolveCallable();
463 /// Return the FunctionType inferred from the arg operands of this CallOp.
464 /// This is not necessarily the same as the callee's FunctionType but should unify with it
465 /// or else IR verification will fail.
466 ::mlir::FunctionType getTypeSignature();
468 /// Attempt type unfication between the inferred FunctionType from this CallOp (as LHS) and
469 /// the given FunctionType (as RHS). If successful, return a UnificationMap containing the
470 /// unifications that were made. Otherwise, return failure.
471 ::mlir::FailureOr<UnificationMap> unifyTypeSignature(::mlir::FunctionType other);
473 /// Return `true` iff the contract targets a struct type.
474 bool contractTargetsStruct();
476 /// Return the "self" value (i.e. the first parameter) from the callee contract,
477 /// assuming the target of the contract is a struct target.
478 ::mlir::Value getSelfValue();
480 /// Resolve and return the target Contract for this CallOp.
481 ::mlir::FailureOr<::llzk::SymbolLookupResult<::llzk::verif::ContractOp>>
482 getCalleeTarget(::mlir::SymbolTableCollection &tables);
484 /// Allocate consecutive storage of the ValueRange instances in the parameter
485 /// so it can be passed to the builders as an `ArrayRef<ValueRange>`.
486 static ::llvm::SmallVector<::mlir::ValueRange> toVectorOfValueRange(::mlir::OperandRangeRange);
488 /// Check type compatibility of the given template parameter value from this `CallOp` against
489 /// the declared type on the given `TemplateParamOp` (if any).
490 ::mlir::LogicalResult verifyTemplateParamCompatibility(
491 ::mlir::Attribute paramFromCallOp, ::llzk::polymorphic::TemplateParamOp targetParam
494 /// Check type compatibility of each template parameter value provided in this `CallOp` against
495 /// the declared type on each `TemplateParamOp` (if any).
497 /// Pre-condition assertions:
498 /// - `!isNullOrEmpty(getTemplateParamsAttr())`
499 /// - `getTemplateParamsAttr().size() == llvm::range_size(targetParamDefs)`
500 ::mlir::LogicalResult verifyTemplateParamCompatibility(
501 ::llvm::iterator_range<::mlir::Region::op_iterator<::llzk::polymorphic::TemplateParamOp>> targetParamDefs
504 /// Verify that each template parameter value provided in this `CallOp` is consistent with
505 /// the value inferred for the target `TemplateParamOp` in the given `UnificationMap`. The
506 /// `UnificationMap` is expected to contain the unification results of this `CallOp` against
507 /// the target function type signature.
509 /// Pre-condition assertions:
510 /// - `!isNullOrEmpty(getTemplateParamsAttr())`
511 /// - `getTemplateParamsAttr().size() == llvm::range_size(targetParamDefs)`
512 ::mlir::LogicalResult verifyTemplateParamsMatchInferred(
513 ::llvm::iterator_range<::mlir::Region::op_iterator<::llzk::polymorphic::TemplateParamOp>> targetParamDefs,
514 const UnificationMap &unifications
519//===------------------------------------------------------------------===//
521//===------------------------------------------------------------------===//
524 : VerifDialectOp<"invariant", [HasAncestor<"::llzk::verif::ContractOp">,
525 NoTerminator, SingleBlock]> {
527 let summary = "loop invariant definition operation";
529 Defines an invariant for a loop inside the target of a contract.
531 The targeted loop op must implement the `InvariantTargetOpInterface`.
532 This interface is already implemented by the `scf.while` and `scf.for`
533 operations. The loop name is defined on either of those ops with a
534 string attribute named `loop_label`.
536 The arguments of the body must match those declared by the target op.
537 In the case of `scf.for` the control values must also be block arguments
538 in the following order: lower bound, induction variable, upper bound, and stride.
542 module attributes {llzk.lang} {
544 function.def @compute() -> !struct.type<@Top> {
545 %self = struct.new : !struct.type<@Top>
546 %c0 = arith.constant 0 : index
547 %c10 = arith.constant 10 : index
548 %c1 = arith.constant 1 : index
550 %1 = scf.for %iv = %c0 to %c10 step %c1 iter_args(%2 = %0) -> !felt.type {
551 scf.yield %2 : !felt.type
552 } {loop_label = "loopA"}
553 function.return %self : !struct.type<@Top>
555 function.def @constrain(%self: !struct.type<@Top>) {
560 verif.contract @Foo for @Top (%self: !struct.type<@Top>) {
561 verif.invariant for @loopA(%lb: index, %iv: index, %ub: index, %step: index, %extra: !felt.type) {
562 %true = arith.constant true
563 verif.require_compute %true
570 let arguments = (ins StrAttr:$loop_name, TypeArrayAttr:$loop_arg_types);
572 let regions = (region SizedRegion<1>:$region);
574 let skipDefaultBuilders = 1;
575 let builders = [OpBuilder<(ins "::mlir::StringRef":$loop_name,
576 CArg<"::llvm::ArrayRef<::mlir::Type>", "{}">:$loop_arg_types,
577 CArg<"::llvm::ArrayRef<::mlir::Location>", "{}">:$loop_arg_locs)>];
579 let hasCustomAssemblyFormat = 1;
582 let extraClassDeclaration = [{
583 /// Returns the contract operation that contains this invariant.
584 ::llzk::verif::ContractOp getParentContract();
585 /// Returns the loop target.
586 ::mlir::FailureOr<::llzk::verif::InvariantTargetOpInterface> getTarget();
590//===------------------------------------------------------------------===//
591// Invariant inner ops
592//===------------------------------------------------------------------===//
594class InvariantInnerOp<string mnemonic, list<Trait> traits = []>
597 traits#[HasAncestor<"::llzk::verif::InvariantOp">,
598 DeclareOpInterfaceMethods<MemoryEffectsOpInterface>]> {
600 let summary = mnemonic#" value operation";
602 Indicates that a value }]#mnemonic#[{ on each iteration of the loop.
604 This declares the `MemoryEffectsOpInterface`, which, like the `cf.assert` (MLIR `cf` dialect)
605 and `bool.assert` (LLZK `bool` dialect) ops, adds a MemWrite affect to model program termination.
608 let arguments = (ins LLZK_FeltType:$value);
610 let extraClassDefinition = [{
611 // This side effect models "program termination". Based on
612 // https://github.com/llvm/llvm-project/blob/f325e4b2d836d6e65a4d0cf3efc6b0996ccf3765/mlir/lib/Dialect/ControlFlow/IR/ControlFlowOps.cpp#L92-L97
613 void $cppClass::getEffects(
614 ::mlir::SmallVectorImpl<::mlir::SideEffects::EffectInstance<::mlir::MemoryEffects::Effect>> &effects
616 effects.emplace_back(::mlir::MemoryEffects::Write::get());
620 let assemblyFormat = "$value attr-dict";
623def IncreasesOp : InvariantInnerOp<"increases"> {}
625def DecreasesOp : InvariantInnerOp<"decreases"> {}
627def StepOp : VerifDialectOp<
628 "step", [HasAncestor<"::llzk::verif::InvariantOp">,
629 DeclareOpInterfaceMethods<MemoryEffectsOpInterface>,
630 NoRegionArguments, SingleBlock]> {
631 let summary = "step predicate operation";
633 Defines a predicate that must be satisfied between iterations of the loop.
635 The predicate can access the value in the previous iteration by using the `verif.old` operation.
637 Declares the `MemoryEffectsOpInterface`, which, like the `cf.assert` (MLIR `cf` dialect)
638 and `bool.assert` (LLZK `bool` dialect) ops, adds a MemWrite affect to model program termination.
641 let regions = (region SizedRegion<1>:$region);
643 let extraClassDefinition = [{
644 // This side effect models "program termination". Based on
645 // https://github.com/llvm/llvm-project/blob/f325e4b2d836d6e65a4d0cf3efc6b0996ccf3765/mlir/lib/Dialect/ControlFlow/IR/ControlFlowOps.cpp#L92-L97
646 void $cppClass::getEffects(
647 ::mlir::SmallVectorImpl<::mlir::SideEffects::EffectInstance<::mlir::MemoryEffects::Effect>> &effects
649 effects.emplace_back(::mlir::MemoryEffects::Write::get());
653 let assemblyFormat = "$region attr-dict";
657 : VerifDialectOp<"step.yield", [HasParent<"StepOp">, Terminator]> {
658 let summary = "step yield operation";
660 Terminator operation for `verif.step` blocks. Takes the boolean value carrying the predicate's result.
663 let arguments = (ins I1:$value);
665 let assemblyFormat = "$value attr-dict";
668def OldOp : VerifDialectOp<"old", [Pure, HasAncestor<"::llzk::verif::StepOp">,
669 AllTypesMatch<["value", "result"]>]> {
670 let summary = "old operation";
672 This operation allows accessing the value of an expression in the previous iteration of the loop.
674 In can only be used within the body of a `verif.step` operation.
677 let arguments = (ins AnyLLZKType:$value);
678 let results = (outs AnyLLZKType:$result);
680 let assemblyFormat = "$value `:` type($result) attr-dict";
683#endif // LLZK_VERIF_OPS