UltrafastSecp256k1 3.50.0
Ultra high-performance secp256k1 elliptic curve cryptography library
Loading...
Searching...
No Matches
field_branchless.hpp
Go to the documentation of this file.
1#ifndef SECP256K1_FIELD_BRANCHLESS_HPP
2#define SECP256K1_FIELD_BRANCHLESS_HPP
3
4#include <cstdint>
5#include "field.hpp"
6
7namespace secp256k1::fast {
8
9// ============================================================================
10// Branchless Operations (GPU-inspired, CPU-optimized)
11// ============================================================================
12// These functions use conditional moves (CMOV) instead of branches for
13// predictable performance. Critical for hot paths and batch operations.
14//
15// Performance impact:
16// - Eliminates branch mispredictions (~10-20 cycles penalty)
17// - Enables better pipelining and instruction-level parallelism
18// - 5-10% faster in tight loops with unpredictable conditions
19//
20// Usage:
21// - field_cmov: Conditional move entire field element
22// - field_cmovznz: Move based on zero/nonzero flag
23// - field_select: Select between two field elements
24//
25// ============================================================================
26
27// Branchless conditional move: if (flag) *r = *a; else *r = *b;
28// Uses CMOV instruction when available (x86_64, ARM64)
29// flag MUST be 0 or 1 (behavior undefined otherwise)
30inline void field_cmov(FieldElement* r, const FieldElement* a,
31 const FieldElement* b, bool flag) noexcept {
32 // Convert flag to mask: 0 -> 0x0000000000000000, 1 -> 0xFFFFFFFFFFFFFFFF
33 std::uint64_t const mask = 0ULL - static_cast<std::uint64_t>(flag);
34
35 auto& r_limbs = const_cast<std::array<std::uint64_t, 4>&>(r->limbs());
36 const auto& a_limbs = a->limbs();
37 const auto& b_limbs = b->limbs();
38
39 // Branchless selection: r = (a & mask) | (b & ~mask)
40 r_limbs[0] = (a_limbs[0] & mask) | (b_limbs[0] & ~mask);
41 r_limbs[1] = (a_limbs[1] & mask) | (b_limbs[1] & ~mask);
42 r_limbs[2] = (a_limbs[2] & mask) | (b_limbs[2] & ~mask);
43 r_limbs[3] = (a_limbs[3] & mask) | (b_limbs[3] & ~mask);
44}
45
46// Conditional move zero/nonzero: if (flag != 0) *r = *a; else *r = *b;
47// More flexible than field_cmov: flag can be any value (not just 0/1)
48inline void field_cmovznz(FieldElement* r, const FieldElement* a,
49 const FieldElement* b, std::uint64_t flag) noexcept {
50 // Convert flag to mask: 0 -> 0x0000000000000000, nonzero -> 0xFFFFFFFFFFFFFFFF
51 // Use bitwise OR reduction across all bits to detect nonzero
52 std::uint64_t mask = (flag | (0ULL - flag)) >> 63; // Branchless nonzero test
53 mask = 0ULL - mask; // Expand to full 64-bit mask
54
55 auto& r_limbs = const_cast<std::array<std::uint64_t, 4>&>(r->limbs());
56 const auto& a_limbs = a->limbs();
57 const auto& b_limbs = b->limbs();
58
59 r_limbs[0] = (a_limbs[0] & mask) | (b_limbs[0] & ~mask);
60 r_limbs[1] = (a_limbs[1] & mask) | (b_limbs[1] & ~mask);
61 r_limbs[2] = (a_limbs[2] & mask) | (b_limbs[2] & ~mask);
62 r_limbs[3] = (a_limbs[3] & mask) | (b_limbs[3] & ~mask);
63}
64
65// Select between two field elements: return (flag ? a : b)
66// Returns NEW FieldElement (no in-place mutation)
68 bool flag) noexcept {
69 std::uint64_t const mask = 0ULL - static_cast<std::uint64_t>(flag);
70
71 const auto& a_limbs = a.limbs();
72 const auto& b_limbs = b.limbs();
73
75 (a_limbs[0] & mask) | (b_limbs[0] & ~mask),
76 (a_limbs[1] & mask) | (b_limbs[1] & ~mask),
77 (a_limbs[2] & mask) | (b_limbs[2] & ~mask),
78 (a_limbs[3] & mask) | (b_limbs[3] & ~mask)
79 });
80}
81
82// Check if field element is zero (branchless)
83// Returns 1 if zero, 0 if nonzero
84inline std::uint64_t field_is_zero(const FieldElement& a) noexcept {
85 const auto& limbs = a.limbs();
86 std::uint64_t const z = limbs[0] | limbs[1] | limbs[2] | limbs[3];
87 // Branchless zero check: z==0 ? 1 : 0
88 return (z | (0ULL - z)) >> 63 ^ 1;
89}
90
91// Check if two field elements are equal (branchless)
92// Returns 1 if equal, 0 if different
93inline std::uint64_t field_eq(const FieldElement& a, const FieldElement& b) noexcept {
94 const auto& a_limbs = a.limbs();
95 const auto& b_limbs = b.limbs();
96
97 std::uint64_t const diff = (a_limbs[0] ^ b_limbs[0]) |
98 (a_limbs[1] ^ b_limbs[1]) |
99 (a_limbs[2] ^ b_limbs[2]) |
100 (a_limbs[3] ^ b_limbs[3]);
101
102 // Branchless equality check: diff==0 ? 1 : 0
103 return (diff | (0ULL - diff)) >> 63 ^ 1;
104}
105
106// Conditional negate: if (flag) *r = -a; else *r = a;
107// Used in GLV scalar decomposition and point operations
108inline void field_cneg(FieldElement* r, const FieldElement& a, bool flag) noexcept {
109 FieldElement const negated = FieldElement::zero() - a;
110 field_cmov(r, &negated, &a, flag);
111}
112
113// Conditional addition: if (flag) *r = a + b; else *r = a;
114// Avoids branch in tight loops
115inline void field_cadd(FieldElement* r, const FieldElement& a,
116 const FieldElement& b, bool flag) noexcept {
117 FieldElement const sum = a + b;
118 field_cmov(r, &sum, &a, flag);
119}
120
121// Conditional subtraction: sets *r to (a - b) when flag is true, otherwise keeps a
122inline void field_csub(FieldElement* r, const FieldElement& a,
123 const FieldElement& b, bool flag) noexcept {
124 FieldElement const diff = a - b;
125 field_cmov(r, &diff, &a, flag);
126}
127
128} // namespace secp256k1::fast
129
130#endif // SECP256K1_FIELD_BRANCHLESS_HPP
const limbs_type & limbs() const noexcept
Definition field.hpp:58
static FieldElement zero()
static FieldElement from_limbs(const limbs_type &limbs)
void field_csub(FieldElement *r, const FieldElement &a, const FieldElement &b, bool flag) noexcept
void field_cmov(FieldElement *r, const FieldElement *a, const FieldElement *b, bool flag) noexcept
FieldElement field_select(const FieldElement &a, const FieldElement &b, bool flag) noexcept
void field_cneg(FieldElement *r, const FieldElement &a, bool flag) noexcept
void field_cadd(FieldElement *r, const FieldElement &a, const FieldElement &b, bool flag) noexcept
std::uint64_t field_eq(const FieldElement &a, const FieldElement &b) noexcept
std::uint64_t field_is_zero(const FieldElement &a) noexcept
void field_cmovznz(FieldElement *r, const FieldElement *a, const FieldElement *b, std::uint64_t flag) noexcept