aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator
diff options
context:
space:
mode:
authorBjörn Gustavsson <[email protected]>2012-05-23 16:08:59 +0200
committerBjörn-Egil Dahlberg <[email protected]>2014-01-28 15:56:24 +0100
commit331bb6cab54e6697e12cc9c5a4ca0ae618c37dd3 (patch)
tree674772a9bc325fc962d3e16c914356e5c06e5fd2 /erts/emulator
parent3c112fbde20a21db0cafc140aa72346f60315b90 (diff)
downloadotp-331bb6cab54e6697e12cc9c5a4ca0ae618c37dd3.tar.gz
otp-331bb6cab54e6697e12cc9c5a4ca0ae618c37dd3.tar.bz2
otp-331bb6cab54e6697e12cc9c5a4ca0ae618c37dd3.zip
BEAM loader: Support preservation of extra operand in transforms
It was not possible to preserve extra arguments in transformations. The following (hypothetical) example will now work: some_op Lit=c SizeArg Rest=* => move Lit x | some_op x SizeArg Rest
Diffstat (limited to 'erts/emulator')
-rw-r--r--erts/emulator/beam/beam_load.c18
-rwxr-xr-xerts/emulator/utils/beam_makeops23
2 files changed, 32 insertions, 9 deletions
diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c
index 938fd8f2c9..58207ec75b 100644
--- a/erts/emulator/beam/beam_load.c
+++ b/erts/emulator/beam/beam_load.c
@@ -4376,6 +4376,7 @@ transform_engine(LoaderState* st)
Uint* restart; /* Where to restart if current match fails. */
GenOpArg def_vars[TE_MAX_VARS]; /* Default buffer for variables. */
GenOpArg* var = def_vars;
+ int num_vars = 0;
int i; /* General index. */
Uint mask;
GenOp* instr;
@@ -4578,9 +4579,9 @@ transform_engine(LoaderState* st)
{
int n = *pc++;
int formal_arity = gen_opc[instr->op].arity;
- int num_vars = n + (instr->arity - formal_arity);
int j = formal_arity;
+ num_vars = n + (instr->arity - formal_arity);
var = erts_alloc(ERTS_ALC_T_LOADER_TMP,
num_vars * sizeof(GenOpArg));
for (i = 0; i < n; i++) {
@@ -4592,7 +4593,6 @@ transform_engine(LoaderState* st)
}
break;
#endif
-
case TOP_next_arg:
ap++;
break;
@@ -4680,6 +4680,20 @@ transform_engine(LoaderState* st)
instr->a[ap].val = var[i].val;
ap++;
break;
+#if defined(TOP_store_rest_args)
+ case TOP_store_rest_args:
+ {
+ int n = *pc++;
+ int num_extra = num_vars - n;
+
+ ASSERT(n <= num_vars);
+ GENOP_ARITY(instr, instr->arity+num_extra);
+ memcpy(instr->a, instr->def_args, ap*sizeof(GenOpArg));
+ memcpy(instr->a+ap, var+n, num_extra*sizeof(GenOpArg));
+ ap += num_extra;
+ }
+ break;
+#endif
case TOP_try_me_else:
restart = pc + 1;
restart += *pc++;
diff --git a/erts/emulator/utils/beam_makeops b/erts/emulator/utils/beam_makeops
index 16a949c2a6..0b7c16f606 100755
--- a/erts/emulator/utils/beam_makeops
+++ b/erts/emulator/utils/beam_makeops
@@ -1202,6 +1202,7 @@ sub parse_transformation {
my($from, $to) = split(/\s*=>\s*/);
my(@op);
+ my $rest_var;
# The source instructions.
@@ -1212,7 +1213,7 @@ sub parse_transformation {
$_ = (&compile_transform_function($name, split(/\s*,\s*/, $arglist)));
} else {
(@op) = split;
- $_ = &compile_transform(1, @op);
+ ($rest_var,$_) = compile_transform(1, $rest_var, @op);
}
}
@@ -1230,7 +1231,7 @@ sub parse_transformation {
@to = split(/\s*\|\s*/, $to);
foreach (@to) {
(@op) = split;
- $_ = &compile_transform(0, @op);
+ (undef,$_) = compile_transform(0, $rest_var, @op);
}
}
push(@transformations, [$., $orig, [@from], [reverse @to]]);
@@ -1243,12 +1244,18 @@ sub compile_transform_function {
}
sub compile_transform {
- my($src, $name, @ops) = @_;
+ my($src, $rest_var, $name, @ops) = @_;
my $arity = 0;
-
+
foreach (@ops) {
my(@list) = &tr_parse_op($src, $_);
- $arity++ unless $list[1] eq '*';
+ if ($list[1] eq '*') {
+ $rest_var = $list[0];
+ } elsif (defined $rest_var and $list[0] eq $rest_var) {
+ $list[1] = '*';
+ } else {
+ $arity++;
+ }
$_ = [ @list ];
}
@@ -1260,7 +1267,7 @@ sub compile_transform {
$is_transformed{$name,$arity} = 1;
}
- [$name,$arity,@ops];
+ ($rest_var,[$name,$arity,@ops]);
}
sub tr_parse_op {
@@ -1681,7 +1688,9 @@ sub tr_gen_to {
foreach $op (@ops) {
my($var, $type, $type_val) = @$op;
- if ($var ne '') {
+ if ($type eq '*') {
+ push(@code, make_op($var, 'store_rest_args', $var{$var}));
+ } elsif ($var ne '') {
&error($where, "variable '$var' unbound")
unless defined $var{$var};
push(@code, &make_op($var, 'store_var_next_arg', $var{$var}));