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 2025 Veridise Inc.
6// SPDX-License-Identifier: Apache-2.0
8//===----------------------------------------------------------------------===//
13include "llzk/Dialect/Shared/Types.td"
14include "llzk/Dialect/Cast/IR/Dialect.td"
15include "llzk/Dialect/Cast/IR/Attrs.td"
16include "llzk/Dialect/Felt/IR/Types.td"
17include "llzk/Dialect/Shared/OpsBase.td"
18include "mlir/Interfaces/InferTypeOpInterface.td"
19include "llzk/Dialect/Function/IR/OpTraits.td"
21include "mlir/IR/OpBase.td"
22include "mlir/Interfaces/SideEffectInterfaces.td"
23include "mlir/IR/SymbolInterfaces.td"
25class CastOp<string mnemonic, list<Trait> traits = []>
27 CastDialect, mnemonic, LLZK_FeltType.builderCall,
28 traits#[DeclareOpInterfaceMethods<MemoryEffectsOpInterface>,
29 DeclareOpInterfaceMethods<ConditionallySpeculatable>]> {
30 let extraClassDefinition = [{
31 /// Model possible assertion failure as program termination, like bool.assert.
32 void $cppClass::getEffects(
33 ::llvm::SmallVectorImpl<::mlir::MemoryEffects::EffectInstance> &effects
35 if (canCastAssertFail(getValue().getType(), getOverflow())) {
36 effects.emplace_back(::mlir::MemoryEffects::Write::get());
40 /// Only infallible casts may execute on paths where they were not present.
41 ::mlir::Speculation::Speculatability $cppClass::getSpeculatability() {
42 return canCastAssertFail(getValue().getType(), getOverflow())
43 ? ::mlir::Speculation::NotSpeculatable
44 : ::mlir::Speculation::Speculatable;
51 "tofelt", [DeclareOpInterfaceMethods<
52 InferTypeOpInterface, ["isCompatibleReturnTypes"]>]> {
53 let summary = "convert an integer into a field element";
55 This operation converts a supported integer type value into a field element value.
56 Boolean (`i1`) values map to zero or one, while `index` values are interpreted
57 as signed integers. The overflow mode determines how values outside the
58 target field's safe range are handled. For `index` inputs, the default
59 `assert` mode fails on such values, even when the result is unused, and
60 cannot be speculated. Boolean inputs always fit in every field, so their
61 casts are side-effect-free and speculatable in every mode, even when the
62 result field is unspecified.
63 The `sat`, `wrap`, and `trunc` modes are side-effect-free and speculatable.
65 The `trunc` mode keeps the low `k = bitwidth(p)` bits, then reduces modulo
66 the target field's prime `p`: `(value mod 2^k) mod p`, with nonnegative
67 remainders. The result is always in `[0, p)`. This differs from `wrap`,
68 which reduces the original integer directly modulo `p`. For example, with
69 `p = 7`, truncating `-1` keeps the bits `111` and reduces `7` to `0`, while
70 wrapping `-1` yields `6`.
72 Constant folding defers casts whose result field is unspecified, whose
73 operand is unknown, or whose `assert` range check fails.
78 %0 = bool.cmp lt(%a, %b)
79 %1 = cast.tofelt %0 : i1
83 let arguments = (ins DefaultValuedAttr<LLZK_OverflowSemanticsAttr,
84 "OverflowSemantics::ASSERT">:$overflow,
85 AnyLLZKIntType:$value);
86 let results = (outs LLZK_FeltType:$result);
87 let assemblyFormat = [{
88 `` custom<OptionalOverflowSemantics>($overflow) $value
90 `` custom<InferredOrParsedType>(type($result), "false") attr-dict
93 // Children of NaryOpBase must add their extra C++ code in the extra-extra
95 let extraExtraClassDeclaration = [{
96 static bool isCompatibleReturnTypes(::mlir::TypeRange lhs, ::mlir::TypeRange rhs);
97 static ::mlir::ParseResult parseOptionalOverflowSemantics(
98 ::mlir::OpAsmParser &parser, ::llzk::cast::OverflowSemanticsAttr &overflow
101 static void printOptionalOverflowSemantics(
102 ::mlir::OpAsmPrinter &printer, IntToFeltOp op,
103 ::llzk::cast::OverflowSemanticsAttr overflow
110def LLZK_FeltToIndexOp : CastOp<"toindex", [NotFieldNative]> {
111 let summary = "convert a field element into an index";
113 This operation converts a field element value into an index value to allow use
114 as an array index or loop bound.
116 The default `assert` overflow mode fails when the value is outside the
117 index safe range, even when the result is unused, and cannot be speculated.
118 If the source has a declared field whose maximum canonical value `p-1`
119 fits in the index safe range, the assertion cannot fail, so the cast is
120 side-effect-free and speculatable. Unspecified fields remain conservative.
121 The `sat`, `wrap`, and `trunc` modes are side-effect-free and speculatable.
123 Constant folding reduces the operand in its field before applying the
124 overflow mode and returns an index constant. Folding defers when the field
125 is unspecified or the `assert` range check fails.
130 %1 = array.read %b[%0]
134 let arguments = (ins DefaultValuedAttr<LLZK_OverflowSemanticsAttr,
135 "OverflowSemantics::ASSERT">:$overflow,
136 LLZK_FeltType:$value);
137 let results = (outs Index:$result);
138 let assemblyFormat = [{
139 `` custom<OptionalOverflowSemantics>($overflow) $value
140 `` custom<InferredOrParsedType>(type($value), "true") attr-dict
143 let extraExtraClassDeclaration = [{
144 static ::mlir::ParseResult parseOptionalOverflowSemantics(
145 ::mlir::OpAsmParser &parser, ::llzk::cast::OverflowSemanticsAttr &overflow
148 static void printOptionalOverflowSemantics(
149 ::mlir::OpAsmPrinter &printer, FeltToIndexOp op,
150 ::llzk::cast::OverflowSemanticsAttr overflow
157#endif // LLZK_CAST_OPS