LLZK 2.1.1
An open-source IR for Zero Knowledge (ZK) circuits
Loading...
Searching...
No Matches
DynamicAPIntHelper.cpp
Go to the documentation of this file.
1//===-- DynamicAPIntHelper.cpp ----------------------------------*- C++ -*-===//
2//
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
7//
8//===----------------------------------------------------------------------===//
9
11
12#include "llzk/Util/Compare.h"
13
14#include <llvm/ADT/ArrayRef.h>
15#include <llvm/ADT/SmallString.h>
16#include <llvm/Support/raw_ostream.h>
17
18#include <limits>
19
20using namespace llvm;
21using namespace std;
22
23static DynamicAPInt po2(const DynamicAPInt &e) {
24 // APInt/APSInt bitwidth is limited to max unsigned bits, so must be strictly
25 // less than the max to accommodate for the sign bit
26 assert(e >= 0);
27 assert(e < std::numeric_limits<unsigned>::max());
28 unsigned shiftAmt = llzk::toAPSInt(e).getZExtValue();
29 APSInt p(shiftAmt + 1, /* isUnsigned */ true);
30 p.setBit(shiftAmt);
31 return llzk::toDynamicAPInt(p);
32}
33
34static DynamicAPInt fromBigEndian(const std::vector<bool> &bits) {
35 APSInt rawInt(bits.size(), /* isUnsigned */ false);
36 for (unsigned i = 0; i < bits.size(); ++i) {
37 rawInt.setBitVal(i, bits[i]);
38 }
39 return llzk::toDynamicAPInt(rawInt);
40}
41
42static DynamicAPInt
43binaryBitOp(const DynamicAPInt &lhs, const DynamicAPInt &rhs, function_ref<bool(bool, bool)> fn) {
44 DynamicAPInt a = lhs, b = rhs;
45 std::vector<bool> bits;
46 while (a != 0 || b != 0) {
47 // bits are sign extended
48 bool abit = a != 0 ? bool(int64_t(a % 2)) : lhs < 0;
49 bool bbit = b != 0 ? bool(int64_t(b % 2)) : rhs < 0;
50 bits.push_back(fn(abit, bbit));
51 a /= 2;
52 b /= 2;
53 }
54 // Insert final sign bit, as the above will ignore 0 sign bits. This is also
55 // acceptable when both numbers are signed, as it acts as a sign extension.
56 bits.push_back(fn(lhs < 0, rhs < 0));
57 return fromBigEndian(bits);
58}
59
60namespace llzk {
61
62DynamicAPInt operator&(const DynamicAPInt &lhs, const DynamicAPInt &rhs) {
63 auto fn = [](bool a, bool b) { return a && b; };
64 return binaryBitOp(lhs, rhs, fn);
65}
66
67DynamicAPInt operator|(const DynamicAPInt &lhs, const DynamicAPInt &rhs) {
68 auto fn = [](bool a, bool b) { return a || b; };
69 return binaryBitOp(lhs, rhs, fn);
70}
71
72DynamicAPInt operator^(const DynamicAPInt &lhs, const DynamicAPInt &rhs) {
73 auto fn = [](bool a, bool b) { return a ^ b; };
74 return binaryBitOp(lhs, rhs, fn);
75}
76
77DynamicAPInt operator<<(const DynamicAPInt &lhs, const DynamicAPInt &rhs) { return lhs * po2(rhs); }
78
79DynamicAPInt operator>>(const DynamicAPInt &lhs, const DynamicAPInt &rhs) {
80 if (lhs >= 0) {
81 return lhs / po2(rhs);
82 } else {
83 // round towards negative infinity
84 DynamicAPInt divisor = po2(rhs);
85 if (lhs % divisor == 0) {
86 return lhs / divisor;
87 } else {
88 return (lhs - (divisor - 1)) / divisor;
89 }
90 }
91}
92
93DynamicAPInt toDynamicAPInt(StringRef str) {
94 APSInt parsedInt(str);
95 return toDynamicAPInt(parsedInt);
96}
97
98DynamicAPInt toDynamicAPInt(const APSInt &i) {
99 // Fast path for smaller values, just use the `int64_t` conversion. However, that only works if
100 // the value is signed or if the sign bit is clear otherwise it will incorrectly interpret the
101 // value as a negative number.
102 if (i.getBitWidth() <= 64 && (i.isSigned() || i.isSignBitClear())) {
103 return DynamicAPInt(i.isNegative() ? i.getSExtValue() : static_cast<int64_t>(i.getZExtValue()));
104 }
105
106 DynamicAPInt res(0), po2(1);
107 // Since LLVM 20 doesn't have a direct APInt to DynamicAPInt constructor, we
108 // manually construct the DynamicAPInt from bits of the input.
109 // We use the positive representation so our negation works at the end.
110 APSInt raw = i < 0 ? -i : i;
111 for (unsigned b = 0; b < raw.getActiveBits(); b++) {
112 DynamicAPInt bitSet(raw[b]);
113 res += (bitSet * po2);
114 po2 *= 2;
115 }
116 if (i.isNegative() && res > 0) {
117 res = -res;
118 }
119 return res;
120}
121
122APSInt toAPSInt(const DynamicAPInt &i) {
123 if (numeric_limits<int64_t>::min() <= i && i <= numeric_limits<int64_t>::max()) {
124 // Fast path for smaller values, just use the int64_t conversion
125 return APSInt::get(int64_t(i));
126 }
127
128 // Else, convert to string and parse back as an APSInt.
129 // This may not be the most efficient implementation, but it is the cleanest
130 // due to the lack of direct conversions between DynamicAPInt and APInts.
131 SmallString<64> repr;
132 raw_svector_ostream(repr) << i;
133
134 APSInt res(repr);
135 // For consistency, we add a bit and mark these as signed integers, since
136 // DynamicAPInts are inherently signed.
137 res = res.extend(res.getBitWidth() + 1);
138 res.setIsSigned(true);
139
140 return res;
141}
142
143APInt toAPInt(const DynamicAPInt &val, unsigned bitWidth) {
144 SmallString<64> str;
145 raw_svector_ostream(str) << val;
146 return APInt(bitWidth + 1, str, 10);
147}
148
149APInt toExactWidthAPInt(const DynamicAPInt &val, unsigned bitWidth) {
150 SmallString<64> str;
151 raw_svector_ostream(str) << val;
152 return APInt(bitWidth, str, 10);
153}
154
155DynamicAPInt modExp(const DynamicAPInt &base, const DynamicAPInt &exp, const DynamicAPInt &mod) {
156 DynamicAPInt result(1);
157 DynamicAPInt b = base;
158 DynamicAPInt e = exp;
159 DynamicAPInt one(1);
160
161 while (e != 0) {
162 if (e % 2 != 0) {
163 result = (result * b) % mod;
164 }
165
166 b = (b * b) % mod;
167 e = e >> one;
168 }
169 return result;
170}
171
172DynamicAPInt modInversePrime(const DynamicAPInt &f, const DynamicAPInt &p) {
173 assert(f != 0 && "0 has no inverse");
174 // Fermat: f^(p-2) mod p
175 DynamicAPInt exp = p - 2;
176 DynamicAPInt result = modExp(f, exp, p);
177 assert((f * result) % p == 1 && "inverse is incorrect");
178 return result;
179}
180
181} // namespace llzk
This file implements helper methods for constructing DynamicAPInts.
ExpressionValue mod(const llvm::SMTSolverRef &solver, const ExpressionValue &lhs, const ExpressionValue &rhs)
Interval operator|(const Interval &lhs, const Interval &rhs)
Interval operator^(const Interval &lhs, const Interval &rhs)
DynamicAPInt toDynamicAPInt(StringRef str)
Interval operator<<(const Interval &lhs, const Interval &rhs)
APInt toAPInt(const DynamicAPInt &val, unsigned bitWidth)
APInt toExactWidthAPInt(const DynamicAPInt &val, unsigned bitWidth)
Interval operator>>(const Interval &lhs, const Interval &rhs)
Interval operator&(const Interval &lhs, const Interval &rhs)
DynamicAPInt modInversePrime(const DynamicAPInt &f, const DynamicAPInt &p)
APSInt toAPSInt(const DynamicAPInt &i)
DynamicAPInt modExp(const DynamicAPInt &base, const DynamicAPInt &exp, const DynamicAPInt &mod)