Skip to content

Commit

Permalink
Simplify arithmetic with a bignum and a small
Browse files Browse the repository at this point in the history
  • Loading branch information
bjorng committed Aug 25, 2023
1 parent e43e063 commit 8d6cb95
Show file tree
Hide file tree
Showing 3 changed files with 157 additions and 45 deletions.
74 changes: 71 additions & 3 deletions erts/emulator/beam/big.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

/*
Expand Down
4 changes: 3 additions & 1 deletion erts/emulator/beam/big.h
Original file line number Diff line number Diff line change
Expand Up @@ -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*);
Expand Down
124 changes: 83 additions & 41 deletions erts/emulator/beam/erl_arith.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand All @@ -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;
}
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand All @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand All @@ -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;
}
Expand All @@ -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;
Expand Down

0 comments on commit 8d6cb95

Please sign in to comment.