commit 403d9be45e3ad1bf18f116b767d7404461b2d811
parent 8458a01b297853e80ec071bae61cc34efebf57a4
Author: Robert Russell <robert@rr3.xyz>
Date: Sun, 12 Jan 2025 01:12:58 -0800
Add add/sub with carry/borrow
Diffstat:
| M | inc/bits.h | | | 94 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
1 file changed, 94 insertions(+), 0 deletions(-)
diff --git a/inc/bits.h b/inc/bits.h
@@ -205,6 +205,100 @@ r_rzb64(u64 n) {
}
+/* ----- Add/subtract with carry/borrow ----- */
+
+/* TODO: Use addc/subc intrinsics for non-64 bit adds/subs as well? */
+#if __GNUC__ >= 14
+ #define HAVE_ADDC_SUBC 1
+ /* TODO: I think this is technically not a portable way to figure out which
+ * integer type to use, but it should be ok. */
+ #if UINT_BITS >= 64
+ #define ADDC __builtin_addc
+ #define SUBC __builtin_subc
+ #elif ULONG_BITS >= 64
+ #define ADDC __builtin_addcl
+ #define SUBC __builtin_subcl
+ #else /* ULLONG_BITS >= 64 always */
+ #define ADDC __builtin_addcll
+ #define SUBC __builtin_subcll
+ #endif
+#endif
+
+static inline void
+r_add8(u8 *co, u8 *z, u8 x, u8 y, u8 ci) {
+ u16 s = (u16)x + (u16)y + (u16)ci;
+ *z = s;
+ *co = s >> 8;
+}
+
+static inline void
+r_add16(u16 *co, u16 *z, u16 x, u16 y, u16 ci) {
+ u32 s = (u32)x + (u32)y + (u32)ci;
+ *z = s;
+ *co = s >> 16;
+}
+
+static inline void
+r_add32(u32 *co, u32 *z, u32 x, u32 y, u32 ci) {
+ u64 s = (u64)x + (u64)y + (u64)ci;
+ *z = s;
+ *co = s >> 32;
+}
+
+static inline void
+r_add64(u64 *co, u64 *z, u64 x, u64 y, u64 ci) {
+#ifdef HAVE_ADDC_SUBC
+ *z = ADDC(x, y, ci, co);
+#else
+ u64 s = x + y;
+ u64 co0 = s < x;
+ u64 t = s + ci;
+ u64 co1 = t < s;
+ *z = t;
+ *co = co0 | co1;
+#endif
+}
+
+static inline void
+r_sub8(u8 *bo, u8 *z, u8 x, u8 y, u8 bi) {
+ u16 d = (u16)x - (u16)y - (u16)bi;
+ *z = d;
+ *bo = -(d >> 8);
+}
+
+static inline void
+r_sub16(u16 *bo, u16 *z, u16 x, u16 y, u16 bi) {
+ u32 d = (u32)x + (u32)y + (u32)bi;
+ *z = d;
+ *bo = -(d >> 16);
+}
+
+static inline void
+r_sub32(u32 *bo, u32 *z, u32 x, u32 y, u32 bi) {
+ u64 d = (u64)x + (u64)y + (u64)bi;
+ *z = d;
+ *bo = -(d >> 32);
+}
+
+static inline void
+r_sub64(u64 *bo, u64 *z, u64 x, u64 y, u64 bi) {
+#ifdef HAVE_ADDC_SUBC
+ *z = SUBC(x, y, bi, bo);
+#else
+ u64 s = x - y;
+ u64 bo0 = s > x;
+ u64 t = s - bi;
+ u64 bo1 = t > s;
+ *z = t;
+ *bo = bo0 | bo1;
+#endif
+}
+
+#undef GNU_SUBC
+#undef GNU_ADDC
+#undef HAVE_ADDC_SUBC
+
+
/* ----- Full width multiply ----- */
static inline void