Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
booth_recode.hpp
Go to the documentation of this file.
1// Carry-less signed-Booth window recoding helpers.
2//
3// Each window is a c-bit signed digit in [-2^(c-1), 2^(c-1)], read as a (c+1)-bit
4// slice that overlaps its lower neighbour by one bit; the shared boundary bit
5// substitutes for an explicit carry. This is the algorithm Constantine calls
6// `signedWindowEncoding` / `getSignedFullWindowAt`
7// (constantine/math/arithmetic/bigints.nim).
8//
9#pragma once
10
12#include <array>
13#include <cstddef>
14#include <cstdint>
15
16namespace bb::ecc::booth {
17
28 uint32_t lo_mask;
29 uint32_t hi_mask;
30 uint32_t lo_limb;
31 uint32_t hi_limb; // == lo_limb + 1, except clamped to last valid limb at the top window
32 uint32_t lo_off;
33 uint32_t lo_bits;
35};
36
49[[nodiscard]] constexpr BoothSliceParams compute_booth_slice_params_unchecked(size_t bit_offset,
50 size_t window_bits,
51 size_t num_uint64_limbs) noexcept
52{
53 constexpr size_t LIMB_BITS = 64;
55 if (bit_offset == 0) {
56 // Bottom window: the boundary bit below the LSB is a synthetic 0. Encode this by
57 // reading "limb -1" as a zero-masked load (lo_mask = 0), then reading window_bits
58 // bits from limb 0 into the hi side and shifting them left by 1. This puts the
59 // window_bits-bit window at bits 1..window_bits with bit 0 = 0, matching the inner-
60 // loop body used by every other window. Not localised — the synthetic-lookback
61 // assembly only works in the slow path.
62 sp.lo_limb = 0; // safe in-range, but masked to 0
63 sp.hi_limb = 0; // = scalar limb 0
64 sp.lo_off = LIMB_BITS - 1;
65 sp.lo_bits = 1; // shifts hi_part left by 1, planting the window_bits-bit window at bits 1..window_bits
66 sp.lo_mask = 0; // lo_part contributes nothing
67 sp.hi_mask = (uint32_t{ 1 } << window_bits) - 1;
68 sp.slice_localised_to_one_u64 = false;
69 } else {
70 const size_t lookback_bit = bit_offset - 1;
71 const size_t bits_to_read = window_bits + 1;
72 sp.lo_limb = static_cast<uint32_t>(lookback_bit / LIMB_BITS);
73 sp.lo_off = static_cast<uint32_t>(lookback_bit & (LIMB_BITS - 1));
74 sp.lo_bits = static_cast<uint32_t>(LIMB_BITS - sp.lo_off < bits_to_read ? LIMB_BITS - sp.lo_off : bits_to_read);
75 const uint32_t hi_bits = static_cast<uint32_t>(bits_to_read) - sp.lo_bits;
76 // window_bits+1 ≤ 32 for our windows ⇒ lo_bits ≤ 32 ⇒ mask fits in uint32.
77 sp.lo_mask = (uint32_t{ 1 } << sp.lo_bits) - 1;
78 // If the natural hi-limb read would land past the end of the scalar's storage,
79 // clamp `hi_limb` to a safe in-range index and mask its contribution to zero. The
80 // top window's hi_bits worth of bits are conceptually zero (scalar < 2^num_bits ≤
81 // num_windows·window_bits). Re-reading lo_limb under a zero mask keeps the slow
82 // path's two unconditional limb loads branch-free.
83 if (static_cast<size_t>(sp.lo_limb) + 1 >= num_uint64_limbs) {
84 sp.hi_limb = sp.lo_limb;
85 sp.hi_mask = 0;
86 } else {
87 sp.hi_limb = sp.lo_limb + 1;
88 sp.hi_mask = (uint32_t{ 1 } << hi_bits) - 1;
89 }
90 // Fast path: the full (window_bits+1)-bit window lives inside `lo_limb`. hi_bits == 0
91 // captures both the in-limb case (window doesn't straddle a 64-bit boundary) and the
92 // clamped top-window case (above) where hi_mask was forced to 0.
93 sp.slice_localised_to_one_u64 = (hi_bits == 0);
94 }
95 return sp;
96}
97
98[[nodiscard]] inline BoothSliceParams compute_booth_slice_params(size_t bit_offset,
99 size_t window_bits,
100 size_t num_uint64_limbs) noexcept
101{
102 BB_ASSERT(window_bits + 1 <= 32, "Booth window_bits must fit in uint32_t masks");
103 BB_ASSERT(num_uint64_limbs > 0, "Booth scalar limb count must be non-zero");
104 return compute_booth_slice_params_unchecked(bit_offset, window_bits, num_uint64_limbs);
105}
106
107template <size_t NUM_WINDOWS, size_t WINDOW_BITS, size_t NUM_UINT64_LIMBS>
109{
110 static_assert(WINDOW_BITS + 1 <= 32);
111 static_assert(NUM_UINT64_LIMBS > 0);
112
114 for (size_t w = 0; w < NUM_WINDOWS; ++w) {
115 sp[w] = compute_booth_slice_params_unchecked(w * WINDOW_BITS, WINDOW_BITS, NUM_UINT64_LIMBS);
116 }
117 return sp;
118}
119
120template <size_t NUM_WINDOWS, size_t WINDOW_BITS, size_t LOW_WINDOW_BITS, size_t NUM_UINT64_LIMBS>
122{
123 static_assert(NUM_WINDOWS > 0);
124 static_assert(WINDOW_BITS + 1 <= 32);
125 static_assert(LOW_WINDOW_BITS + 1 <= 32);
126 static_assert(NUM_UINT64_LIMBS > 0);
127
129 sp[0] = compute_booth_slice_params_unchecked(0, LOW_WINDOW_BITS, NUM_UINT64_LIMBS);
130 for (size_t w = 1; w < NUM_WINDOWS; ++w) {
131 const size_t bit_offset = (w - 1) * WINDOW_BITS + LOW_WINDOW_BITS;
132 sp[w] = compute_booth_slice_params_unchecked(bit_offset, WINDOW_BITS, NUM_UINT64_LIMBS);
133 }
134 return sp;
135}
136
146[[nodiscard]] [[gnu::always_inline]] inline uint32_t booth_packed_digit(const uint64_t* s,
147 const BoothSliceParams& sp,
148 size_t window_bits) noexcept
149{
150 const uint64_t s_lo = s[sp.lo_limb];
151 const uint64_t s_hi = s[sp.hi_limb];
152 const uint64_t lo_part = (s_lo >> sp.lo_off) & sp.lo_mask;
153 const uint64_t hi_part = (s_hi & sp.hi_mask) << sp.lo_bits;
154 // raw fits in window_bits+1 ≤ 32 bits, safe to narrow.
155 const uint32_t raw = static_cast<uint32_t>(lo_part | hi_part);
156 const uint32_t neg = (raw >> window_bits) & uint32_t{ 1 };
157 const uint32_t neg_mask = uint32_t{ 0 } - neg;
158 const uint32_t val_mask = (uint32_t{ 1 } << window_bits) - 1;
159 const uint32_t encode = (raw + 1) >> 1;
160 const uint32_t magnitude = ((encode + neg_mask) ^ neg_mask) & val_mask;
161 return (neg << 31) | magnitude;
162}
163
164} // namespace bb::ecc::booth
#define BB_ASSERT(expression,...)
Definition assert.hpp:70
constexpr BoothSliceParams compute_booth_slice_params_unchecked(size_t bit_offset, size_t window_bits, size_t num_uint64_limbs) noexcept
Compute the Booth slice params for a window starting at absolute bit position bit_offset....
BoothSliceParams compute_booth_slice_params(size_t bit_offset, size_t window_bits, size_t num_uint64_limbs) noexcept
constexpr std::array< BoothSliceParams, NUM_WINDOWS > make_offset_booth_slice_params() noexcept
constexpr std::array< BoothSliceParams, NUM_WINDOWS > make_booth_slice_params() noexcept
uint32_t booth_packed_digit(const uint64_t *s, const BoothSliceParams &sp, size_t window_bits) noexcept
Read a (window_bits+1)-bit window from s[] (uint64 limbs) and apply Constantine's signedWindowEncodin...
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
Per-window precomputed slice parameters for the carry-less signed-Booth window recoding....