From 8d6cb9552fbc0339c5a04538aa30fe3816f5ca1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 24 Aug 2023 12:07:53 +0200 Subject: [PATCH] Simplify arithmetic with a bignum and a small --- erts/emulator/beam/big.c | 74 +++++++++++++++++++- erts/emulator/beam/big.h | 4 +- erts/emulator/beam/erl_arith.c | 124 ++++++++++++++++++++++----------- 3 files changed, 157 insertions(+), 45 deletions(-) diff --git a/erts/emulator/beam/big.c b/erts/emulator/beam/big.c index 06dc9b02200c..1e3912c4a1c2 100644 --- a/erts/emulator/beam/big.c +++ b/erts/emulator/beam/big.c @@ -2900,12 +2900,80 @@ Eterm big_plus_small(Eterm x, Uint y, Eterm *r) BIG_V(r)), (short) BIG_SIGN(xp)); } -Eterm big_times_small(Eterm x, Uint y, Eterm *r) +Eterm big_add_signed(Eterm x, Sint y0, Eterm *r) { Eterm* xp = big_val(x); + ErtsDigit y; + short xsgn; + short ysgn; + + xsgn = BIG_SIGN(xp); + if (y0 >= 0) { + ysgn = 0; + y = (ErtsDigit)y0; + } else { + ysgn = 1; + y = (ErtsDigit)-y0; + } + + if (xsgn == ysgn) { + ASSERT(I_comp(BIG_V(xp), BIG_SIZE(xp), &y, 1) >= 0); + return big_norm(r, D_add(BIG_V(xp), BIG_SIZE(xp), + y, BIG_V(r)), + xsgn); + } else { + ASSERT(I_comp(BIG_V(xp), BIG_SIZE(xp), &y, 1) >= 0); + return big_norm(r, D_sub(BIG_V(xp), BIG_SIZE(xp), + y, BIG_V(r)), + xsgn); + } +} + +/* Calculate SignedSmall - Big */ +Eterm big_sub_signed(Sint x0, Eterm y, Eterm *r) +{ + Eterm* yp = big_val(y); + ErtsDigit x; + short xsgn; + short ysgn; + + if (x0 >= 0) { + xsgn = 0; + x = (ErtsDigit)x0; + } else { + xsgn = 1; + x = (ErtsDigit)-x0; + } + ysgn = !BIG_SIGN(yp); + + if (xsgn == ysgn) { + ASSERT(I_comp(BIG_V(yp), BIG_SIZE(yp), &x, 1) >= 0); + return big_norm(r, D_add(BIG_V(yp), BIG_SIZE(yp), + x, BIG_V(r)), + xsgn); + } else { + ASSERT(I_comp(BIG_V(yp), BIG_SIZE(yp), &x, 1) >= 0); + return big_norm(r, D_sub(BIG_V(yp), BIG_SIZE(yp), + x, BIG_V(r)), + ysgn); + } +} + +Eterm big_mul_small(Eterm x, Eterm y, Eterm *r) +{ + Eterm* xp = big_val(x); + Sint val; + short sgn = BIG_SIGN(xp); + + val = signed_val(y); + if (val < 0) { + val = -val; + sgn = !sgn; + } - return big_norm(r, D_mul(BIG_V(xp),BIG_SIZE(xp), (ErtsDigit) y, - BIG_V(r)), (short) BIG_SIGN(xp)); + return big_norm(r, D_mul(BIG_V(xp), BIG_SIZE(xp), + (ErtsDigit) val, BIG_V(r)), + sgn); } /* diff --git a/erts/emulator/beam/big.h b/erts/emulator/beam/big.h index b705421ca907..6f18c449bf97 100644 --- a/erts/emulator/beam/big.h +++ b/erts/emulator/beam/big.h @@ -144,7 +144,9 @@ Eterm big_div(Eterm, Eterm, Eterm*); Eterm big_rem(Eterm, Eterm, Eterm*); Eterm big_plus_small(Eterm, Uint, Eterm*); -Eterm big_times_small(Eterm, Uint, Eterm*); +Eterm big_add_signed(Eterm x, Sint y, Eterm *r); +Eterm big_sub_signed(Sint x0, Eterm y, Eterm *r); +Eterm big_mul_small(Eterm, Eterm, Eterm*); Eterm big_band(Eterm, Eterm, Eterm*); Eterm big_bor(Eterm, Eterm, Eterm*); diff --git a/erts/emulator/beam/erl_arith.c b/erts/emulator/beam/erl_arith.c index e15333400757..69525584b03e 100644 --- a/erts/emulator/beam/erl_arith.c +++ b/erts/emulator/beam/erl_arith.c @@ -312,8 +312,6 @@ BIF_RETTYPE bnot_1(BIF_ALIST_1) Eterm erts_mixed_plus(Process* p, Eterm arg1, Eterm arg2) { - DECLARE_TMP(tmp_big1,0,p); - DECLARE_TMP(tmp_big2,1,p); Eterm res; Eterm hdr; FloatDef f1, f2; @@ -352,8 +350,16 @@ erts_mixed_plus(Process* p, Eterm arg1, Eterm arg2) if (arg1 == SMALL_ZERO) { return arg2; } - arg1 = small_to_big(signed_val(arg1), tmp_big1); - goto do_big; + sz = big_size(arg2) + 1; + need_heap = BIG_NEED_SIZE(sz); + hp = HeapFragOnlyAlloc(p, need_heap); + res = big_add_signed(arg2, signed_val(arg1), hp); + maybe_shrink(p, hp, res, need_heap); + if (is_nil(res)) { + p->freason = SYSTEM_LIMIT; + return THE_NON_VALUE; + } + return res; case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE): f1.fd = signed_val(arg1); GET_DOUBLE(arg2, f2); @@ -377,8 +383,16 @@ erts_mixed_plus(Process* p, Eterm arg1, Eterm arg2) if (arg2 == SMALL_ZERO) { return arg1; } - arg2 = small_to_big(signed_val(arg2), tmp_big2); - goto do_big; + sz = big_size(arg1) + 1; + need_heap = BIG_NEED_SIZE(sz); + hp = HeapFragOnlyAlloc(p, need_heap); + res = big_add_signed(arg1, signed_val(arg2), hp); + maybe_shrink(p, hp, res, need_heap); + if (is_nil(res)) { + p->freason = SYSTEM_LIMIT; + return THE_NON_VALUE; + } + return res; default: goto badarith; } @@ -387,7 +401,6 @@ erts_mixed_plus(Process* p, Eterm arg1, Eterm arg2) switch ((hdr & _TAG_HEADER_MASK) >> _TAG_PRIMARY_SIZE) { case (_TAG_HEADER_POS_BIG >> _TAG_PRIMARY_SIZE): case (_TAG_HEADER_NEG_BIG >> _TAG_PRIMARY_SIZE): - do_big: sz1 = big_size(arg1); sz2 = big_size(arg2); sz = MAX(sz1, sz2)+1; @@ -522,8 +535,6 @@ erts_unary_minus(Process* p, Eterm arg) Eterm erts_mixed_minus(Process* p, Eterm arg1, Eterm arg2) { - DECLARE_TMP(tmp_big1,0,p); - DECLARE_TMP(tmp_big2,1,p); Eterm hdr; Eterm res; FloatDef f1, f2; @@ -559,8 +570,16 @@ erts_mixed_minus(Process* p, Eterm arg1, Eterm arg2) switch ((hdr & _TAG_HEADER_MASK) >> _TAG_PRIMARY_SIZE) { case (_TAG_HEADER_POS_BIG >> _TAG_PRIMARY_SIZE): case (_TAG_HEADER_NEG_BIG >> _TAG_PRIMARY_SIZE): - arg1 = small_to_big(signed_val(arg1), tmp_big1); - goto do_big; + sz = big_size(arg2) + 1; + need_heap = BIG_NEED_SIZE(sz); + hp = HeapFragOnlyAlloc(p, need_heap); + res = big_sub_signed(signed_val(arg1), arg2, hp); + maybe_shrink(p, hp, res, need_heap); + if (is_nil(res)) { + p->freason = SYSTEM_LIMIT; + return THE_NON_VALUE; + } + return res; case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE): f1.fd = signed_val(arg1); GET_DOUBLE(arg2, f2); @@ -584,9 +603,24 @@ erts_mixed_minus(Process* p, Eterm arg1, Eterm arg2) if (arg2 == SMALL_ZERO) { return arg1; } - arg2 = small_to_big(signed_val(arg2), tmp_big2); - - do_big: + sz = big_size(arg1) + 1; + need_heap = BIG_NEED_SIZE(sz); + hp = HeapFragOnlyAlloc(p, need_heap); + res = big_add_signed(arg1, -signed_val(arg2), hp); + maybe_shrink(p, hp, res, need_heap); + if (is_nil(res)) { + p->freason = SYSTEM_LIMIT; + return THE_NON_VALUE; + } + return res; + default: + goto badarith; + } + case TAG_PRIMARY_BOXED: + hdr = *boxed_val(arg2); + switch ((hdr & _TAG_HEADER_MASK) >> _TAG_PRIMARY_SIZE) { + case (_TAG_HEADER_POS_BIG >> _TAG_PRIMARY_SIZE): + case (_TAG_HEADER_NEG_BIG >> _TAG_PRIMARY_SIZE): sz1 = big_size(arg1); sz2 = big_size(arg2); sz = MAX(sz1, sz2)+1; @@ -599,15 +633,6 @@ erts_mixed_minus(Process* p, Eterm arg1, Eterm arg2) return THE_NON_VALUE; } return res; - default: - goto badarith; - } - case TAG_PRIMARY_BOXED: - hdr = *boxed_val(arg2); - switch ((hdr & _TAG_HEADER_MASK) >> _TAG_PRIMARY_SIZE) { - case (_TAG_HEADER_POS_BIG >> _TAG_PRIMARY_SIZE): - case (_TAG_HEADER_NEG_BIG >> _TAG_PRIMARY_SIZE): - goto do_big; case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE): if (big_to_double(arg1, &f1.fd) < 0) { goto badarith; @@ -665,8 +690,6 @@ erts_mixed_minus(Process* p, Eterm arg1, Eterm arg2) Eterm erts_mixed_times(Process* p, Eterm arg1, Eterm arg2) { - DECLARE_TMP(tmp_big1,0,p); - DECLARE_TMP(tmp_big2,1,p); Eterm hdr; Eterm res; FloatDef f1, f2; @@ -737,9 +760,23 @@ erts_mixed_times(Process* p, Eterm arg1, Eterm arg2) return(SMALL_ZERO); if (arg1 == SMALL_ONE) return(arg2); - arg1 = small_to_big(signed_val(arg1), tmp_big1); - sz = 2 + big_size(arg2); - goto do_big; + sz = big_size(arg2) + 1; + need_heap = BIG_NEED_SIZE(sz); +#ifdef DEBUG + need_heap++; +#endif + hp = HeapFragOnlyAlloc(p, need_heap); +#ifdef DEBUG + hp[need_heap-1] = ERTS_HOLE_MARKER; +#endif + res = big_mul_small(arg2, arg1, hp); + ASSERT(hp[need_heap-1] == ERTS_HOLE_MARKER); + maybe_shrink(p, hp, res, need_heap); + if (is_nil(res)) { + p->freason = SYSTEM_LIMIT; + return THE_NON_VALUE; + } + return res; case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE): f1.fd = signed_val(arg1); GET_DOUBLE(arg2, f2); @@ -764,9 +801,24 @@ erts_mixed_times(Process* p, Eterm arg1, Eterm arg2) return(SMALL_ZERO); if (arg2 == SMALL_ONE) return(arg1); - arg2 = small_to_big(signed_val(arg2), tmp_big2); - sz = 2 + big_size(arg1); - goto do_big; + sz = big_size(arg1) + 1; + need_heap = BIG_NEED_SIZE(sz); +#ifdef DEBUG + need_heap++; +#endif + hp = HeapFragOnlyAlloc(p, need_heap); + +#ifdef DEBUG + hp[need_heap-1] = ERTS_HOLE_MARKER; +#endif + res = big_mul_small(arg1, arg2, hp); + ASSERT(hp[need_heap-1] == ERTS_HOLE_MARKER); + maybe_shrink(p, hp, res, need_heap); + if (is_nil(res)) { + p->freason = SYSTEM_LIMIT; + return THE_NON_VALUE; + } + return res; default: goto badarith; } @@ -778,26 +830,16 @@ erts_mixed_times(Process* p, Eterm arg1, Eterm arg2) sz1 = big_size(arg1); sz2 = big_size(arg2); sz = sz1 + sz2; - - do_big: need_heap = BIG_NEED_SIZE(sz); #ifdef DEBUG need_heap++; #endif hp = HeapFragOnlyAlloc(p, need_heap); - #ifdef DEBUG hp[need_heap-1] = ERTS_HOLE_MARKER; #endif res = big_times(arg1, arg2, hp); ASSERT(hp[need_heap-1] == ERTS_HOLE_MARKER); - - /* - * Note that the result must be big in this case, since - * at least one operand was big to begin with, and - * the absolute value of the other is > 1. - */ - maybe_shrink(p, hp, res, need_heap); if (is_nil(res)) { p->freason = SYSTEM_LIMIT;