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/Felt/IR/Types.td"
14include "llzk/Dialect/Function/IR/OpTraits.td"
15include "llzk/Dialect/Verif/IR/Dialect.td"
16include "llzk/Dialect/Verif/IR/OpInterfaces.td"
17include "llzk/Dialect/Shared/CallLikeOpInterfaces.td"
18include "llzk/Dialect/Shared/OpTraits.td"
19include "llzk/Dialect/Shared/Types.td"
21include "mlir/Dialect/SMT/IR/SMTTypes.td"
22include "mlir/Interfaces/CallInterfaces.td"
23include "mlir/Interfaces/FunctionInterfaces.td"
24include "mlir/IR/OpAsmInterface.td"
25include "mlir/IR/OpBase.td"
26include "mlir/IR/SymbolInterfaces.td"
27include "mlir/Interfaces/CallInterfaces.td"
28include "mlir/Interfaces/FunctionInterfaces.td"
30class VerifDialectOp<string mnemonic, list<Trait> traits = []>
31 : Op<VerifDialect, mnemonic, traits>;
33class SideEffectsOp<string mnemonic, list<Trait> traits = []>
34 : VerifDialectOp<mnemonic, traits> {}
36class ConditionOp<string mnemonic, list<Trait> traits = []>
37 : VerifDialectOp<mnemonic, traits#[ConditionOpInterface]> {
39 let arguments = (ins I1:$condition);
42 let assemblyFormat = [{ $condition attr-dict }];
44 let extraClassDefinition = [{
45 // This side effect models "program termination". Based on
46 // https://github.com/llvm/llvm-project/blob/f325e4b2d836d6e65a4d0cf3efc6b0996ccf3765/mlir/lib/Dialect/ControlFlow/IR/ControlFlowOps.cpp#L92-L97
47 void $cppClass::getEffects(
48 ::mlir::SmallVectorImpl<::mlir::SideEffects::EffectInstance<::mlir::MemoryEffects::Effect>> &effects
50 effects.emplace_back(::mlir::MemoryEffects::Write::get());
55class ContractConditionOp<string mnemonic, bit inlinable = false,
56 list<Trait> traits = []>
57 : ConditionOp<mnemonic,
59 [AncestorOneOf<["::llzk::verif::ContractOp",
60 "::llzk::function::FuncDefOp"]>],
61 [HasAncestor<"::llzk::verif::ContractOp">])>;
63//===------------------------------------------------------------------===//
64// Struct verification condition operations
65//===------------------------------------------------------------------===//
67def VerifAssertOp : ConditionOp<"assert", []> {
68 let summary = "An assertion in the target of a contract, derived from one of "
69 "the contract's specifications";
70 let description = summary;
73def VerifProveOp : ConditionOp<"prove", []> {
74 let summary = "A proof obligation in the target of a contract, derived from"
75 "one of the contract's specifications";
76 let description = summary;
79def VerifSMTProveOp : Op<VerifDialect, "smt_prove", []> {
80 let arguments = (ins BoolType:$condition);
81 let summary = "A lowered proof obligation over an SMT expression";
82 let assemblyFormat = [{ $condition attr-dict }];
85//===------------------------------------------------------------------===//
86// Determinism operations
87//===------------------------------------------------------------------===//
89def ProveDetOp : VerifDialectOp<"det.prove", [Verification<>]> {
90 let summary = "proof obligation of determinism";
92 A proof obligation that the operand is deterministic, for a concrete definition
93 of determinism determined by the backend.
95 Returns a boolean that indicates if the operand was proven deterministic or not.
97 This operation has the `Verification` trait and can be used inside `function.def`
98 ops that have the `allow_verif` attribute.
100 let arguments = (ins EmitEqType:$condition);
101 let results = (outs I1:$result);
103 let assemblyFormat = "$condition attr-dict `:` type($condition)";
106def AssumeDetOp : VerifDialectOp<"det.assume", [Verification<>]> {
107 let summary = "hint of determinisms";
109 Hints to the proving backend that the operand is deterministic, for a concrete definition
110 of determinism determined by the backend.
112 This operation has the `Verification` trait and can be used inside `function.def`
113 ops that have the `allow_verif` attribute.
115 let arguments = (ins EmitEqType:$hint);
116 let results = (outs);
118 let assemblyFormat = "$hint attr-dict `:` type($hint)";
120 let extraClassDefinition = [{
121 // This side effect models "program termination". Based on
122 // https://github.com/llvm/llvm-project/blob/f325e4b2d836d6e65a4d0cf3efc6b0996ccf3765/mlir/lib/Dialect/ControlFlow/IR/ControlFlowOps.cpp#L92-L97
123 static void getEffects(
124 ::mlir::SmallVectorImpl<::mlir::SideEffects::EffectInstance<::mlir::MemoryEffects::Effect>> &effects
126 effects.emplace_back(::mlir::MemoryEffects::Write::get());
131//===------------------------------------------------------------------===//
132// Precondition operations
133//===------------------------------------------------------------------===//
135class RequireOpBase<string mnemonic, list<Trait> traits = []>
136 : ContractConditionOp<"require_"#mnemonic, false,
137 traits#[PreconditionOpInterface]> {
139 Encodes a precondition in the `@}]#mnemonic#[{` function.
142 - May not depend on values transitively derived from struct member reads
143 or from function return values.
144 - May not be present on the `llzk.main` entry struct.
148def RequireComputeOp : RequireOpBase<"compute"> {
149 let summary = "witness computation precondition";
152def RequireConstrainOp : RequireOpBase<"constrain"> {
153 let summary = "constraint generation precondition";
156//===------------------------------------------------------------------===//
157// Postcondition operations
158//===------------------------------------------------------------------===//
161class EnsureOpBase<string mnemonic, bit inlinable = false,
162 list<string> verificationExtraTraits = [],
163 list<Trait> traits = []>
164 : ContractConditionOp<
165 "ensure_"#mnemonic, inlinable,
166 traits#[PostconditionOpInterface]#!if(
167 inlinable, [Verification<verificationExtraTraits>], [])> {
169 Encodes a postcondition in the `@}]#mnemonic#[{` function.
172 This operation has the `Verification` trait and can be used inside `function.def`
173 ops that have the `allow_verif_ops` attribute.
178def EnsureComputeOp : EnsureOpBase<"compute"> {
179 let summary = "witness computation postcondition";
186 /*verificationExtraTraits=*/["::llzk::function::ConstraintGen"]> {
187 let summary = "constraint generation postcondition";
190//===------------------------------------------------------------------===//
192//===------------------------------------------------------------------===//
197 [ParentOneOf<["::mlir::ModuleOp", "::llzk::polymorphic::TemplateOp"]>,
198 DeclareOpInterfaceMethods<SymbolUserOpInterface>, AffineScope,
199 AutomaticAllocationScope, FunctionOpInterface,
200 SingleBlockImplicitTerminator<"::llzk::verif::ContractEndOp">,
201 IsolatedFromAbove]> {
202 let summary = "defines a specification contract for a given symbol";
204 A specification contract for a given function or struct in the circuit.
205 Contracts are "function-like", as they have arguments matching their targets,
206 and are "called" by the `verif.include` operation so that contracts can require
207 other contracts to hold on subcomponents, etc.
209 Contract argument rules:
210 - For function targets, the contract accepts arguments matching the function's arguments.
211 - For struct targets, the contract accepts arguments matching the argument list of the
212 struct's `@constrain` function.
214 Contracts do not return values. For function targets with return values, the
215 return values are appended to the end of the contract's argument list.
217 Contracts may not directly target struct functions; they must target the structs themselves.
218 Contracts targeting the module's `llzk.main` struct may contain `verif.require_compute`
219 or `verif.require_constrain` operations. A circuit whose main-level contract contains such
220 preconditions is valid only for inputs that satisfy them.
222 For templated targets (i.e., functions or structs within a `poly.template`), the contract
223 body may reference the target template's parameters and expressions. If a contract is nested
224 inside a `poly.template`, then the target must be in that same template.
231 function.def @compute(%in : !felt.type) -> !struct.type<@Bar> { ... }
232 function.def @constrain(%self : !struct.type<@Bar>, %in : !felt.type) { ... }
235 verif.contract @FooContract for @Bar (%self : !struct.type<@Bar>, %in : !felt.type) {
239 function.def @free_func (%a : !felt.type) -> !felt.type { ... }
241 verif.contract @BarContract for @free_func (%a : !felt.type, %ret_value : !felt.type) {
248 let arguments = (ins SymbolNameAttr:$sym_name, SymbolRefAttr:$target,
249 TypeAttrOf<FunctionType>:$function_type,
250 OptionalAttr<DictArrayAttr>:$arg_attrs,
251 OptionalAttr<DictArrayAttr>:$res_attrs);
253 let regions = (region AnyRegion:$body);
255 let hasCustomAssemblyFormat = 1;
257 let hasRegionVerifier = 1;
259 // Define builders manually so builder-created contracts always contain a
260 // complete single-block body with the implicit `verif.contract_end`.
261 let skipDefaultBuilders = 1;
262 let builders = [OpBuilder<(ins "::mlir::StringAttr":$sym_name,
263 "::mlir::SymbolRefAttr":$target,
264 "::mlir::TypeAttr":$function_type,
265 CArg<"::mlir::ArrayAttr", "{}">:$arg_attrs,
266 CArg<"::mlir::ArrayAttr", "{}">:$res_attrs),
268 build($_builder, $_state, ::mlir::TypeRange {}, sym_name, target, function_type, arg_attrs,
271 OpBuilder<(ins "::mlir::TypeRange":$resultTypes,
272 "::mlir::StringAttr":$sym_name,
273 "::mlir::SymbolRefAttr":$target,
274 "::mlir::TypeAttr":$function_type,
275 CArg<"::mlir::ArrayAttr", "{}">:$arg_attrs,
276 CArg<"::mlir::ArrayAttr", "{}">:$res_attrs),
278 $_state.getOrAddProperties<Properties>().sym_name = sym_name;
279 $_state.getOrAddProperties<Properties>().target = target;
280 $_state.getOrAddProperties<Properties>().function_type = function_type;
282 $_state.getOrAddProperties<Properties>().arg_attrs = arg_attrs;
285 $_state.getOrAddProperties<Properties>().res_attrs = res_attrs;
288 $_builder, $_state, ::llvm::cast<::mlir::FunctionType>(function_type.getValue())
290 assert(resultTypes.size() == 0u && "mismatched number of results");
291 $_state.addTypes(resultTypes);
293 OpBuilder<(ins "::llvm::StringRef":$sym_name,
294 "::mlir::SymbolRefAttr":$target,
295 "::mlir::FunctionType":$function_type,
296 CArg<"::mlir::ArrayAttr", "{}">:$arg_attrs,
297 CArg<"::mlir::ArrayAttr", "{}">:$res_attrs),
299 build($_builder, $_state, ::mlir::TypeRange {}, sym_name, target, function_type, arg_attrs,
302 OpBuilder<(ins "::mlir::TypeRange":$resultTypes,
303 "::llvm::StringRef":$sym_name,
304 "::mlir::SymbolRefAttr":$target,
305 "::mlir::FunctionType":$function_type,
306 CArg<"::mlir::ArrayAttr", "{}">:$arg_attrs,
307 CArg<"::mlir::ArrayAttr", "{}">:$res_attrs),
309 $_state.getOrAddProperties<Properties>().sym_name = $_builder.getStringAttr(sym_name);
310 $_state.getOrAddProperties<Properties>().target = target;
311 $_state.getOrAddProperties<Properties>().function_type = ::mlir::TypeAttr::get(function_type);
313 $_state.getOrAddProperties<Properties>().arg_attrs = arg_attrs;
316 $_state.getOrAddProperties<Properties>().res_attrs = res_attrs;
318 initializeEmptyBody($_builder, $_state, function_type);
319 assert(resultTypes.size() == 0u && "mismatched number of results");
320 $_state.addTypes(resultTypes);
322 OpBuilder<(ins "::llvm::StringRef":$name,
323 "llvm::StringRef":$target)>,
324 OpBuilder<(ins "::llvm::StringRef":$name,
325 "::mlir::SymbolRefAttr":$target)>];
327 let extraClassDeclaration = [{
328 /// Create a deep copy of this contract and all of its blocks, remapping any
329 /// operands that use values outside of the contract using the map that is
330 /// provided (leaving them alone if no entry is present). If the mapper
331 /// contains entries for contract arguments, these arguments are not
332 /// included in the new contract. Replaces references to cloned sub-values
333 /// with the corresponding value that is copied, and adds those mappings to
335 ContractOp clone(::mlir::IRMapping &mapper);
338 /// Clone the internal blocks and attributes from this contract into dest.
339 /// Any cloned blocks are appended to the back of dest. This contract
340 /// asserts that the attributes of the current contract and dest are
342 void cloneInto(ContractOp dest, ::mlir::IRMapping &mapper);
344 /// Return `true` iff the argument at the given index has `pub` attribute.
345 bool hasArgPublicAttr(unsigned index);
347 /// Return `true` iff the argument at the given index has a `function.arg_name` attribute.
348 bool hasArgName(unsigned index);
350 /// Return the `function.arg_name` attribute for the argument at the given index.
351 ::std::optional<::mlir::StringAttr> getArgNameAttr(unsigned index);
353 /// Set the `function.arg_name` attribute for the argument at the given index.
354 void setArgNameAttr(unsigned index, const ::mlir::StringAttr &attr);
356 /// Set the `function.arg_name` attribute for the argument at the given index from a string.
357 void setArgName(unsigned index, ::llvm::StringRef name);
359 /// Required by FunctionOpInterface.
360 /// Returns the region on the current operation that is callable.
361 ::mlir::Region *getCallableRegion() { return &getBody(); }
363 /// Required by FunctionOpInterface.
364 /// Returns the argument types of this contract.
365 ::llvm::ArrayRef<::mlir::Type> getArgumentTypes() { return getFunctionType().getInputs(); }
367 /// Required by FunctionOpInterface.
368 /// Returns the result types of this contract. Since contracts don't have
369 /// return values, the returned ArrayRef will always be empty.
370 ::llvm::ArrayRef<::mlir::Type> getResultTypes() { return getFunctionType().getResults(); }
372 /// Required by SymbolOpInterface.
373 bool isDeclaration() { return false; }
375 /// Return the full name for this contract from the root module, including
376 /// all surrounding symbol table names (i.e., modules and structs).
377 ::mlir::SymbolRefAttr getFullyQualifiedName(bool requireParent = true);
379 /// Return `true` iff the contract targets a struct type.
380 bool hasStructTarget() { return succeeded(getStructTarget()); }
382 /// Return the StructDefOp that this contract targets, or failure if it does not
383 /// target a struct or the struct is not found.
384 ::mlir::FailureOr<SymbolLookupResult<component::StructDefOp>> getStructTarget(::mlir::SymbolTableCollection &tables);
386 ::mlir::FailureOr<SymbolLookupResult<component::StructDefOp>> getStructTarget() {
387 ::mlir::SymbolTableCollection tables;
388 return getStructTarget(tables);
391 /// Return the "self" value (i.e. the first parameter) from the contract, or
392 /// failure if the contract does not target a struct.
393 ::mlir::FailureOr<::mlir::Value> getSelfValue();
395 /// Return `true` iff the contract targets a function.
396 bool hasFuncTarget() { return succeeded(getFuncTarget()); }
398 /// Return the FuncDefOp that this contract targets, or failure if it does not
399 /// target a function or the function is not found.
400 ::mlir::FailureOr<SymbolLookupResult<function::FuncDefOp>> getFuncTarget(::mlir::SymbolTableCollection &tables);
402 ::mlir::FailureOr<SymbolLookupResult<function::FuncDefOp>> getFuncTarget() {
403 ::mlir::SymbolTableCollection tables;
404 return getFuncTarget(tables);
407 /// Return the operation that this contract targets, or failure if it does not
408 /// target an operation that implements the `ContractTarget` op interface or is not found.
409 ::mlir::FailureOr<SymbolLookupResult<::llzk::verif::ContractTargetOpInterface>> getTargetOp(::mlir::SymbolTableCollection &tables);
411 ::mlir::FailureOr<SymbolLookupResult<::llzk::verif::ContractTargetOpInterface>> getTargetOp() {
412 ::mlir::SymbolTableCollection tables;
413 return getTargetOp(tables);
417 /// Populate a builder-created contract body with one entry block matching
418 /// the function signature and insert the implicit `verif.contract_end`.
419 static void initializeEmptyBody(
420 ::mlir::OpBuilder &builder, ::mlir::OperationState &state,
421 ::mlir::FunctionType functionType
426//===------------------------------------------------------------------===//
428//===------------------------------------------------------------------===//
431 : VerifDialectOp<"contract_end", [Pure, Terminator,
432 HasParent<"::llzk::verif::ContractOp">]> {
433 let summary = "terminates a verif.contract body";
435 Implicit terminator for `verif.contract` regions. This operation is inserted
436 automatically by the parser and omitted by the custom printer so contract
437 syntax remains terminator-free.
439 A terminator is expected by certain MLIR utilities (e.g., the data-flow analysis
440 framework), which is why this op is added.
443 let assemblyFormat = "attr-dict";
446//===------------------------------------------------------------------===//
448//===------------------------------------------------------------------===//
451 : VerifDialectOp<"include", [MemRefsNormalizable, AttrSizedOperandSegments,
452 DeclareOpInterfaceMethods<
453 LLZKCallLikeOpInterface, ["getSelfValue"]>,
454 VerifySizesForMultiAffineOps<1>,
455 HasAncestor<"::llzk::verif::ContractOp">]> {
456 let summary = "contract inclusion operation";
458 Invokes another specification contract from another contract, effectively including the
459 specifications from another specification into the current contract.
462 // See `VerifySizesForMultiAffineOps` for more explanation of these arguments.
464 // Call target contract reference.
465 SymbolRefAttr:$callee,
466 // List of arguments to call the target contract.
467 Variadic<AnyLLZKType>:$argOperands,
468 // List of parameters to instantiate all `poly.param` symbols when the
469 // callee is a contract inside a `poly.template` region.
470 OptionalAttr<ArrayAttr>:$templateParams,
471 // List of AffineMap operand groups where each group provides the
472 // arguments to instantiate the next (left-to-right) AffineMap used as a
473 // struct parameter in the result StructType.
474 VariadicOfVariadic<Index, "mapOpGroupSizes">:$mapOperands,
475 // Within each group in '$mapOperands', denotes the number of values that
476 // are AffineMap "dimensional" arguments with the remaining values being
477 // AffineMap "symbolic" arguments.
478 DefaultValuedAttr<DenseI32ArrayAttr, "{}">:$numDimsPerMap,
479 // Denotes the size of each variadic group in '$mapOperands'.
480 DenseI32ArrayAttr:$mapOpGroupSizes,
481 // Per-call argument and result attribute dictionaries required by
482 // CallOpInterface. Includes have no results, so res_attrs is normally
484 OptionalAttr<DictArrayAttr>:$arg_attrs,
485 OptionalAttr<DictArrayAttr>:$res_attrs);
487 let assemblyFormat = [{
489 ( `<` custom<TemplateParams>($templateParams)^ `>` )?
490 `` `(` $argOperands `)`
491 ( `{` custom<MultiDimAndSymbolList>($mapOperands, $numDimsPerMap)^ `}` )?
492 `:` functional-type($argOperands, results)
493 custom<AttrDictWithWarnings>(attr-dict, prop-dict)
496 // Define builders manually so inference of operand layout attributes is not
498 let skipDefaultBuilders = 1;
500 [OpBuilder<(ins "::mlir::SymbolRefAttr":$callee,
501 CArg<"::mlir::ValueRange", "{}">:$argOperands,
502 CArg<"::llvm::ArrayRef<::mlir::Attribute>", "{}">:$templateParams)>,
503 OpBuilder<(ins "::mlir::SymbolRefAttr":$callee,
504 "::llvm::ArrayRef<::mlir::ValueRange>":$mapOperands,
505 "::mlir::DenseI32ArrayAttr":$numDimsPerMap,
506 CArg<"::mlir::ValueRange", "{}">:$argOperands,
507 CArg<"::llvm::ArrayRef<::mlir::Attribute>", "{}">:$templateParams)>,
508 OpBuilder<(ins "::mlir::SymbolRefAttr":$callee,
509 "::llvm::ArrayRef<::mlir::ValueRange>":$mapOperands,
510 "::llvm::ArrayRef<int32_t>":$numDimsPerMap,
511 CArg<"::mlir::ValueRange", "{}">:$argOperands,
512 CArg<"::llvm::ArrayRef<::mlir::Attribute>",
513 "{}">:$templateParams),
515 build($_builder, $_state, callee, mapOperands,
516 $_builder.getDenseI32ArrayAttr(numDimsPerMap),
517 argOperands, templateParams);
519 OpBuilder<(ins "::llzk::verif::ContractOp":$callee,
520 CArg<"::mlir::ValueRange", "{}">:$argOperands,
521 CArg<"::llvm::ArrayRef<::mlir::Attribute>",
522 "{}">:$templateParams),
524 build($_builder, $_state,
525 callee.getFullyQualifiedName(false),
526 argOperands, templateParams);
528 OpBuilder<(ins "::llzk::verif::ContractOp":$callee,
529 "::llvm::ArrayRef<::mlir::ValueRange>":$mapOperands,
530 "::mlir::DenseI32ArrayAttr":$numDimsPerMap,
531 CArg<"::mlir::ValueRange", "{}">:$argOperands,
532 CArg<"::llvm::ArrayRef<::mlir::Attribute>",
533 "{}">:$templateParams),
535 build($_builder, $_state,
536 callee.getFullyQualifiedName(false), mapOperands, numDimsPerMap,
537 argOperands, templateParams);
539 OpBuilder<(ins "::llzk::verif::ContractOp":$callee,
540 "::llvm::ArrayRef<::mlir::ValueRange>":$mapOperands,
541 "::llvm::ArrayRef<int32_t>":$numDimsPerMap,
542 CArg<"::mlir::ValueRange", "{}">:$argOperands,
543 CArg<"::llvm::ArrayRef<::mlir::Attribute>",
544 "{}">:$templateParams),
546 build($_builder, $_state, callee, mapOperands,
547 $_builder.getDenseI32ArrayAttr(numDimsPerMap),
548 argOperands, templateParams);
551 let extraClassDeclaration = [{
552 /// Return `true` iff the contract targets a struct type.
553 bool contractTargetsStruct();
555 /// Resolve and return the target Contract for this CallOp.
556 ::mlir::FailureOr<::llzk::SymbolLookupResult<::llzk::verif::ContractOp>>
557 getCalleeTarget(::mlir::SymbolTableCollection &tables);
561//===------------------------------------------------------------------===//
563//===------------------------------------------------------------------===//
566 : VerifDialectOp<"invariant", [HasAncestor<"::llzk::verif::ContractOp">,
567 NoTerminator, SingleBlock]> {
569 let summary = "loop invariant definition operation";
571 Defines an invariant for a loop inside the target of a contract.
573 The targeted loop op must implement the `InvariantTargetOpInterface`.
574 This interface is already implemented by the `scf.while` and `scf.for`
575 operations. The loop name is defined on either of those ops with a
576 string attribute named `loop_label`.
578 The arguments of the body must match those declared by the target op.
579 In the case of `scf.for` the control values must also be block arguments
580 in the following order: lower bound, induction variable, upper bound, and stride.
584 module attributes {llzk.lang} {
586 function.def @compute() -> !struct.type<@Top> {
587 %self = struct.new : !struct.type<@Top>
588 %c0 = arith.constant 0 : index
589 %c10 = arith.constant 10 : index
590 %c1 = arith.constant 1 : index
592 %1 = scf.for %iv = %c0 to %c10 step %c1 iter_args(%2 = %0) -> !felt.type {
593 scf.yield %2 : !felt.type
594 } {loop_label = "loopA"}
595 function.return %self : !struct.type<@Top>
597 function.def @constrain(%self: !struct.type<@Top>) {
602 verif.contract @Foo for @Top (%self: !struct.type<@Top>) {
603 verif.invariant for @loopA(%lb: index, %iv: index, %ub: index, %step: index, %extra: !felt.type) {
604 %true = arith.constant true
605 verif.require_compute %true
612 let arguments = (ins StrAttr:$loop_name, TypeArrayAttr:$loop_arg_types);
614 let regions = (region SizedRegion<1>:$region);
616 let skipDefaultBuilders = 1;
617 let builders = [OpBuilder<(ins "::mlir::StringRef":$loop_name,
618 CArg<"::llvm::ArrayRef<::mlir::Type>", "{}">:$loop_arg_types,
619 CArg<"::llvm::ArrayRef<::mlir::Location>", "{}">:$loop_arg_locs)>];
621 let hasCustomAssemblyFormat = 1;
624 let extraClassDeclaration = [{
625 /// Returns the contract operation that contains this invariant.
626 ::llzk::verif::ContractOp getParentContract();
627 /// Returns the loop target.
628 ::mlir::FailureOr<::llzk::verif::InvariantTargetOpInterface> getTarget();
632//===------------------------------------------------------------------===//
633// Invariant inner ops
634//===------------------------------------------------------------------===//
636class InvariantInnerOp<string mnemonic, list<Trait> traits = []>
639 traits#[HasAncestor<"::llzk::verif::InvariantOp">,
640 DeclareOpInterfaceMethods<MemoryEffectsOpInterface>]> {
642 let summary = mnemonic#" value operation";
644 Indicates that a value }]#mnemonic#[{ on each iteration of the loop.
646 This declares the `MemoryEffectsOpInterface`, which, like the `cf.assert` (MLIR `cf` dialect)
647 and `bool.assert` (LLZK `bool` dialect) ops, adds a MemWrite affect to model program termination.
650 let arguments = (ins LLZK_FeltType:$value);
652 let extraClassDefinition = [{
653 // This side effect models "program termination". Based on
654 // https://github.com/llvm/llvm-project/blob/f325e4b2d836d6e65a4d0cf3efc6b0996ccf3765/mlir/lib/Dialect/ControlFlow/IR/ControlFlowOps.cpp#L92-L97
655 void $cppClass::getEffects(
656 ::mlir::SmallVectorImpl<::mlir::SideEffects::EffectInstance<::mlir::MemoryEffects::Effect>> &effects
658 effects.emplace_back(::mlir::MemoryEffects::Write::get());
662 let assemblyFormat = "$value attr-dict";
665def IncreasesOp : InvariantInnerOp<"increases"> {}
667def DecreasesOp : InvariantInnerOp<"decreases"> {}
669def StepOp : VerifDialectOp<
670 "step", [HasAncestor<"::llzk::verif::InvariantOp">,
671 DeclareOpInterfaceMethods<MemoryEffectsOpInterface>,
672 NoRegionArguments, SingleBlock]> {
673 let summary = "step predicate operation";
675 Defines a predicate that must be satisfied between iterations of the loop.
677 The predicate can access the value in the previous iteration by using the `verif.old` operation.
679 Declares the `MemoryEffectsOpInterface`, which, like the `cf.assert` (MLIR `cf` dialect)
680 and `bool.assert` (LLZK `bool` dialect) ops, adds a MemWrite affect to model program termination.
683 let regions = (region SizedRegion<1>:$region);
685 let extraClassDefinition = [{
686 // This side effect models "program termination". Based on
687 // https://github.com/llvm/llvm-project/blob/f325e4b2d836d6e65a4d0cf3efc6b0996ccf3765/mlir/lib/Dialect/ControlFlow/IR/ControlFlowOps.cpp#L92-L97
688 void $cppClass::getEffects(
689 ::mlir::SmallVectorImpl<::mlir::SideEffects::EffectInstance<::mlir::MemoryEffects::Effect>> &effects
691 effects.emplace_back(::mlir::MemoryEffects::Write::get());
695 let assemblyFormat = "$region attr-dict";
699 : VerifDialectOp<"step.yield", [HasParent<"StepOp">, Terminator]> {
700 let summary = "step yield operation";
702 Terminator operation for `verif.step` blocks. Takes the boolean value carrying the predicate's result.
705 let arguments = (ins I1:$value);
707 let assemblyFormat = "$value attr-dict";
710def OldOp : VerifDialectOp<"old", [Pure, HasAncestor<"::llzk::verif::StepOp">,
711 AllTypesMatch<["value", "result"]>]> {
712 let summary = "old operation";
714 This operation allows accessing the value of an expression in the previous iteration of the loop.
716 In can only be used within the body of a `verif.step` operation.
719 let arguments = (ins AnyLLZKType:$value);
720 let results = (outs AnyLLZKType:$result);
722 let assemblyFormat = "$value `:` type($result) attr-dict";
725#endif // LLZK_VERIF_OPS