aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator/beam/erl_arith.c
diff options
context:
space:
mode:
Diffstat (limited to 'erts/emulator/beam/erl_arith.c')
-rw-r--r--erts/emulator/beam/erl_arith.c2040
1 files changed, 2040 insertions, 0 deletions
diff --git a/erts/emulator/beam/erl_arith.c b/erts/emulator/beam/erl_arith.c
new file mode 100644
index 0000000000..b692832677
--- /dev/null
+++ b/erts/emulator/beam/erl_arith.c
@@ -0,0 +1,2040 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1999-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/*
+ * Arithmetic functions formerly found in beam_emu.c
+ * now available as bifs as erl_db_util and db_match_compile needs
+ * them.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "sys.h"
+#include "erl_vm.h"
+#include "global.h"
+#include "erl_process.h"
+#include "error.h"
+#include "bif.h"
+#include "big.h"
+#include "atom.h"
+
+#ifndef MAX
+# define MAX(x, y) (((x) > (y)) ? (x) : (y))
+#endif
+
+static Eterm shift(Process* p, Eterm arg1, Eterm arg2, int right);
+
+static ERTS_INLINE void maybe_shrink(Process* p, Eterm* hp, Eterm res, Uint alloc)
+{
+ Uint actual;
+
+ if (is_immed(res)) {
+ if (p->heap <= hp && hp < p->htop) {
+ p->htop = hp;
+#if defined(CHECK_FOR_HOLES)
+ } else {
+ erts_arith_shrink(p, hp);
+#endif
+ }
+ } else if ((actual = bignum_header_arity(*hp)+1) < alloc) {
+ if (p->heap <= hp && hp < p->htop) {
+ p->htop = hp+actual;
+#if defined(CHECK_FOR_HOLES)
+ } else {
+ erts_arith_shrink(p, hp+actual);
+#endif
+ }
+ }
+}
+
+/*
+ * BIF interfaces. They will only be from match specs and
+ * when a BIF is applied.
+ */
+
+BIF_RETTYPE splus_1(BIF_ALIST_1)
+{
+ if (is_number(BIF_ARG_1)) {
+ BIF_RET(BIF_ARG_1);
+ } else {
+ BIF_ERROR(BIF_P, BADARITH);
+ }
+}
+
+BIF_RETTYPE splus_2(BIF_ALIST_2)
+{
+ BIF_RET(erts_mixed_plus(BIF_P, BIF_ARG_1, BIF_ARG_2));
+}
+
+BIF_RETTYPE sminus_1(BIF_ALIST_1)
+{
+ BIF_RET(erts_mixed_minus(BIF_P, make_small(0), BIF_ARG_1));
+}
+
+BIF_RETTYPE sminus_2(BIF_ALIST_2)
+{
+ BIF_RET(erts_mixed_minus(BIF_P, BIF_ARG_1, BIF_ARG_2));
+}
+
+BIF_RETTYPE stimes_2(BIF_ALIST_2)
+{
+ BIF_RET(erts_mixed_times(BIF_P, BIF_ARG_1, BIF_ARG_2));
+}
+
+BIF_RETTYPE div_2(BIF_ALIST_2)
+{
+ BIF_RET(erts_mixed_div(BIF_P, BIF_ARG_1, BIF_ARG_2));
+}
+
+BIF_RETTYPE intdiv_2(BIF_ALIST_2)
+{
+ if (BIF_ARG_2 == SMALL_ZERO) {
+ BIF_ERROR(BIF_P, BADARITH);
+ }
+ if (is_both_small(BIF_ARG_1,BIF_ARG_2)){
+ Sint ires = signed_val(BIF_ARG_1) / signed_val(BIF_ARG_2);
+ if (MY_IS_SSMALL(ires))
+ BIF_RET(make_small(ires));
+ }
+ BIF_RET(erts_int_div(BIF_P, BIF_ARG_1, BIF_ARG_2));
+}
+
+BIF_RETTYPE rem_2(BIF_ALIST_2)
+{
+ if (BIF_ARG_2 == SMALL_ZERO) {
+ BIF_ERROR(BIF_P, BADARITH);
+ }
+ if (is_both_small(BIF_ARG_1,BIF_ARG_2)){
+ /* Is this really correct? Isn't there a difference between
+ remainder and modulo that is not defined in C? Well, I don't
+ remember, this is the way it's done in beam_emu anyway... */
+ BIF_RET(make_small(signed_val(BIF_ARG_1) % signed_val(BIF_ARG_2)));
+ }
+ BIF_RET(erts_int_rem(BIF_P, BIF_ARG_1, BIF_ARG_2));
+}
+
+BIF_RETTYPE band_2(BIF_ALIST_2)
+{
+ if (is_both_small(BIF_ARG_1,BIF_ARG_2)){
+ BIF_RET(BIF_ARG_1 & BIF_ARG_2);
+ }
+ BIF_RET(erts_band(BIF_P, BIF_ARG_1, BIF_ARG_2));
+}
+
+BIF_RETTYPE bor_2(BIF_ALIST_2)
+{
+ if (is_both_small(BIF_ARG_1,BIF_ARG_2)){
+ BIF_RET(BIF_ARG_1 | BIF_ARG_2);
+ }
+ BIF_RET(erts_bor(BIF_P, BIF_ARG_1, BIF_ARG_2));
+}
+
+BIF_RETTYPE bxor_2(BIF_ALIST_2)
+{
+ if (is_both_small(BIF_ARG_1,BIF_ARG_2)){
+ BIF_RET(make_small(signed_val(BIF_ARG_1) ^ signed_val(BIF_ARG_2)));
+ }
+ BIF_RET(erts_bxor(BIF_P, BIF_ARG_1, BIF_ARG_2));
+}
+
+BIF_RETTYPE bsl_2(Process* p, Eterm arg1, Eterm arg2)
+{
+ BIF_RET(shift(p, arg1, arg2, 0));
+}
+
+BIF_RETTYPE bsr_2(Process* p, Eterm arg1, Eterm arg2)
+{
+ BIF_RET(shift(p, arg1, arg2, 1));
+}
+
+static Eterm
+shift(Process* p, Eterm arg1, Eterm arg2, int right)
+{
+ Sint i;
+ Sint ires;
+ Eterm tmp_big1[2];
+ Eterm* bigp;
+ Uint need;
+
+ if (right) {
+ if (is_small(arg2)) {
+ i = -signed_val(arg2);
+ if (is_small(arg1)) {
+ goto small_shift;
+ } else if (is_big(arg1)) {
+ if (i == 0) {
+ BIF_RET(arg1);
+ }
+ goto big_shift;
+ }
+ } else if (is_big(arg2)) {
+ /*
+ * N bsr NegativeBigNum == N bsl MAX_SMALL
+ * N bsr PositiveBigNum == N bsl MIN_SMALL
+ */
+ arg2 = make_small(bignum_header_is_neg(*big_val(arg2)) ?
+ MAX_SMALL : MIN_SMALL);
+ goto do_bsl;
+ }
+ } else {
+ do_bsl:
+ if (is_small(arg2)) {
+ i = signed_val(arg2);
+
+ if (is_small(arg1)) {
+ small_shift:
+ ires = signed_val(arg1);
+
+ if (i == 0 || ires == 0) {
+ BIF_RET(arg1);
+ } else if (i < 0) { /* Right shift */
+ i = -i;
+ if (i >= SMALL_BITS-1) {
+ arg1 = (ires < 0) ? SMALL_MINUS_ONE : SMALL_ZERO;
+ } else {
+ arg1 = make_small(ires >> i);
+ }
+ BIF_RET(arg1);
+ } else if (i < SMALL_BITS-1) { /* Left shift */
+ if ((ires > 0 && ((~(Uint)0 << ((SMALL_BITS-1)-i)) & ires) == 0) ||
+ ((~(Uint)0 << ((SMALL_BITS-1)-i)) & ~ires) == 0) {
+ arg1 = make_small(ires << i);
+ BIF_RET(arg1);
+ }
+ }
+ arg1 = small_to_big(ires, tmp_big1);
+
+ big_shift:
+ if (i > 0) { /* Left shift. */
+ ires = big_size(arg1) + (i / D_EXP);
+ } else { /* Right shift. */
+ ires = big_size(arg1);
+ if (ires <= (-i / D_EXP))
+ ires = 3;
+ else
+ ires -= (-i / D_EXP);
+ }
+
+ /*
+ * Slightly conservative check the size to avoid
+ * allocating huge amounts of memory for bignums that
+ * clearly would overflow the arity in the header
+ * word.
+ */
+ if (ires-8 > BIG_ARITY_MAX) {
+ BIF_ERROR(p, SYSTEM_LIMIT);
+ }
+ need = BIG_NEED_SIZE(ires+1);
+ bigp = HAlloc(p, need);
+ arg1 = big_lshift(arg1, i, bigp);
+ maybe_shrink(p, bigp, arg1, need);
+ if (is_nil(arg1)) {
+ /*
+ * This result must have been only slight larger
+ * than allowed since it wasn't caught by the
+ * previous test.
+ */
+ BIF_ERROR(p, SYSTEM_LIMIT);
+ }
+ BIF_RET(arg1);
+ } else if (is_big(arg1)) {
+ if (i == 0) {
+ BIF_RET(arg1);
+ }
+ goto big_shift;
+ }
+ } else if (is_big(arg2)) {
+ if (bignum_header_is_neg(*big_val(arg2))) {
+ /*
+ * N bsl NegativeBigNum is either 0 or -1, depending on
+ * the sign of N. Since we don't believe this case
+ * is common, do the calculation with the minimum
+ * amount of code.
+ */
+ arg2 = make_small(MIN_SMALL);
+ goto do_bsl;
+ } else if (is_small(arg1) || is_big(arg1)) {
+ /*
+ * N bsl PositiveBigNum is too large to represent.
+ */
+ BIF_ERROR(p, SYSTEM_LIMIT);
+ }
+ /* Fall through if the left argument is not an integer. */
+ }
+ }
+ BIF_ERROR(p, BADARITH);
+}
+
+BIF_RETTYPE bnot_1(BIF_ALIST_1)
+{
+ Eterm ret;
+
+ if (is_small(BIF_ARG_1)) {
+ ret = make_small(~signed_val(BIF_ARG_1));
+ } else if (is_big(BIF_ARG_1)) {
+ Uint need = BIG_NEED_SIZE(big_size(BIF_ARG_1)+1);
+ Eterm* bigp = HAlloc(BIF_P, need);
+
+ ret = big_bnot(BIF_ARG_1, bigp);
+ maybe_shrink(BIF_P, bigp, ret, need);
+ if (is_nil(ret)) {
+ BIF_ERROR(BIF_P, SYSTEM_LIMIT);
+ }
+ } else {
+ BIF_ERROR(BIF_P, BADARITH);
+ }
+ BIF_RET(ret);
+}
+
+/*
+ * Implementation and interfaces for the rest of the runtime system.
+ * The functions that follow are only used in match specs and when
+ * arithmetic functions are applied.
+ */
+
+Eterm
+erts_mixed_plus(Process* p, Eterm arg1, Eterm arg2)
+{
+ Eterm tmp_big1[2];
+ Eterm tmp_big2[2];
+ Eterm res;
+ Eterm hdr;
+ FloatDef f1, f2;
+ dsize_t sz1, sz2, sz;
+ int need_heap;
+ Eterm* hp;
+ Sint ires;
+
+ ERTS_FP_CHECK_INIT(p);
+ switch (arg1 & _TAG_PRIMARY_MASK) {
+ case TAG_PRIMARY_IMMED1:
+ switch ((arg1 & _TAG_IMMED1_MASK) >> _TAG_PRIMARY_SIZE) {
+ case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE):
+ switch (arg2 & _TAG_PRIMARY_MASK) {
+ case TAG_PRIMARY_IMMED1:
+ switch ((arg2 & _TAG_IMMED1_MASK) >> _TAG_PRIMARY_SIZE) {
+ case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE):
+ ires = signed_val(arg1) + signed_val(arg2);
+ ASSERT(MY_IS_SSMALL(ires) == IS_SSMALL(ires));
+ if (MY_IS_SSMALL(ires)) {
+ return make_small(ires);
+ } else {
+ hp = HAlloc(p, 2);
+ res = small_to_big(ires, hp);
+ return res;
+ }
+ default:
+ badarith:
+ p->freason = BADARITH;
+ return THE_NON_VALUE;
+ }
+ 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):
+ if (arg1 == SMALL_ZERO) {
+ return arg2;
+ }
+ arg1 = small_to_big(signed_val(arg1), tmp_big1);
+ goto do_big;
+ case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE):
+ f1.fd = signed_val(arg1);
+ GET_DOUBLE(arg2, f2);
+ goto do_float;
+ default:
+ goto badarith;
+ }
+ }
+ default:
+ goto badarith;
+ }
+ case TAG_PRIMARY_BOXED:
+ hdr = *boxed_val(arg1);
+ switch ((hdr & _TAG_HEADER_MASK) >> _TAG_PRIMARY_SIZE) {
+ case (_TAG_HEADER_POS_BIG >> _TAG_PRIMARY_SIZE):
+ case (_TAG_HEADER_NEG_BIG >> _TAG_PRIMARY_SIZE):
+ switch (arg2 & _TAG_PRIMARY_MASK) {
+ case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE):
+ switch ((arg2 & _TAG_IMMED1_MASK) >> _TAG_PRIMARY_SIZE) {
+ case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE):
+ if (arg2 == SMALL_ZERO) {
+ return arg1;
+ }
+ arg2 = small_to_big(signed_val(arg2), tmp_big2);
+ goto do_big;
+ 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):
+ do_big:
+ sz1 = big_size(arg1);
+ sz2 = big_size(arg2);
+ sz = MAX(sz1, sz2)+1;
+ need_heap = BIG_NEED_SIZE(sz);
+ hp = HAlloc(p, need_heap);
+ res = big_plus(arg1, arg2, hp);
+ if (is_nil(res)) {
+ erts_arith_shrink(p, hp);
+ p->freason = SYSTEM_LIMIT;
+ return THE_NON_VALUE;
+ }
+ maybe_shrink(p, hp, res, need_heap);
+ return res;
+ case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE):
+ if (big_to_double(arg1, &f1.fd) < 0) {
+ goto badarith;
+ }
+ GET_DOUBLE(arg2, f2);
+ goto do_float;
+ default:
+ goto badarith;
+ }
+ }
+ case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE):
+ switch (arg2 & _TAG_PRIMARY_MASK) {
+ case TAG_PRIMARY_IMMED1:
+ switch ((arg2 & _TAG_IMMED1_MASK) >> _TAG_PRIMARY_SIZE) {
+ case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE):
+ GET_DOUBLE(arg1, f1);
+ f2.fd = signed_val(arg2);
+ goto do_float;
+ 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):
+ GET_DOUBLE(arg1, f1);
+ if (big_to_double(arg2, &f2.fd) < 0) {
+ goto badarith;
+ }
+ goto do_float;
+ case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE):
+ GET_DOUBLE(arg1, f1);
+ GET_DOUBLE(arg2, f2);
+
+ do_float:
+ f1.fd = f1.fd + f2.fd;
+ ERTS_FP_ERROR(p, f1.fd, goto badarith);
+ hp = HAlloc(p, FLOAT_SIZE_OBJECT);
+ res = make_float(hp);
+ PUT_DOUBLE(f1, hp);
+ return res;
+ default:
+ goto badarith;
+ }
+ default:
+ goto badarith;
+ }
+ }
+ default:
+ goto badarith;
+ }
+}
+
+Eterm
+erts_mixed_minus(Process* p, Eterm arg1, Eterm arg2)
+{
+ Eterm tmp_big1[2];
+ Eterm tmp_big2[2];
+ Eterm hdr;
+ Eterm res;
+ FloatDef f1, f2;
+ dsize_t sz1, sz2, sz;
+ int need_heap;
+ Eterm* hp;
+ Sint ires;
+
+ ERTS_FP_CHECK_INIT(p);
+ switch (arg1 & _TAG_PRIMARY_MASK) {
+ case TAG_PRIMARY_IMMED1:
+ switch ((arg1 & _TAG_IMMED1_MASK) >> _TAG_PRIMARY_SIZE) {
+ case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE):
+ switch (arg2 & _TAG_PRIMARY_MASK) {
+ case TAG_PRIMARY_IMMED1:
+ switch ((arg2 & _TAG_IMMED1_MASK) >> _TAG_PRIMARY_SIZE) {
+ case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE):
+ ires = signed_val(arg1) - signed_val(arg2);
+ ASSERT(MY_IS_SSMALL(ires) == IS_SSMALL(ires));
+ if (MY_IS_SSMALL(ires)) {
+ return make_small(ires);
+ } else {
+ hp = HAlloc(p, 2);
+ res = small_to_big(ires, hp);
+ return res;
+ }
+ default:
+ badarith:
+ p->freason = BADARITH;
+ return THE_NON_VALUE;
+ }
+ 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):
+ arg1 = small_to_big(signed_val(arg1), tmp_big1);
+ goto do_big;
+ case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE):
+ f1.fd = signed_val(arg1);
+ GET_DOUBLE(arg2, f2);
+ goto do_float;
+ default:
+ goto badarith;
+ }
+ }
+ default:
+ goto badarith;
+ }
+ case TAG_PRIMARY_BOXED:
+ hdr = *boxed_val(arg1);
+ switch ((hdr & _TAG_HEADER_MASK) >> _TAG_PRIMARY_SIZE) {
+ case (_TAG_HEADER_POS_BIG >> _TAG_PRIMARY_SIZE):
+ case (_TAG_HEADER_NEG_BIG >> _TAG_PRIMARY_SIZE):
+ switch (arg2 & _TAG_PRIMARY_MASK) {
+ case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE):
+ switch ((arg2 & _TAG_IMMED1_MASK) >> _TAG_PRIMARY_SIZE) {
+ case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE):
+ if (arg2 == SMALL_ZERO) {
+ return arg1;
+ }
+ arg2 = small_to_big(signed_val(arg2), tmp_big2);
+
+ do_big:
+ sz1 = big_size(arg1);
+ sz2 = big_size(arg2);
+ sz = MAX(sz1, sz2)+1;
+ need_heap = BIG_NEED_SIZE(sz);
+ hp = HAlloc(p, need_heap);
+ res = big_minus(arg1, arg2, hp);
+ if (is_nil(res)) {
+ erts_arith_shrink(p, hp);
+ p->freason = SYSTEM_LIMIT;
+ return THE_NON_VALUE;
+ }
+ maybe_shrink(p, hp, res, need_heap);
+ 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;
+ }
+ GET_DOUBLE(arg2, f2);
+ goto do_float;
+ default:
+ goto badarith;
+ }
+ }
+ case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE):
+ switch (arg2 & _TAG_PRIMARY_MASK) {
+ case TAG_PRIMARY_IMMED1:
+ switch ((arg2 & _TAG_IMMED1_MASK) >> _TAG_PRIMARY_SIZE) {
+ case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE):
+ GET_DOUBLE(arg1, f1);
+ f2.fd = signed_val(arg2);
+ goto do_float;
+ 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):
+ GET_DOUBLE(arg1, f1);
+ if (big_to_double(arg2, &f2.fd) < 0) {
+ goto badarith;
+ }
+ goto do_float;
+ case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE):
+ GET_DOUBLE(arg1, f1);
+ GET_DOUBLE(arg2, f2);
+
+ do_float:
+ f1.fd = f1.fd - f2.fd;
+ ERTS_FP_ERROR(p, f1.fd, goto badarith);
+ hp = HAlloc(p, FLOAT_SIZE_OBJECT);
+ res = make_float(hp);
+ PUT_DOUBLE(f1, hp);
+ return res;
+ default:
+ goto badarith;
+ }
+ default:
+ goto badarith;
+ }
+ }
+ default:
+ goto badarith;
+ }
+}
+
+Eterm
+erts_mixed_times(Process* p, Eterm arg1, Eterm arg2)
+{
+ Eterm tmp_big1[2];
+ Eterm tmp_big2[2];
+ Eterm hdr;
+ Eterm res;
+ FloatDef f1, f2;
+ dsize_t sz1, sz2, sz;
+ int need_heap;
+ Eterm* hp;
+
+ ERTS_FP_CHECK_INIT(p);
+ switch (arg1 & _TAG_PRIMARY_MASK) {
+ case TAG_PRIMARY_IMMED1:
+ switch ((arg1 & _TAG_IMMED1_MASK) >> _TAG_PRIMARY_SIZE) {
+ case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE):
+ switch (arg2 & _TAG_PRIMARY_MASK) {
+ case TAG_PRIMARY_IMMED1:
+ switch ((arg2 & _TAG_IMMED1_MASK) >> _TAG_PRIMARY_SIZE) {
+ case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE):
+ if ((arg1 == SMALL_ZERO) || (arg2 == SMALL_ZERO)) {
+ return(SMALL_ZERO);
+ } else if (arg1 == SMALL_ONE) {
+ return(arg2);
+ } else if (arg2 == SMALL_ONE) {
+ return(arg1);
+ } else {
+ Eterm big_res[3];
+
+ /*
+ * The following code is optimized for the case that
+ * result is small (which should be the most common case
+ * in practice).
+ */
+ res = small_times(signed_val(arg1), signed_val(arg2), big_res);
+ if (is_small(res)) {
+ return res;
+ } else {
+ /*
+ * The result is a a big number.
+ * Allocate a heap fragment and copy the result.
+ * Be careful to allocate exactly what we need
+ * to not leave any holes.
+ */
+ Uint arity;
+
+ ASSERT(is_big(res));
+ hdr = big_res[0];
+ arity = bignum_header_arity(hdr);
+ ASSERT(arity == 1 || arity == 2);
+ hp = HAlloc(p, arity+1);
+ res = make_big(hp);
+ *hp++ = hdr;
+ *hp++ = big_res[1];
+ if (arity > 1) {
+ *hp = big_res[2];
+ }
+ return res;
+ }
+ }
+ default:
+ badarith:
+ p->freason = BADARITH;
+ return THE_NON_VALUE;
+ }
+ 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):
+ if (arg1 == SMALL_ZERO)
+ 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;
+ case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE):
+ f1.fd = signed_val(arg1);
+ GET_DOUBLE(arg2, f2);
+ goto do_float;
+ default:
+ goto badarith;
+ }
+ }
+ default:
+ goto badarith;
+ }
+ case TAG_PRIMARY_BOXED:
+ hdr = *boxed_val(arg1);
+ switch ((hdr & _TAG_HEADER_MASK) >> _TAG_PRIMARY_SIZE) {
+ case (_TAG_HEADER_POS_BIG >> _TAG_PRIMARY_SIZE):
+ case (_TAG_HEADER_NEG_BIG >> _TAG_PRIMARY_SIZE):
+ switch (arg2 & _TAG_PRIMARY_MASK) {
+ case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE):
+ switch ((arg2 & _TAG_IMMED1_MASK) >> _TAG_PRIMARY_SIZE) {
+ case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE):
+ if (arg2 == SMALL_ZERO)
+ 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;
+ 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 = sz1 + sz2;
+
+ do_big:
+ need_heap = BIG_NEED_SIZE(sz);
+ hp = HAlloc(p, need_heap);
+ res = big_times(arg1, arg2, hp);
+
+ /*
+ * 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.
+ */
+
+ if (is_nil(res)) {
+ erts_arith_shrink(p, hp);
+ p->freason = SYSTEM_LIMIT;
+ return THE_NON_VALUE;
+ }
+ maybe_shrink(p, hp, res, need_heap);
+ return res;
+ case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE):
+ if (big_to_double(arg1, &f1.fd) < 0) {
+ goto badarith;
+ }
+ GET_DOUBLE(arg2, f2);
+ goto do_float;
+ default:
+ goto badarith;
+ }
+ }
+ case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE):
+ switch (arg2 & _TAG_PRIMARY_MASK) {
+ case TAG_PRIMARY_IMMED1:
+ switch ((arg2 & _TAG_IMMED1_MASK) >> _TAG_PRIMARY_SIZE) {
+ case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE):
+ GET_DOUBLE(arg1, f1);
+ f2.fd = signed_val(arg2);
+ goto do_float;
+ 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):
+ GET_DOUBLE(arg1, f1);
+ if (big_to_double(arg2, &f2.fd) < 0) {
+ goto badarith;
+ }
+ goto do_float;
+ case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE):
+ GET_DOUBLE(arg1, f1);
+ GET_DOUBLE(arg2, f2);
+
+ do_float:
+ f1.fd = f1.fd * f2.fd;
+ ERTS_FP_ERROR(p, f1.fd, goto badarith);
+ hp = HAlloc(p, FLOAT_SIZE_OBJECT);
+ res = make_float(hp);
+ PUT_DOUBLE(f1, hp);
+ return res;
+ default:
+ goto badarith;
+ }
+ default:
+ goto badarith;
+ }
+ }
+ default:
+ goto badarith;
+ }
+}
+
+Eterm
+erts_mixed_div(Process* p, Eterm arg1, Eterm arg2)
+{
+ FloatDef f1, f2;
+ Eterm* hp;
+ Eterm hdr;
+
+ ERTS_FP_CHECK_INIT(p);
+ switch (arg1 & _TAG_PRIMARY_MASK) {
+ case TAG_PRIMARY_IMMED1:
+ switch ((arg1 & _TAG_IMMED1_MASK) >> _TAG_PRIMARY_SIZE) {
+ case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE):
+ switch (arg2 & _TAG_PRIMARY_MASK) {
+ case TAG_PRIMARY_IMMED1:
+ switch ((arg2 & _TAG_IMMED1_MASK) >> _TAG_PRIMARY_SIZE) {
+ case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE):
+ f1.fd = signed_val(arg1);
+ f2.fd = signed_val(arg2);
+ goto do_float;
+ default:
+ badarith:
+ p->freason = BADARITH;
+ return THE_NON_VALUE;
+ }
+ 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):
+ f1.fd = signed_val(arg1);
+ if (big_to_double(arg2, &f2.fd) < 0) {
+ goto badarith;
+ }
+ goto do_float;
+ case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE):
+ f1.fd = signed_val(arg1);
+ GET_DOUBLE(arg2, f2);
+ goto do_float;
+ default:
+ goto badarith;
+ }
+ }
+ default:
+ goto badarith;
+ }
+ case TAG_PRIMARY_BOXED:
+ hdr = *boxed_val(arg1);
+ switch ((hdr & _TAG_HEADER_MASK) >> _TAG_PRIMARY_SIZE) {
+ case (_TAG_HEADER_POS_BIG >> _TAG_PRIMARY_SIZE):
+ case (_TAG_HEADER_NEG_BIG >> _TAG_PRIMARY_SIZE):
+ switch (arg2 & _TAG_PRIMARY_MASK) {
+ case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE):
+ switch ((arg2 & _TAG_IMMED1_MASK) >> _TAG_PRIMARY_SIZE) {
+ case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE):
+ if (big_to_double(arg1, &f1.fd) < 0) {
+ goto badarith;
+ }
+ f2.fd = signed_val(arg2);
+ goto do_float;
+ 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):
+ if (big_to_double(arg1, &f1.fd) < 0 ||
+ big_to_double(arg2, &f2.fd) < 0) {
+ goto badarith;
+ }
+ goto do_float;
+ case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE):
+ if (big_to_double(arg1, &f1.fd) < 0) {
+ goto badarith;
+ }
+ GET_DOUBLE(arg2, f2);
+ goto do_float;
+ default:
+ goto badarith;
+ }
+ }
+ case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE):
+ switch (arg2 & _TAG_PRIMARY_MASK) {
+ case TAG_PRIMARY_IMMED1:
+ switch ((arg2 & _TAG_IMMED1_MASK) >> _TAG_PRIMARY_SIZE) {
+ case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE):
+ GET_DOUBLE(arg1, f1);
+ f2.fd = signed_val(arg2);
+ goto do_float;
+ 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):
+ GET_DOUBLE(arg1, f1);
+ if (big_to_double(arg2, &f2.fd) < 0) {
+ goto badarith;
+ }
+ goto do_float;
+ case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE):
+ GET_DOUBLE(arg1, f1);
+ GET_DOUBLE(arg2, f2);
+
+ do_float:
+ f1.fd = f1.fd / f2.fd;
+ ERTS_FP_ERROR(p, f1.fd, goto badarith);
+ hp = HAlloc(p, FLOAT_SIZE_OBJECT);
+ PUT_DOUBLE(f1, hp);
+ return make_float(hp);
+ default:
+ goto badarith;
+ }
+ default:
+ goto badarith;
+ }
+ }
+ default:
+ goto badarith;
+ }
+}
+
+Eterm
+erts_int_div(Process* p, Eterm arg1, Eterm arg2)
+{
+ Eterm tmp_big1[2];
+ Eterm tmp_big2[2];
+ int ires;
+
+ switch (NUMBER_CODE(arg1, arg2)) {
+ case SMALL_SMALL:
+ /* This case occurs if the most negative fixnum is divided by -1. */
+ ASSERT(arg2 == make_small(-1));
+ arg1 = small_to_big(signed_val(arg1), tmp_big1);
+ /*FALLTHROUGH*/
+ case BIG_SMALL:
+ arg2 = small_to_big(signed_val(arg2), tmp_big2);
+ goto L_big_div;
+ case SMALL_BIG:
+ if (arg1 != make_small(MIN_SMALL)) {
+ return SMALL_ZERO;
+ }
+ arg1 = small_to_big(signed_val(arg1), tmp_big1);
+ /*FALLTHROUGH*/
+ case BIG_BIG:
+ L_big_div:
+ ires = big_ucomp(arg1, arg2);
+ if (ires < 0) {
+ arg1 = SMALL_ZERO;
+ } else if (ires == 0) {
+ arg1 = (big_sign(arg1) == big_sign(arg2)) ?
+ SMALL_ONE : SMALL_MINUS_ONE;
+ } else {
+ Eterm* hp;
+ int i = big_size(arg1);
+ Uint need;
+
+ ires = big_size(arg2);
+ need = BIG_NEED_SIZE(i-ires+1) + BIG_NEED_SIZE(i);
+ hp = HAlloc(p, need);
+ arg1 = big_div(arg1, arg2, hp);
+ if (is_nil(arg1)) {
+ erts_arith_shrink(p, hp);
+ p->freason = SYSTEM_LIMIT;
+ return THE_NON_VALUE;
+ }
+ maybe_shrink(p, hp, arg1, need);
+ }
+ return arg1;
+ default:
+ p->freason = BADARITH;
+ return THE_NON_VALUE;
+ }
+}
+
+Eterm
+erts_int_rem(Process* p, Eterm arg1, Eterm arg2)
+{
+ Eterm tmp_big1[2];
+ Eterm tmp_big2[2];
+ int ires;
+
+ switch (NUMBER_CODE(arg1, arg2)) {
+ case BIG_SMALL:
+ arg2 = small_to_big(signed_val(arg2), tmp_big2);
+ goto L_big_rem;
+ case SMALL_BIG:
+ if (arg1 != make_small(MIN_SMALL)) {
+ return arg1;
+ } else {
+ Eterm tmp = small_to_big(signed_val(arg1), tmp_big1);
+ if ((ires = big_ucomp(tmp, arg2)) == 0) {
+ return SMALL_ZERO;
+ } else {
+ ASSERT(ires < 0);
+ return arg1;
+ }
+ }
+ /* All paths returned */
+ case BIG_BIG:
+ L_big_rem:
+ ires = big_ucomp(arg1, arg2);
+ if (ires == 0) {
+ arg1 = SMALL_ZERO;
+ } else if (ires > 0) {
+ Uint need = BIG_NEED_SIZE(big_size(arg1));
+ Eterm* hp = HAlloc(p, need);
+
+ arg1 = big_rem(arg1, arg2, hp);
+ if (is_nil(arg1)) {
+ erts_arith_shrink(p, hp);
+ p->freason = SYSTEM_LIMIT;
+ return THE_NON_VALUE;
+ }
+ maybe_shrink(p, hp, arg1, need);
+ }
+ return arg1;
+ default:
+ p->freason = BADARITH;
+ return THE_NON_VALUE;
+ }
+}
+
+Eterm erts_band(Process* p, Eterm arg1, Eterm arg2)
+{
+ Eterm tmp_big1[2];
+ Eterm tmp_big2[2];
+ Eterm* hp;
+ int need;
+
+ switch (NUMBER_CODE(arg1, arg2)) {
+ case SMALL_BIG:
+ arg1 = small_to_big(signed_val(arg1), tmp_big1);
+ break;
+ case BIG_SMALL:
+ arg2 = small_to_big(signed_val(arg2), tmp_big2);
+ break;
+ case BIG_BIG:
+ break;
+ default:
+ p->freason = BADARITH;
+ return THE_NON_VALUE;
+ }
+ need = BIG_NEED_SIZE(MAX(big_size(arg1), big_size(arg2)) + 1);
+ hp = HAlloc(p, need);
+ arg1 = big_band(arg1, arg2, hp);
+ ASSERT(is_not_nil(arg1));
+ maybe_shrink(p, hp, arg1, need);
+ return arg1;
+}
+
+Eterm erts_bor(Process* p, Eterm arg1, Eterm arg2)
+{
+ Eterm tmp_big1[2];
+ Eterm tmp_big2[2];
+ Eterm* hp;
+ int need;
+
+ switch (NUMBER_CODE(arg1, arg2)) {
+ case SMALL_BIG:
+ arg1 = small_to_big(signed_val(arg1), tmp_big1);
+ break;
+ case BIG_SMALL:
+ arg2 = small_to_big(signed_val(arg2), tmp_big2);
+ break;
+ case BIG_BIG:
+ break;
+ default:
+ p->freason = BADARITH;
+ return THE_NON_VALUE;
+ }
+ need = BIG_NEED_SIZE(MAX(big_size(arg1), big_size(arg2)) + 1);
+ hp = HAlloc(p, need);
+ arg1 = big_bor(arg1, arg2, hp);
+ ASSERT(is_not_nil(arg1));
+ maybe_shrink(p, hp, arg1, need);
+ return arg1;
+}
+
+Eterm erts_bxor(Process* p, Eterm arg1, Eterm arg2)
+{
+ Eterm tmp_big1[2];
+ Eterm tmp_big2[2];
+ Eterm* hp;
+ int need;
+
+ switch (NUMBER_CODE(arg1, arg2)) {
+ case SMALL_BIG:
+ arg1 = small_to_big(signed_val(arg1), tmp_big1);
+ break;
+ case BIG_SMALL:
+ arg2 = small_to_big(signed_val(arg2), tmp_big2);
+ break;
+ case BIG_BIG:
+ break;
+ default:
+ p->freason = BADARITH;
+ return THE_NON_VALUE;
+ }
+ need = BIG_NEED_SIZE(MAX(big_size(arg1), big_size(arg2)) + 1);
+ hp = HAlloc(p, need);
+ arg1 = big_bxor(arg1, arg2, hp);
+ ASSERT(is_not_nil(arg1));
+ maybe_shrink(p, hp, arg1, need);
+ return arg1;
+}
+
+Eterm erts_bnot(Process* p, Eterm arg)
+{
+ Eterm ret;
+
+ if (is_big(arg)) {
+ Uint need = BIG_NEED_SIZE(big_size(arg)+1);
+ Eterm* bigp = HAlloc(p, need);
+
+ ret = big_bnot(arg, bigp);
+ maybe_shrink(p, bigp, ret, need);
+ if (is_nil(ret)) {
+ p->freason = SYSTEM_LIMIT;
+ return NIL;
+ }
+ } else {
+ p->freason = BADARITH;
+ return NIL;
+ }
+ return ret;
+}
+
+#define ERTS_NEED_GC(p, need) ((HEAP_LIMIT((p)) - HEAP_TOP((p))) <= (need))
+
+static ERTS_INLINE void
+trim_heap(Process* p, Eterm* hp, Eterm res)
+{
+ if (is_immed(res)) {
+ ASSERT(p->heap <= hp && hp <= p->htop);
+ p->htop = hp;
+ } else {
+ Eterm* new_htop;
+ ASSERT(is_big(res));
+ new_htop = hp + bignum_header_arity(*hp) + 1;
+ ASSERT(p->heap <= new_htop && new_htop <= p->htop);
+ p->htop = new_htop;
+ }
+ ASSERT(p->heap <= p->htop && p->htop <= p->stop);
+}
+
+/*
+ * The functions that follow are called from the emulator loop.
+ * They are not allowed to allocate heap fragments, but must do
+ * a garbage collection if there is insufficient heap space.
+ */
+
+#define erts_arith_shrink horrible error
+#define maybe_shrink horrible error
+
+Eterm
+erts_gc_mixed_plus(Process* p, Eterm* reg, Uint live)
+{
+ Eterm arg1;
+ Eterm arg2;
+ Eterm tmp_big1[2];
+ Eterm tmp_big2[2];
+ Eterm res;
+ Eterm hdr;
+ FloatDef f1, f2;
+ dsize_t sz1, sz2, sz;
+ int need_heap;
+ Eterm* hp;
+ Sint ires;
+
+ arg1 = reg[live];
+ arg2 = reg[live+1];
+ ERTS_FP_CHECK_INIT(p);
+ switch (arg1 & _TAG_PRIMARY_MASK) {
+ case TAG_PRIMARY_IMMED1:
+ switch ((arg1 & _TAG_IMMED1_MASK) >> _TAG_PRIMARY_SIZE) {
+ case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE):
+ switch (arg2 & _TAG_PRIMARY_MASK) {
+ case TAG_PRIMARY_IMMED1:
+ switch ((arg2 & _TAG_IMMED1_MASK) >> _TAG_PRIMARY_SIZE) {
+ case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE):
+ ires = signed_val(arg1) + signed_val(arg2);
+ ASSERT(MY_IS_SSMALL(ires) == IS_SSMALL(ires));
+ if (MY_IS_SSMALL(ires)) {
+ return make_small(ires);
+ } else {
+ if (ERTS_NEED_GC(p, 2)) {
+ erts_garbage_collect(p, 2, reg, live);
+ }
+ hp = p->htop;
+ p->htop += 2;
+ res = small_to_big(ires, hp);
+ return res;
+ }
+ default:
+ badarith:
+ p->freason = BADARITH;
+ return THE_NON_VALUE;
+ }
+ 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):
+ if (arg1 == SMALL_ZERO) {
+ return arg2;
+ }
+ arg1 = small_to_big(signed_val(arg1), tmp_big1);
+ goto do_big;
+ case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE):
+ f1.fd = signed_val(arg1);
+ GET_DOUBLE(arg2, f2);
+ goto do_float;
+ default:
+ goto badarith;
+ }
+ }
+ default:
+ goto badarith;
+ }
+ case TAG_PRIMARY_BOXED:
+ hdr = *boxed_val(arg1);
+ switch ((hdr & _TAG_HEADER_MASK) >> _TAG_PRIMARY_SIZE) {
+ case (_TAG_HEADER_POS_BIG >> _TAG_PRIMARY_SIZE):
+ case (_TAG_HEADER_NEG_BIG >> _TAG_PRIMARY_SIZE):
+ switch (arg2 & _TAG_PRIMARY_MASK) {
+ case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE):
+ switch ((arg2 & _TAG_IMMED1_MASK) >> _TAG_PRIMARY_SIZE) {
+ case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE):
+ if (arg2 == SMALL_ZERO) {
+ return arg1;
+ }
+ arg2 = small_to_big(signed_val(arg2), tmp_big2);
+ goto do_big;
+ 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):
+ do_big:
+ sz1 = big_size(arg1);
+ sz2 = big_size(arg2);
+ sz = MAX(sz1, sz2)+1;
+ need_heap = BIG_NEED_SIZE(sz);
+ if (ERTS_NEED_GC(p, need_heap)) {
+ erts_garbage_collect(p, need_heap, reg, live+2);
+ if (arg1 != make_big(tmp_big1)) {
+ arg1 = reg[live];
+ }
+ if (arg2 != make_big(tmp_big2)) {
+ arg2 = reg[live+1];
+ }
+ }
+ hp = p->htop;
+ p->htop += need_heap;
+ res = big_plus(arg1, arg2, hp);
+ trim_heap(p, hp, res);
+ if (is_nil(res)) {
+ p->freason = SYSTEM_LIMIT;
+ return THE_NON_VALUE;
+ }
+ return res;
+ case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE):
+ if (big_to_double(arg1, &f1.fd) < 0) {
+ goto badarith;
+ }
+ GET_DOUBLE(arg2, f2);
+ goto do_float;
+ default:
+ goto badarith;
+ }
+ }
+ case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE):
+ switch (arg2 & _TAG_PRIMARY_MASK) {
+ case TAG_PRIMARY_IMMED1:
+ switch ((arg2 & _TAG_IMMED1_MASK) >> _TAG_PRIMARY_SIZE) {
+ case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE):
+ GET_DOUBLE(arg1, f1);
+ f2.fd = signed_val(arg2);
+ goto do_float;
+ 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):
+ GET_DOUBLE(arg1, f1);
+ if (big_to_double(arg2, &f2.fd) < 0) {
+ goto badarith;
+ }
+ goto do_float;
+ case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE):
+ GET_DOUBLE(arg1, f1);
+ GET_DOUBLE(arg2, f2);
+
+ do_float:
+ f1.fd = f1.fd + f2.fd;
+ ERTS_FP_ERROR(p, f1.fd, goto badarith);
+ if (ERTS_NEED_GC(p, FLOAT_SIZE_OBJECT)) {
+ erts_garbage_collect(p, FLOAT_SIZE_OBJECT, reg, live);
+ }
+ hp = p->htop;
+ p->htop += FLOAT_SIZE_OBJECT;
+ res = make_float(hp);
+ PUT_DOUBLE(f1, hp);
+ return res;
+ default:
+ goto badarith;
+ }
+ default:
+ goto badarith;
+ }
+ }
+ default:
+ goto badarith;
+ }
+}
+
+Eterm
+erts_gc_mixed_minus(Process* p, Eterm* reg, Uint live)
+{
+ Eterm arg1;
+ Eterm arg2;
+ Eterm tmp_big1[2];
+ Eterm tmp_big2[2];
+ Eterm hdr;
+ Eterm res;
+ FloatDef f1, f2;
+ dsize_t sz1, sz2, sz;
+ int need_heap;
+ Eterm* hp;
+ Sint ires;
+
+ arg1 = reg[live];
+ arg2 = reg[live+1];
+ ERTS_FP_CHECK_INIT(p);
+ switch (arg1 & _TAG_PRIMARY_MASK) {
+ case TAG_PRIMARY_IMMED1:
+ switch ((arg1 & _TAG_IMMED1_MASK) >> _TAG_PRIMARY_SIZE) {
+ case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE):
+ switch (arg2 & _TAG_PRIMARY_MASK) {
+ case TAG_PRIMARY_IMMED1:
+ switch ((arg2 & _TAG_IMMED1_MASK) >> _TAG_PRIMARY_SIZE) {
+ case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE):
+ ires = signed_val(arg1) - signed_val(arg2);
+ ASSERT(MY_IS_SSMALL(ires) == IS_SSMALL(ires));
+ if (MY_IS_SSMALL(ires)) {
+ return make_small(ires);
+ } else {
+ if (ERTS_NEED_GC(p, 2)) {
+ erts_garbage_collect(p, 2, reg, live);
+ }
+ hp = p->htop;
+ p->htop += 2;
+ res = small_to_big(ires, hp);
+ return res;
+ }
+ default:
+ badarith:
+ p->freason = BADARITH;
+ return THE_NON_VALUE;
+ }
+ 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):
+ arg1 = small_to_big(signed_val(arg1), tmp_big1);
+ goto do_big;
+ case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE):
+ f1.fd = signed_val(arg1);
+ GET_DOUBLE(arg2, f2);
+ goto do_float;
+ default:
+ goto badarith;
+ }
+ }
+ default:
+ goto badarith;
+ }
+ case TAG_PRIMARY_BOXED:
+ hdr = *boxed_val(arg1);
+ switch ((hdr & _TAG_HEADER_MASK) >> _TAG_PRIMARY_SIZE) {
+ case (_TAG_HEADER_POS_BIG >> _TAG_PRIMARY_SIZE):
+ case (_TAG_HEADER_NEG_BIG >> _TAG_PRIMARY_SIZE):
+ switch (arg2 & _TAG_PRIMARY_MASK) {
+ case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE):
+ switch ((arg2 & _TAG_IMMED1_MASK) >> _TAG_PRIMARY_SIZE) {
+ case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE):
+ if (arg2 == SMALL_ZERO) {
+ return arg1;
+ }
+ arg2 = small_to_big(signed_val(arg2), tmp_big2);
+
+ do_big:
+ sz1 = big_size(arg1);
+ sz2 = big_size(arg2);
+ sz = MAX(sz1, sz2)+1;
+ need_heap = BIG_NEED_SIZE(sz);
+ if (ERTS_NEED_GC(p, need_heap)) {
+ erts_garbage_collect(p, need_heap, reg, live+2);
+ if (arg1 != make_big(tmp_big1)) {
+ arg1 = reg[live];
+ }
+ if (arg2 != make_big(tmp_big2)) {
+ arg2 = reg[live+1];
+ }
+ }
+ hp = p->htop;
+ p->htop += need_heap;
+ res = big_minus(arg1, arg2, hp);
+ trim_heap(p, hp, res);
+ 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):
+ goto do_big;
+ case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE):
+ if (big_to_double(arg1, &f1.fd) < 0) {
+ goto badarith;
+ }
+ GET_DOUBLE(arg2, f2);
+ goto do_float;
+ default:
+ goto badarith;
+ }
+ }
+ case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE):
+ switch (arg2 & _TAG_PRIMARY_MASK) {
+ case TAG_PRIMARY_IMMED1:
+ switch ((arg2 & _TAG_IMMED1_MASK) >> _TAG_PRIMARY_SIZE) {
+ case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE):
+ GET_DOUBLE(arg1, f1);
+ f2.fd = signed_val(arg2);
+ goto do_float;
+ 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):
+ GET_DOUBLE(arg1, f1);
+ if (big_to_double(arg2, &f2.fd) < 0) {
+ goto badarith;
+ }
+ goto do_float;
+ case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE):
+ GET_DOUBLE(arg1, f1);
+ GET_DOUBLE(arg2, f2);
+
+ do_float:
+ f1.fd = f1.fd - f2.fd;
+ ERTS_FP_ERROR(p, f1.fd, goto badarith);
+ if (ERTS_NEED_GC(p, FLOAT_SIZE_OBJECT)) {
+ erts_garbage_collect(p, FLOAT_SIZE_OBJECT, reg, live);
+ }
+ hp = p->htop;
+ p->htop += FLOAT_SIZE_OBJECT;
+ res = make_float(hp);
+ PUT_DOUBLE(f1, hp);
+ return res;
+ default:
+ goto badarith;
+ }
+ default:
+ goto badarith;
+ }
+ }
+ default:
+ goto badarith;
+ }
+}
+
+Eterm
+erts_gc_mixed_times(Process* p, Eterm* reg, Uint live)
+{
+ Eterm arg1;
+ Eterm arg2;
+ Eterm tmp_big1[2];
+ Eterm tmp_big2[2];
+ Eterm hdr;
+ Eterm res;
+ FloatDef f1, f2;
+ dsize_t sz1, sz2, sz;
+ int need_heap;
+ Eterm* hp;
+
+ arg1 = reg[live];
+ arg2 = reg[live+1];
+ ERTS_FP_CHECK_INIT(p);
+ switch (arg1 & _TAG_PRIMARY_MASK) {
+ case TAG_PRIMARY_IMMED1:
+ switch ((arg1 & _TAG_IMMED1_MASK) >> _TAG_PRIMARY_SIZE) {
+ case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE):
+ switch (arg2 & _TAG_PRIMARY_MASK) {
+ case TAG_PRIMARY_IMMED1:
+ switch ((arg2 & _TAG_IMMED1_MASK) >> _TAG_PRIMARY_SIZE) {
+ case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE):
+ if ((arg1 == SMALL_ZERO) || (arg2 == SMALL_ZERO)) {
+ return(SMALL_ZERO);
+ } else if (arg1 == SMALL_ONE) {
+ return(arg2);
+ } else if (arg2 == SMALL_ONE) {
+ return(arg1);
+ } else {
+ Eterm big_res[3];
+
+ /*
+ * The following code is optimized for the case that
+ * result is small (which should be the most common case
+ * in practice).
+ */
+ res = small_times(signed_val(arg1), signed_val(arg2),
+ big_res);
+ if (is_small(res)) {
+ return res;
+ } else {
+ /*
+ * The result is a a big number.
+ * Allocate a heap fragment and copy the result.
+ * Be careful to allocate exactly what we need
+ * to not leave any holes.
+ */
+ Uint arity;
+ Uint need;
+
+ ASSERT(is_big(res));
+ hdr = big_res[0];
+ arity = bignum_header_arity(hdr);
+ ASSERT(arity == 1 || arity == 2);
+ need = arity + 1;
+ if (ERTS_NEED_GC(p, need)) {
+ erts_garbage_collect(p, need, reg, live);
+ }
+ hp = p->htop;
+ p->htop += need;
+ res = make_big(hp);
+ *hp++ = hdr;
+ *hp++ = big_res[1];
+ if (arity > 1) {
+ *hp = big_res[2];
+ }
+ return res;
+ }
+ }
+ default:
+ badarith:
+ p->freason = BADARITH;
+ return THE_NON_VALUE;
+ }
+ 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):
+ if (arg1 == SMALL_ZERO)
+ 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;
+ case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE):
+ f1.fd = signed_val(arg1);
+ GET_DOUBLE(arg2, f2);
+ goto do_float;
+ default:
+ goto badarith;
+ }
+ }
+ default:
+ goto badarith;
+ }
+ case TAG_PRIMARY_BOXED:
+ hdr = *boxed_val(arg1);
+ switch ((hdr & _TAG_HEADER_MASK) >> _TAG_PRIMARY_SIZE) {
+ case (_TAG_HEADER_POS_BIG >> _TAG_PRIMARY_SIZE):
+ case (_TAG_HEADER_NEG_BIG >> _TAG_PRIMARY_SIZE):
+ switch (arg2 & _TAG_PRIMARY_MASK) {
+ case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE):
+ switch ((arg2 & _TAG_IMMED1_MASK) >> _TAG_PRIMARY_SIZE) {
+ case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE):
+ if (arg2 == SMALL_ZERO)
+ 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;
+ 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 = sz1 + sz2;
+
+ do_big:
+ need_heap = BIG_NEED_SIZE(sz);
+ if (ERTS_NEED_GC(p, need_heap)) {
+ erts_garbage_collect(p, need_heap, reg, live+2);
+ if (arg1 != make_big(tmp_big1)) {
+ arg1 = reg[live];
+ }
+ if (arg2 != make_big(tmp_big2)) {
+ arg2 = reg[live+1];
+ }
+ }
+ hp = p->htop;
+ p->htop += need_heap;
+ res = big_times(arg1, arg2, hp);
+ trim_heap(p, hp, res);
+
+ /*
+ * 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.
+ */
+
+ if (is_nil(res)) {
+ p->freason = SYSTEM_LIMIT;
+ return THE_NON_VALUE;
+ }
+ return res;
+ case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE):
+ if (big_to_double(arg1, &f1.fd) < 0) {
+ goto badarith;
+ }
+ GET_DOUBLE(arg2, f2);
+ goto do_float;
+ default:
+ goto badarith;
+ }
+ }
+ case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE):
+ switch (arg2 & _TAG_PRIMARY_MASK) {
+ case TAG_PRIMARY_IMMED1:
+ switch ((arg2 & _TAG_IMMED1_MASK) >> _TAG_PRIMARY_SIZE) {
+ case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE):
+ GET_DOUBLE(arg1, f1);
+ f2.fd = signed_val(arg2);
+ goto do_float;
+ 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):
+ GET_DOUBLE(arg1, f1);
+ if (big_to_double(arg2, &f2.fd) < 0) {
+ goto badarith;
+ }
+ goto do_float;
+ case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE):
+ GET_DOUBLE(arg1, f1);
+ GET_DOUBLE(arg2, f2);
+
+ do_float:
+ f1.fd = f1.fd * f2.fd;
+ ERTS_FP_ERROR(p, f1.fd, goto badarith);
+ if (ERTS_NEED_GC(p, FLOAT_SIZE_OBJECT)) {
+ erts_garbage_collect(p, FLOAT_SIZE_OBJECT, reg, live);
+ }
+ hp = p->htop;
+ p->htop += FLOAT_SIZE_OBJECT;
+ res = make_float(hp);
+ PUT_DOUBLE(f1, hp);
+ return res;
+ default:
+ goto badarith;
+ }
+ default:
+ goto badarith;
+ }
+ }
+ default:
+ goto badarith;
+ }
+}
+
+Eterm
+erts_gc_mixed_div(Process* p, Eterm* reg, Uint live)
+{
+ Eterm arg1;
+ Eterm arg2;
+ FloatDef f1, f2;
+ Eterm* hp;
+ Eterm hdr;
+
+ arg1 = reg[live];
+ arg2 = reg[live+1];
+ ERTS_FP_CHECK_INIT(p);
+ switch (arg1 & _TAG_PRIMARY_MASK) {
+ case TAG_PRIMARY_IMMED1:
+ switch ((arg1 & _TAG_IMMED1_MASK) >> _TAG_PRIMARY_SIZE) {
+ case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE):
+ switch (arg2 & _TAG_PRIMARY_MASK) {
+ case TAG_PRIMARY_IMMED1:
+ switch ((arg2 & _TAG_IMMED1_MASK) >> _TAG_PRIMARY_SIZE) {
+ case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE):
+ f1.fd = signed_val(arg1);
+ f2.fd = signed_val(arg2);
+ goto do_float;
+ default:
+ badarith:
+ p->freason = BADARITH;
+ return THE_NON_VALUE;
+ }
+ 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):
+ f1.fd = signed_val(arg1);
+ if (big_to_double(arg2, &f2.fd) < 0) {
+ goto badarith;
+ }
+ goto do_float;
+ case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE):
+ f1.fd = signed_val(arg1);
+ GET_DOUBLE(arg2, f2);
+ goto do_float;
+ default:
+ goto badarith;
+ }
+ }
+ default:
+ goto badarith;
+ }
+ case TAG_PRIMARY_BOXED:
+ hdr = *boxed_val(arg1);
+ switch ((hdr & _TAG_HEADER_MASK) >> _TAG_PRIMARY_SIZE) {
+ case (_TAG_HEADER_POS_BIG >> _TAG_PRIMARY_SIZE):
+ case (_TAG_HEADER_NEG_BIG >> _TAG_PRIMARY_SIZE):
+ switch (arg2 & _TAG_PRIMARY_MASK) {
+ case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE):
+ switch ((arg2 & _TAG_IMMED1_MASK) >> _TAG_PRIMARY_SIZE) {
+ case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE):
+ if (big_to_double(arg1, &f1.fd) < 0) {
+ goto badarith;
+ }
+ f2.fd = signed_val(arg2);
+ goto do_float;
+ 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):
+ if (big_to_double(arg1, &f1.fd) < 0 ||
+ big_to_double(arg2, &f2.fd) < 0) {
+ goto badarith;
+ }
+ goto do_float;
+ case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE):
+ if (big_to_double(arg1, &f1.fd) < 0) {
+ goto badarith;
+ }
+ GET_DOUBLE(arg2, f2);
+ goto do_float;
+ default:
+ goto badarith;
+ }
+ }
+ case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE):
+ switch (arg2 & _TAG_PRIMARY_MASK) {
+ case TAG_PRIMARY_IMMED1:
+ switch ((arg2 & _TAG_IMMED1_MASK) >> _TAG_PRIMARY_SIZE) {
+ case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE):
+ GET_DOUBLE(arg1, f1);
+ f2.fd = signed_val(arg2);
+ goto do_float;
+ 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):
+ GET_DOUBLE(arg1, f1);
+ if (big_to_double(arg2, &f2.fd) < 0) {
+ goto badarith;
+ }
+ goto do_float;
+ case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE):
+ GET_DOUBLE(arg1, f1);
+ GET_DOUBLE(arg2, f2);
+
+ do_float:
+ f1.fd = f1.fd / f2.fd;
+ ERTS_FP_ERROR(p, f1.fd, goto badarith);
+ if (ERTS_NEED_GC(p, FLOAT_SIZE_OBJECT)) {
+ erts_garbage_collect(p, FLOAT_SIZE_OBJECT, reg, live);
+ }
+ hp = p->htop;
+ p->htop += FLOAT_SIZE_OBJECT;
+ PUT_DOUBLE(f1, hp);
+ return make_float(hp);
+ default:
+ goto badarith;
+ }
+ default:
+ goto badarith;
+ }
+ }
+ default:
+ goto badarith;
+ }
+}
+
+Eterm
+erts_gc_int_div(Process* p, Eterm* reg, Uint live)
+{
+ Eterm arg1;
+ Eterm arg2;
+ Eterm tmp_big1[2];
+ Eterm tmp_big2[2];
+ int ires;
+
+ arg1 = reg[live];
+ arg2 = reg[live+1];
+ switch (NUMBER_CODE(arg1, arg2)) {
+ case SMALL_SMALL:
+ /* This case occurs if the most negative fixnum is divided by -1. */
+ ASSERT(arg2 == make_small(-1));
+ arg1 = small_to_big(signed_val(arg1), tmp_big1);
+ /*FALLTHROUGH*/
+ case BIG_SMALL:
+ arg2 = small_to_big(signed_val(arg2), tmp_big2);
+ goto L_big_div;
+ case SMALL_BIG:
+ if (arg1 != make_small(MIN_SMALL)) {
+ return SMALL_ZERO;
+ }
+ arg1 = small_to_big(signed_val(arg1), tmp_big1);
+ /*FALLTHROUGH*/
+ case BIG_BIG:
+ L_big_div:
+ ires = big_ucomp(arg1, arg2);
+ if (ires < 0) {
+ arg1 = SMALL_ZERO;
+ } else if (ires == 0) {
+ arg1 = (big_sign(arg1) == big_sign(arg2)) ?
+ SMALL_ONE : SMALL_MINUS_ONE;
+ } else {
+ Eterm* hp;
+ int i = big_size(arg1);
+ Uint need;
+
+ ires = big_size(arg2);
+ need = BIG_NEED_SIZE(i-ires+1) + BIG_NEED_SIZE(i);
+ if (ERTS_NEED_GC(p, need)) {
+ erts_garbage_collect(p, need, reg, live+2);
+ if (arg1 != make_big(tmp_big1)) {
+ arg1 = reg[live];
+ }
+ if (arg2 != make_big(tmp_big2)) {
+ arg2 = reg[live+1];
+ }
+ }
+ hp = p->htop;
+ p->htop += need;
+ arg1 = big_div(arg1, arg2, hp);
+ trim_heap(p, hp, arg1);
+ if (is_nil(arg1)) {
+ p->freason = SYSTEM_LIMIT;
+ return THE_NON_VALUE;
+ }
+ }
+ return arg1;
+ default:
+ p->freason = BADARITH;
+ return THE_NON_VALUE;
+ }
+}
+
+Eterm
+erts_gc_int_rem(Process* p, Eterm* reg, Uint live)
+{
+ Eterm arg1;
+ Eterm arg2;
+ Eterm tmp_big1[2];
+ Eterm tmp_big2[2];
+ int ires;
+
+ arg1 = reg[live];
+ arg2 = reg[live+1];
+ switch (NUMBER_CODE(arg1, arg2)) {
+ case BIG_SMALL:
+ arg2 = small_to_big(signed_val(arg2), tmp_big2);
+ goto L_big_rem;
+ case SMALL_BIG:
+ if (arg1 != make_small(MIN_SMALL)) {
+ return arg1;
+ } else {
+ Eterm tmp = small_to_big(signed_val(arg1), tmp_big1);
+ if ((ires = big_ucomp(tmp, arg2)) == 0) {
+ return SMALL_ZERO;
+ } else {
+ ASSERT(ires < 0);
+ return arg1;
+ }
+ }
+ /* All paths returned */
+ case BIG_BIG:
+ L_big_rem:
+ ires = big_ucomp(arg1, arg2);
+ if (ires == 0) {
+ arg1 = SMALL_ZERO;
+ } else if (ires > 0) {
+ Eterm* hp;
+ Uint need = BIG_NEED_SIZE(big_size(arg1));
+
+ if (ERTS_NEED_GC(p, need)) {
+ erts_garbage_collect(p, need, reg, live+2);
+ if (arg1 != make_big(tmp_big1)) {
+ arg1 = reg[live];
+ }
+ if (arg2 != make_big(tmp_big2)) {
+ arg2 = reg[live+1];
+ }
+ }
+ hp = p->htop;
+ p->htop += need;
+ arg1 = big_rem(arg1, arg2, hp);
+ trim_heap(p, hp, arg1);
+ if (is_nil(arg1)) {
+ p->freason = SYSTEM_LIMIT;
+ return THE_NON_VALUE;
+ }
+ }
+ return arg1;
+ default:
+ p->freason = BADARITH;
+ return THE_NON_VALUE;
+ }
+}
+
+#define DEFINE_GC_LOGIC_FUNC(func) \
+Eterm erts_gc_##func(Process* p, Eterm* reg, Uint live) \
+{ \
+ Eterm arg1; \
+ Eterm arg2; \
+ Eterm tmp_big1[2]; \
+ Eterm tmp_big2[2]; \
+ Eterm* hp; \
+ int need; \
+ \
+ arg1 = reg[live]; \
+ arg2 = reg[live+1]; \
+ switch (NUMBER_CODE(arg1, arg2)) { \
+ case SMALL_BIG: \
+ arg1 = small_to_big(signed_val(arg1), tmp_big1); \
+ need = BIG_NEED_SIZE(big_size(arg2) + 1); \
+ if (ERTS_NEED_GC(p, need)) { \
+ erts_garbage_collect(p, need, reg, live+2); \
+ arg2 = reg[live+1]; \
+ } \
+ break; \
+ case BIG_SMALL: \
+ arg2 = small_to_big(signed_val(arg2), tmp_big2); \
+ need = BIG_NEED_SIZE(big_size(arg1) + 1); \
+ if (ERTS_NEED_GC(p, need)) { \
+ erts_garbage_collect(p, need, reg, live+2); \
+ arg1 = reg[live]; \
+ } \
+ break; \
+ case BIG_BIG: \
+ need = BIG_NEED_SIZE(MAX(big_size(arg1), big_size(arg2)) + 1); \
+ if (ERTS_NEED_GC(p, need)) { \
+ erts_garbage_collect(p, need, reg, live+2); \
+ arg1 = reg[live]; \
+ arg2 = reg[live+1]; \
+ } \
+ break; \
+ default: \
+ p->freason = BADARITH; \
+ return THE_NON_VALUE; \
+ } \
+ hp = p->htop; \
+ p->htop += need; \
+ arg1 = big_##func(arg1, arg2, hp); \
+ trim_heap(p, hp, arg1); \
+ return arg1; \
+}
+
+DEFINE_GC_LOGIC_FUNC(band)
+DEFINE_GC_LOGIC_FUNC(bor)
+DEFINE_GC_LOGIC_FUNC(bxor)
+
+Eterm erts_gc_bnot(Process* p, Eterm* reg, Uint live)
+{
+ Eterm result;
+ Eterm arg;
+ Uint need;
+ Eterm* bigp;
+
+ arg = reg[live];
+ if (is_not_big(arg)) {
+ p->freason = BADARITH;
+ return NIL;
+ } else {
+ need = BIG_NEED_SIZE(big_size(arg)+1);
+ if (ERTS_NEED_GC(p, need)) {
+ erts_garbage_collect(p, need, reg, live+1);
+ arg = reg[live];
+ }
+ bigp = p->htop;
+ p->htop += need;
+ result = big_bnot(arg, bigp);
+ trim_heap(p, bigp, result);
+ if (is_nil(result)) {
+ p->freason = SYSTEM_LIMIT;
+ return NIL;
+ }
+ }
+ return result;
+}