diff options
author | Björn Gustavsson <[email protected]> | 2016-04-05 12:56:57 +0200 |
---|---|---|
committer | Björn Gustavsson <[email protected]> | 2016-04-07 15:28:29 +0200 |
commit | 937f527054f13dd524588c064cd5d76e3cfd23eb (patch) | |
tree | 8fe284d496399fb4b0a3e07286b101517dbf4f6a | |
parent | e93a66110aa27a5b8228fb46a3459a6de0e626d0 (diff) | |
download | otp-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.c | 11 | ||||
-rwxr-xr-x | erts/emulator/utils/beam_makeops | 41 |
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'; |