1use crate::codegen_sva::fabric_rtl::Divergence;
7
8use super::multiword::{Builder, Mw};
9
10pub const FAULT_BITS: u32 = 8;
12
13pub fn n64(b: &Builder) -> usize {
15 b.shape().words_for(64)
16}
17
18pub fn nfault(b: &Builder) -> usize {
20 b.shape().words_for(FAULT_BITS)
21}
22
23pub fn fault_when(b: &mut Builder, cond: u32, d: Divergence, otherwise: &Mw) -> Mw {
25 let n = otherwise.len();
26 let code = b.mw_const(d.code() as u128, n);
27 b.mw_sel(cond, &code, otherwise)
28}
29
30pub fn no_fault(b: &mut Builder) -> Mw {
32 let n = nfault(b);
33 b.mw_zero(n)
34}
35
36pub fn magnitude(b: &mut Builder, a: &Mw) -> (Mw, u32) {
38 let s = b.mw_top_bit(a);
39 let neg = b.mw_neg(a);
40 (b.mw_sel(s, &neg, a), s)
41}
42
43pub fn add(b: &mut Builder, x: &Mw, y: &Mw) -> (Mw, Mw) {
45 let z = b.k(0);
46 let (s, _) = b.mw_add(x, y, z);
47 let sx = b.mw_top_bit(x);
48 let sy = b.mw_top_bit(y);
49 let ss = b.mw_top_bit(&s);
50 let same = b.xor(sx, sy);
51 let same = b.bit_not(same);
52 let flipped = b.xor(sx, ss);
53 let ovf = b.and(same, flipped);
54 let ok = no_fault(b);
55 (s, fault_when(b, ovf, Divergence::IntOverflow, &ok))
56}
57
58pub fn sub(b: &mut Builder, x: &Mw, y: &Mw) -> (Mw, Mw) {
60 let z = b.k(0);
61 let (d, _) = b.mw_sub(x, y, z);
62 let sx = b.mw_top_bit(x);
63 let sy = b.mw_top_bit(y);
64 let sd = b.mw_top_bit(&d);
65 let differ = b.xor(sx, sy);
66 let flipped = b.xor(sx, sd);
67 let ovf = b.and(differ, flipped);
68 let ok = no_fault(b);
69 (d, fault_when(b, ovf, Divergence::IntOverflow, &ok))
70}
71
72pub fn mul_checked(b: &mut Builder, x: &Mw, y: &Mw) -> (Mw, u32) {
74 let n = x.len();
75 let (mx, sx) = magnitude(b, x);
76 let (my, sy) = magnitude(b, y);
77 let p = b.mw_mul(&mx, &my);
78 let lo = p.slice(0, n);
79 let hi = p.slice(n, 2 * n);
80 let neg = b.xor(sx, sy);
81 let hi_clear = b.mw_is_zero(&hi);
82 let top = b.mw_top_bit(&lo);
83 let min_mag = b.mw_const(1u128 << 63, n);
84 let is_min_mag = b.mw_eq(&lo, &min_mag);
85 let fits_pos = b.bit_not(top);
86 let fits_neg = b.or(fits_pos, is_min_mag);
87 let fits_low = b.sel(neg, fits_neg, fits_pos);
88 let fits = b.and(hi_clear, fits_low);
89 let neg_lo = b.mw_neg(&lo);
90 let value = b.mw_sel(neg, &neg_lo, &lo);
91 let ovf = b.bit_not(fits);
92 (value, ovf)
93}
94
95pub fn mul(b: &mut Builder, x: &Mw, y: &Mw) -> (Mw, Mw) {
97 let (v, ovf) = mul_checked(b, x, y);
98 let ok = no_fault(b);
99 (v, fault_when(b, ovf, Divergence::IntOverflow, &ok))
100}
101
102fn divmod_signed(b: &mut Builder, x: &Mw, y: &Mw) -> (Mw, Mw, u32, u32) {
105 let n = x.len();
106 let y_zero = b.mw_is_zero(y);
107 let one = b.mw_const(1, n);
108 let y_safe = b.mw_sel(y_zero, &one, y);
109 let (mx, sx) = magnitude(b, x);
110 let (my, sy) = magnitude(b, &y_safe);
111 let (q, r) = b.mw_divrem(&mx, &my);
112 let qneg = b.xor(sx, sy);
113 let nq = b.mw_neg(&q);
114 let q = b.mw_sel(qneg, &nq, &q);
115 let nr = b.mw_neg(&r);
116 let r = b.mw_sel(sx, &nr, &r);
117 let min = b.mw_const(1u128 << 63, n);
118 let neg_one = b.mw_const(u64::MAX as u128, n);
119 let x_min = b.mw_eq(x, &min);
120 let y_m1 = b.mw_eq(y, &neg_one);
121 let min_by_m1 = b.and(x_min, y_m1);
122 (q, r, y_zero, min_by_m1)
123}
124
125pub fn div(b: &mut Builder, x: &Mw, y: &Mw) -> (Mw, Mw) {
127 let (q, _, zero, min_by_m1) = divmod_signed(b, x, y);
128 let ok = no_fault(b);
129 let f = fault_when(b, min_by_m1, Divergence::IntOverflow, &ok);
130 (q, fault_when(b, zero, Divergence::DivideByZero, &f))
131}
132
133pub fn rem(b: &mut Builder, x: &Mw, y: &Mw) -> (Mw, Mw) {
135 let (_, r, zero, _) = divmod_signed(b, x, y);
136 let ok = no_fault(b);
137 (r, fault_when(b, zero, Divergence::DivideByZero, &ok))
138}
139
140pub fn floor_div(b: &mut Builder, x: &Mw, y: &Mw) -> (Mw, Mw) {
142 let (q, r, zero, min_by_m1) = divmod_signed(b, x, y);
143 let sx = b.mw_top_bit(x);
144 let sy = b.mw_top_bit(y);
145 let differ = b.xor(sx, sy);
146 let r_zero = b.mw_is_zero(&r);
147 let inexact = b.bit_not(r_zero);
148 let down = b.and(differ, inexact);
149 let n = q.len();
150 let zero_w = b.k(0);
151 let dec = b.mw_from_word(down, n);
152 let (q2, _) = b.mw_sub(&q, &dec, zero_w);
153 let ok = no_fault(b);
154 let f = fault_when(b, min_by_m1, Divergence::IntOverflow, &ok);
155 (q2, fault_when(b, zero, Divergence::DivideByZero, &f))
156}
157
158pub fn pow(b: &mut Builder, x: &Mw, e: &Mw) -> (Mw, Mw) {
162 let n = x.len();
163 let (mx, sx) = magnitude(b, x);
164 let e_neg = b.mw_top_bit(e);
165 let hi32 = b.mw_shr(e, 32);
166 let hi_set = b.mw_or_reduce(&hi32);
167 let hi_set = b.nonzero(hi_set);
168 let not_neg = b.bit_not(e_neg);
169 let too_large = b.and(hi_set, not_neg);
170 let one = b.mw_const(1, n);
171 let acc = b.mw_state(&one);
172 let base = b.mw_state(&mx);
173 let z = b.k(0);
174 let acc_ovf = b.state(z);
175 let base_ovf = b.state(z);
176 let exp = b.mw_state(e);
177 b.repeat(32, |s| {
178 let bit = s.mw_bit(&exp, 0);
179 let p = s.mw_mul(&acc, &base);
180 let p_lo = p.slice(0, n);
181 let p_hi = p.slice(n, 2 * n);
182 let p_over = s.mw_or_reduce(&p_hi);
183 let p_over = s.nonzero(p_over);
184 let over_now = s.or(p_over, base_ovf);
185 let acc2 = s.mw_sel(bit, &p_lo, &acc);
186 let took = s.and(bit, over_now);
187 let acc_ovf2 = s.or(acc_ovf, took);
188 let sq = s.mw_mul(&base, &base);
189 let sq_lo = sq.slice(0, n);
190 let sq_hi = sq.slice(n, 2 * n);
191 let sq_over = s.mw_or_reduce(&sq_hi);
192 let sq_over = s.nonzero(sq_over);
193 let base_ovf2 = s.or(base_ovf, sq_over);
194 let exp2 = s.mw_shr(&exp, 1);
195 s.mw_assign(&acc, &acc2);
196 s.assign(acc_ovf, acc_ovf2);
197 s.mw_assign(&base, &sq_lo);
198 s.assign(base_ovf, base_ovf2);
199 s.mw_assign(&exp, &exp2);
200 });
201 let odd = b.mw_bit(e, 0);
202 let neg = b.and(sx, odd);
203 let top = b.mw_top_bit(&acc);
204 let min_mag = b.mw_const(1u128 << 63, n);
205 let is_min_mag = b.mw_eq(&acc, &min_mag);
206 let fits_pos = b.bit_not(top);
207 let fits_neg = b.or(fits_pos, is_min_mag);
208 let fits = b.sel(neg, fits_neg, fits_pos);
209 let range_ovf = b.bit_not(fits);
210 let ovf = b.or(acc_ovf, range_ovf);
211 let nacc = b.mw_neg(&acc);
212 let value = b.mw_sel(neg, &nacc, &acc);
213 let ok = no_fault(b);
214 let f = fault_when(b, ovf, Divergence::IntOverflow, &ok);
215 let f = fault_when(b, too_large, Divergence::ExponentTooLarge, &f);
216 (
217 value,
218 fault_when(b, e_neg, Divergence::NegativeExponent, &f),
219 )
220}
221
222const SHIFT_BITS: u32 = 6;
225
226pub fn shl(b: &mut Builder, x: &Mw, y: &Mw) -> Mw {
228 b.mw_shl_var(x, y, SHIFT_BITS)
229}
230
231pub fn shr(b: &mut Builder, x: &Mw, y: &Mw) -> Mw {
233 b.mw_sar_var(x, y, SHIFT_BITS)
234}
235
236pub fn ushr(b: &mut Builder, x: &Mw, y: &Mw) -> Mw {
238 b.mw_shr_var_sticky(x, y, SHIFT_BITS).0
239}
240
241pub fn mul_hi_u(b: &mut Builder, x: &Mw, y: &Mw) -> Mw {
243 let n = x.len();
244 let p = b.mw_mul(x, y);
245 p.slice(n, 2 * n)
246}
247
248pub fn mul_ovf_s(b: &mut Builder, x: &Mw, y: &Mw) -> Mw {
250 let n = x.len();
251 let (_, ovf) = mul_checked(b, x, y);
252 b.mw_from_word(ovf, n)
253}
254
255pub fn popcount(b: &mut Builder, x: &Mw) -> Mw {
257 let n = x.len();
258 let c = b.mw_popcount(x, b.shape().words_for(8));
259 b.mw_resize(&c, n)
260}
261
262pub fn bool64(b: &mut Builder, p: u32, n: usize) -> Mw {
264 b.mw_from_word(p, n)
265}