aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBjörn Gustavsson <[email protected]>2016-04-05 12:56:57 +0200
committerBjörn Gustavsson <[email protected]>2016-04-07 15:28:29 +0200
commit937f527054f13dd524588c064cd5d76e3cfd23eb (patch)
tree8fe284d496399fb4b0a3e07286b101517dbf4f6a
parente93a66110aa27a5b8228fb46a3459a6de0e626d0 (diff)
downloadotp-937f527054f13dd524588c064cd5d76e3cfd23eb.tar.gz
otp-937f527054f13dd524588c064cd5d76e3cfd23eb.tar.bz2
otp-937f527054f13dd524588c064cd5d76e3cfd23eb.zip
Avoid rebuilding unchanged instructions
In transformations such as: move S X0=x==0 | line Loc | call_ext Ar Func => \ line Loc | move S X0 | call_ext Ar Func we can avoid rebuilding the last instruction in the sequence by introducing a 'keep' instruction. Currently, there are only 13 transformations that are hit by this optimization, but most of them are frequently used.
-rw-r--r--erts/emulator/beam/beam_load.c11
-rwxr-xr-xerts/emulator/utils/beam_makeops41
2 files changed, 51 insertions, 1 deletions
diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c
index ad174664ae..bdb451a6fe 100644
--- a/erts/emulator/beam/beam_load.c
+++ b/erts/emulator/beam/beam_load.c
@@ -5028,7 +5028,16 @@ transform_engine(LoaderState* st)
instr = 0;
#endif
break;
-
+#if defined(TOP_keep)
+ case TOP_keep:
+ /* Keep the current instruction unchanged. */
+ keep = instr;
+ st->genop = instr;
+#ifdef DEBUG
+ instr = 0;
+#endif
+ break;
+#endif
#if defined(TOP_call_end)
case TOP_call_end:
{
diff --git a/erts/emulator/utils/beam_makeops b/erts/emulator/utils/beam_makeops
index 3d4213d55d..66ffd83ee9 100755
--- a/erts/emulator/utils/beam_makeops
+++ b/erts/emulator/utils/beam_makeops
@@ -1718,6 +1718,7 @@ sub tr_gen_to {
push(@code, make_op('', 'end'))
unless is_instr($code[$#code], 'call_end');
+ tr_maybe_keep(\@code);
tr_maybe_rename(\@code);
#
@@ -1745,6 +1746,46 @@ sub tr_gen_to {
push(@{$gen_transform{$key}}, @code),
}
+sub tr_maybe_keep {
+ my($ref) = @_;
+ my @last_instr;
+ my $pos;
+ my $reused_instr;
+
+ for (my $i = 0; $i < @$ref; $i++) {
+ my $instr = $$ref[$i];
+ my($size, $instr_ref, $comment) = @$instr;
+ my($op, @args) = @$instr_ref;
+ if ($op eq 'next_instr') {
+ @last_instr = ($args[0]);
+ } elsif ($op eq 'set_var_next_arg') {
+ push @last_instr, $args[0];
+ } elsif ($op eq 'next_arg') {
+ push @last_instr, 'ignored';
+ } elsif ($op eq 'new_instr') {
+ unless (defined $pos) {
+ # 'new_instr' immediately after 'commit'.
+ $reused_instr = $args[0];
+ return unless shift(@last_instr) == $reused_instr;
+ $pos = $i - 1;
+ } else {
+ # Second 'new_instr' after 'commit'. The instructions
+ # from $pos up to and including $i - 1 rebuilds the
+ # existing instruction exactly.
+ my $name = $gen_opname[$reused_instr];
+ my $arity = $gen_arity[$reused_instr];
+ my $reuse = make_op("$name/$arity", 'keep');
+ splice @$ref, $pos, $i-$pos, ($reuse);
+ return;
+ }
+ } elsif ($op eq 'store_var_next_arg') {
+ return unless shift(@last_instr) eq $args[0];
+ } elsif (defined $pos) {
+ return;
+ }
+ }
+}
+
sub tr_maybe_rename {
my($ref) = @_;
my $s = 'left';