aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator/beam
diff options
context:
space:
mode:
Diffstat (limited to 'erts/emulator/beam')
-rw-r--r--erts/emulator/beam/beam_emu.c46
-rw-r--r--erts/emulator/beam/beam_load.c46
-rw-r--r--erts/emulator/beam/ops.tab20
3 files changed, 111 insertions, 1 deletions
diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c
index afba17f7bd..741ba8fb93 100644
--- a/erts/emulator/beam/beam_emu.c
+++ b/erts/emulator/beam/beam_emu.c
@@ -1276,6 +1276,52 @@ void process_main(void)
#define STORE_ARITH_RESULT(res) StoreBifResult(2, (res));
#define ARITH_FUNC(name) erts_gc_##name
+ {
+ Eterm increment_reg_val;
+ Eterm increment_val;
+ Uint live;
+ Eterm result;
+
+ OpCase(i_increment_yIId):
+ increment_reg_val = yb(Arg(0));
+ goto do_increment;
+
+ OpCase(i_increment_xIId):
+ increment_reg_val = xb(Arg(0));
+ goto do_increment;
+
+ OpCase(i_increment_rIId):
+ increment_reg_val = r(0);
+ I--;
+
+ do_increment:
+ increment_val = Arg(1);
+ if (is_small(increment_reg_val)) {
+ Sint i = signed_val(increment_reg_val) + increment_val;
+ ASSERT(MY_IS_SSMALL(i) == IS_SSMALL(i));
+ if (MY_IS_SSMALL(i)) {
+ result = make_small(i);
+ store_result:
+ StoreBifResult(3, result);
+ }
+ }
+
+ live = Arg(2);
+ SWAPOUT;
+ reg[0] = r(0);
+ reg[live] = increment_reg_val;
+ reg[live+1] = make_small(increment_val);
+ result = erts_gc_mixed_plus(c_p, reg, live);
+ r(0) = reg[0];
+ SWAPIN;
+ ERTS_HOLE_CHECK(c_p);
+ if (is_value(result)) {
+ goto store_result;
+ }
+ ASSERT(c_p->freason != BADMATCH || is_value(c_p->fvalue));
+ goto find_func_info;
+ }
+
OpCase(i_plus_jId):
{
Eterm result;
diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c
index 2d57c8a218..e6448931eb 100644
--- a/erts/emulator/beam/beam_load.c
+++ b/erts/emulator/beam/beam_load.c
@@ -2787,6 +2787,52 @@ gen_skip_bits2(LoaderState* stp, GenOpArg Fail, GenOpArg Ms,
return op;
}
+static GenOp*
+gen_increment(LoaderState* stp, GenOpArg Reg, GenOpArg Integer,
+ GenOpArg Live, GenOpArg Dst)
+{
+ GenOp* op;
+
+ NEW_GENOP(stp, op);
+ op->op = genop_i_increment_4;
+ op->arity = 4;
+ op->next = NULL;
+ op->a[0] = Reg;
+ op->a[1].type = TAG_u;
+ op->a[1].val = Integer.val;
+ op->a[2] = Live;
+ op->a[3] = Dst;
+ return op;
+}
+
+static GenOp*
+gen_increment_from_minus(LoaderState* stp, GenOpArg Reg, GenOpArg Integer,
+ GenOpArg Live, GenOpArg Dst)
+{
+ GenOp* op;
+
+ NEW_GENOP(stp, op);
+ op->op = genop_i_increment_4;
+ op->arity = 4;
+ op->next = NULL;
+ op->a[0] = Reg;
+ op->a[1].type = TAG_u;
+ op->a[1].val = -Integer.val;
+ op->a[2] = Live;
+ op->a[3] = Dst;
+ return op;
+}
+
+/*
+ * Test whether the negation of the given number is small.
+ */
+static int
+negation_is_small(LoaderState* stp, GenOpArg Int)
+{
+ return Int.type == TAG_i && IS_SSMALL(-Int.val);
+}
+
+
static int
smp(LoaderState* stp)
{
diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab
index 6db55e1402..029d7b512c 100644
--- a/erts/emulator/beam/ops.tab
+++ b/erts/emulator/beam/ops.tab
@@ -1371,7 +1371,21 @@ apply I
apply_last I P
#
-# New GCing arithmetic instructions.
+# Optimize addition and subtraction of small literals using
+# the i_increment/4 instruction (in bodies, not in guards).
+#
+
+gc_bif2 p Live u$bif:erlang:splus/2 Int=i Reg=d Dst => \
+ gen_increment(Reg, Int, Live, Dst)
+gc_bif2 p Live u$bif:erlang:splus/2 Reg=d Int=i Dst => \
+ gen_increment(Reg, Int, Live, Dst)
+
+gc_bif2 p Live u$bif:erlang:sminus/2 Reg=d Int=i Dst | \
+ negation_is_small(Int) => \
+ gen_increment_from_minus(Reg, Int, Live, Dst)
+
+#
+# GCing arithmetic instructions.
#
gc_bif2 Fail I u$bif:erlang:splus/2 S1 S2 Dst=d => i_fetch S1 S2 | i_plus Fail I Dst
@@ -1394,6 +1408,10 @@ gc_bif1 Fail I u$bif:erlang:bnot/1 Src Dst=d => i_int_bnot Fail Src I Dst
gc_bif1 Fail I u$bif:erlang:sminus/1 Src Dst=d => i_fetch i Src | i_minus Fail I Dst
gc_bif1 Fail I u$bif:erlang:splus/1 Src Dst=d => i_fetch i Src | i_plus Fail I Dst
+i_increment r I I d
+i_increment x I I d
+i_increment y I I d
+
i_plus j I d
i_minus j I d
i_times j I d