fix range op under aborted constant folding
authorZefram <zefram@fysh.org>
Fri, 27 Jan 2017 03:55:46 +0000 (03:55 +0000)
committerZefram <zefram@fysh.org>
Fri, 27 Jan 2017 04:12:32 +0000 (04:12 +0000)
When constant-folding a range/flipflop construct, the op_next threading
of peephole optimisation caused multiple ops in the construct to have
a null op_next, because the final (and top-level) op in the construct
is a null op.  This meant that simple restoration of the top-level
op's op_next after execution wouldn't get it back into a fit state
to be composed with other ops.  In the event that the range construct
couldn't be constant-folded this made it compile to a broken optree.
If it couldn't be constant-folded but could actually be executed, for
example because it generated a warning, this meant the brokenness would
be encountered at runtime.  Execution would stop after the range op,
because of the null op_next.

To avoid this, temporarily mark the null op as a custom op during the
peephole optimisation that supports the execution for constant-folding.
This prevents it being op_next-threaded out, so simple op_next restoring
then works.  If the constant-folding is aborted, it compiles to an
operational optree.  However, the suppression of duplicate peephole
optimisation means that the null op is never ultimately threaded out
as it should be.  For the time being, this stands as a cost of failed
constant-folding of range constructs.

Fixes [perl #130639].

op.c
t/comp/fold.t

diff --git a/op.c b/op.c
index c4c9fc0..c9e2078 100644 (file)
--- a/op.c
+++ b/op.c
@@ -4632,6 +4632,7 @@ S_gen_constant_list(pTHX_ OP *o)
     COP not_compiling;
     int ret = 0;
     dJMPENV;
     COP not_compiling;
     int ret = 0;
     dJMPENV;
+    bool op_was_null;
 
     list(o);
     if (PL_parser && PL_parser->error_count)
 
     list(o);
     if (PL_parser && PL_parser->error_count)
@@ -4640,7 +4641,12 @@ S_gen_constant_list(pTHX_ OP *o)
     curop = LINKLIST(o);
     old_next = o->op_next;
     o->op_next = 0;
     curop = LINKLIST(o);
     old_next = o->op_next;
     o->op_next = 0;
+    op_was_null = o->op_type == OP_NULL;
+    if (op_was_null)
+       o->op_type = OP_CUSTOM;
     CALL_PEEP(curop);
     CALL_PEEP(curop);
+    if (op_was_null)
+       o->op_type = OP_NULL;
     S_prune_chain_head(&curop);
     PL_op = curop;
 
     S_prune_chain_head(&curop);
     PL_op = curop;
 
index a875b5b..a72394e 100644 (file)
@@ -4,7 +4,7 @@
 # we've not yet verified that use works.
 # use strict;
 
 # we've not yet verified that use works.
 # use strict;
 
-print "1..34\n";
+print "1..35\n";
 my $test = 0;
 
 # Historically constant folding was performed by evaluating the ops, and if
 my $test = 0;
 
 # Historically constant folding was performed by evaluating the ops, and if
@@ -189,3 +189,6 @@ $b = 0;
 $a = eval 'my @z; @z = 0..~0 if $b; 3';
 is ($a, 3, "list constant folding doesn't signal compile-time error");
 is ($@, '', 'no error');
 $a = eval 'my @z; @z = 0..~0 if $b; 3';
 is ($a, 3, "list constant folding doesn't signal compile-time error");
 is ($@, '', 'no error');
+
+$a = eval 'local $SIG{__WARN__} = sub {}; join("", ":".."~", "z")';
+is ($a, ":z", "aborted list constant folding still executable");