Skip to content

Commit c899ae2

Browse files
committed
[MERGE] avoid calling pp_null().
Several 'empty' ops like OP_NULL and OP_SCOPE call pp_null() at run-time (which just returns). Attempts are made to strip such empty ops from the op_next execution chain, but this has not been not complete. In particular, ops at the head or tail of a sub-chain, or ops that rpeep() has itself nulled, weren't being eliminated. This merge avoids all calls to pp_null() in the test suite, apart from those called via the constant folder (which is called before null op elimination), and OP_REGCMAYBE (which isn't addressed here).
2 parents 61eef0f + 72621f8 commit c899ae2

File tree

4 files changed

+117
-23
lines changed

4 files changed

+117
-23
lines changed

embed.fnc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2017,7 +2017,7 @@ s |void |qsortsvu |NULLOK SV** array|size_t num_elts|NN SVCOMPARE_t compare
20172017
#endif
20182018

20192019
#if defined(PERL_IN_PP_SYS_C)
2020-
s |OP* |doform |NN CV *cv|NN GV *gv|NN OP *retop
2020+
s |OP* |doform |NN CV *cv|NN GV *gv|NULLOK OP *retop
20212021
# if !defined(HAS_MKDIR) || !defined(HAS_RMDIR)
20222022
sR |int |dooneliner |NN const char *cmd|NN const char *filename
20232023
# endif

op.c

Lines changed: 111 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,24 @@ recursive, but it's recursive on basic blocks, not on tree nodes.
109109
#define CALL_RPEEP(o) PL_rpeepp(aTHX_ o)
110110
#define CALL_OPFREEHOOK(o) if (PL_opfreehook) PL_opfreehook(aTHX_ o)
111111

112+
/* remove any leading "empty" ops from the op_next chain whose first
113+
* node's address is stored in op_p. Store the updated address of the
114+
* first node in op_p.
115+
*/
116+
117+
STATIC void
118+
S_prune_chain_head(pTHX_ OP** op_p)
119+
{
120+
while (*op_p
121+
&& ( (*op_p)->op_type == OP_NULL
122+
|| (*op_p)->op_type == OP_SCOPE
123+
|| (*op_p)->op_type == OP_SCALAR
124+
|| (*op_p)->op_type == OP_LINESEQ)
125+
)
126+
*op_p = (*op_p)->op_next;
127+
}
128+
129+
112130
/* See the explanatory comments above struct opslab in op.h. */
113131

114132
#ifdef PERL_DEBUG_READONLY_OPS
@@ -3297,6 +3315,7 @@ Perl_newPROG(pTHX_ OP *o)
32973315
ENTER;
32983316
CALL_PEEP(PL_eval_start);
32993317
finalize_optree(PL_eval_root);
3318+
S_prune_chain_head(aTHX_ &PL_eval_start);
33003319
LEAVE;
33013320
PL_savestack_ix = i;
33023321
}
@@ -3341,6 +3360,7 @@ Perl_newPROG(pTHX_ OP *o)
33413360
PL_main_root->op_next = 0;
33423361
CALL_PEEP(PL_main_start);
33433362
finalize_optree(PL_main_root);
3363+
S_prune_chain_head(aTHX_ &PL_main_start);
33443364
cv_forget_slab(PL_compcv);
33453365
PL_compcv = 0;
33463366

@@ -3647,9 +3667,11 @@ S_gen_constant_list(pTHX_ OP *o)
36473667
if (PL_parser && PL_parser->error_count)
36483668
return o; /* Don't attempt to run with errors */
36493669

3650-
PL_op = curop = LINKLIST(o);
3670+
curop = LINKLIST(o);
36513671
o->op_next = 0;
36523672
CALL_PEEP(curop);
3673+
S_prune_chain_head(aTHX_ &curop);
3674+
PL_op = curop;
36533675
Perl_pp_pushmark(aTHX);
36543676
CALLRUNOPS(aTHX);
36553677
PL_op = curop;
@@ -4876,6 +4898,7 @@ Perl_pmruntime(pTHX_ OP *o, OP *expr, bool isreg, I32 floor)
48764898
/* have to peep the DOs individually as we've removed it from
48774899
* the op_next chain */
48784900
CALL_PEEP(o);
4901+
S_prune_chain_head(aTHX_ &(o->op_next));
48794902
if (is_compiletime)
48804903
/* runtime finalizes as part of finalizing whole tree */
48814904
finalize_optree(o);
@@ -7599,6 +7622,7 @@ Perl_newMYSUB(pTHX_ I32 floor, OP *o, OP *proto, OP *attrs, OP *block)
75997622
CvROOT(cv)->op_next = 0;
76007623
CALL_PEEP(CvSTART(cv));
76017624
finalize_optree(CvROOT(cv));
7625+
S_prune_chain_head(aTHX_ &CvSTART(cv));
76027626

76037627
/* now that optimizer has done its work, adjust pad values */
76047628

@@ -7954,6 +7978,7 @@ Perl_newATTRSUB_x(pTHX_ I32 floor, OP *o, OP *proto, OP *attrs,
79547978
CvROOT(cv)->op_next = 0;
79557979
CALL_PEEP(CvSTART(cv));
79567980
finalize_optree(CvROOT(cv));
7981+
S_prune_chain_head(aTHX_ &CvSTART(cv));
79577982

79587983
/* now that optimizer has done its work, adjust pad values */
79597984

@@ -8351,6 +8376,7 @@ Perl_newFORM(pTHX_ I32 floor, OP *o, OP *block)
83518376
CvROOT(cv)->op_next = 0;
83528377
CALL_PEEP(CvSTART(cv));
83538378
finalize_optree(CvROOT(cv));
8379+
S_prune_chain_head(aTHX_ &CvSTART(cv));
83548380
cv_forget_slab(cv);
83558381

83568382
finish:
@@ -9962,9 +9988,12 @@ Perl_ck_sort(pTHX_ OP *o)
99629988
if (o->op_flags & OPf_STACKED)
99639989
simplify_sort(o);
99649990
firstkid = cLISTOPo->op_first->op_sibling; /* get past pushmark */
9991+
99659992
if ((stacked = o->op_flags & OPf_STACKED)) { /* may have been cleared */
99669993
OP *kid = cUNOPx(firstkid)->op_first; /* get past null */
99679994

9995+
/* if the first arg is a code block, process it and mark sort as
9996+
* OPf_SPECIAL */
99689997
if (kid->op_type == OP_SCOPE || kid->op_type == OP_LEAVE) {
99699998
LINKLIST(kid);
99709999
if (kid->op_type == OP_LEAVE)
@@ -9991,6 +10020,16 @@ Perl_ck_sort(pTHX_ OP *o)
999110020
return o;
999210021
}
999310022

10023+
/* for sort { X } ..., where X is one of
10024+
* $a <=> $b, $b <= $a, $a cmp $b, $b cmp $a
10025+
* elide the second child of the sort (the one containing X),
10026+
* and set these flags as appropriate
10027+
OPpSORT_NUMERIC;
10028+
OPpSORT_INTEGER;
10029+
OPpSORT_DESCEND;
10030+
* Also, check and warn on lexical $a, $b.
10031+
*/
10032+
999410033
STATIC void
999510034
S_simplify_sort(pTHX_ OP *o)
999610035
{
@@ -11136,21 +11175,28 @@ S_inplace_aassign(pTHX_ OP *o) {
1113611175
op_null(oleft);
1113711176
}
1113811177

11178+
11179+
11180+
/* mechanism for deferring recursion in rpeep() */
11181+
1113911182
#define MAX_DEFERRED 4
1114011183

1114111184
#define DEFER(o) \
1114211185
STMT_START { \
1114311186
if (defer_ix == (MAX_DEFERRED-1)) { \
11144-
CALL_RPEEP(defer_queue[defer_base]); \
11187+
OP **defer = defer_queue[defer_base]; \
11188+
CALL_RPEEP(*defer); \
11189+
S_prune_chain_head(aTHX_ defer); \
1114511190
defer_base = (defer_base + 1) % MAX_DEFERRED; \
1114611191
defer_ix--; \
1114711192
} \
11148-
defer_queue[(defer_base + ++defer_ix) % MAX_DEFERRED] = o; \
11193+
defer_queue[(defer_base + ++defer_ix) % MAX_DEFERRED] = &(o); \
1114911194
} STMT_END
1115011195

1115111196
#define IS_AND_OP(o) (o->op_type == OP_AND)
1115211197
#define IS_OR_OP(o) (o->op_type == OP_OR)
1115311198

11199+
1115411200
STATIC void
1115511201
S_null_listop_in_list_context(pTHX_ OP *o)
1115611202
{
@@ -11181,7 +11227,7 @@ Perl_rpeep(pTHX_ OP *o)
1118111227
dVAR;
1118211228
OP* oldop = NULL;
1118311229
OP* oldoldop = NULL;
11184-
OP* defer_queue[MAX_DEFERRED]; /* small queue of deferred branches */
11230+
OP** defer_queue[MAX_DEFERRED]; /* small queue of deferred branches */
1118511231
int defer_base = 0;
1118611232
int defer_ix = -1;
1118711233

@@ -11194,8 +11240,12 @@ Perl_rpeep(pTHX_ OP *o)
1119411240
if (o && o->op_opt)
1119511241
o = NULL;
1119611242
if (!o) {
11197-
while (defer_ix >= 0)
11198-
CALL_RPEEP(defer_queue[(defer_base + defer_ix--) % MAX_DEFERRED]);
11243+
while (defer_ix >= 0) {
11244+
OP **defer =
11245+
defer_queue[(defer_base + defer_ix--) % MAX_DEFERRED];
11246+
CALL_RPEEP(*defer);
11247+
S_prune_chain_head(aTHX_ defer);
11248+
}
1119911249
break;
1120011250
}
1120111251

@@ -11440,7 +11490,7 @@ Perl_rpeep(pTHX_ OP *o)
1144011490
case OP_LINESEQ:
1144111491
case OP_SCOPE:
1144211492
nothin:
11443-
if (oldop && o->op_next) {
11493+
if (oldop) {
1144411494
oldop->op_next = o->op_next;
1144511495
o->op_opt = 0;
1144611496
continue;
@@ -11871,6 +11921,11 @@ Perl_rpeep(pTHX_ OP *o)
1187111921
DEFER(cLOOP->op_lastop);
1187211922
break;
1187311923

11924+
case OP_ENTERTRY:
11925+
assert(cLOGOPo->op_other->op_type == OP_LEAVETRY);
11926+
DEFER(cLOGOPo->op_other);
11927+
break;
11928+
1187411929
case OP_SUBST:
1187511930
assert(!(cPMOP->op_pmflags & PMf_ONCE));
1187611931
while (cPMOP->op_pmstashstartu.op_pmreplstart &&
@@ -11883,12 +11938,28 @@ Perl_rpeep(pTHX_ OP *o)
1188311938
case OP_SORT: {
1188411939
OP *oright;
1188511940

11886-
if (o->op_flags & OPf_STACKED) {
11887-
OP * const kid =
11888-
cUNOPx(cLISTOP->op_first->op_sibling)->op_first;
11889-
if (kid->op_type == OP_SCOPE
11890-
|| (kid->op_type == OP_NULL && kid->op_targ == OP_LEAVE))
11891-
DEFER(kLISTOP->op_first);
11941+
if (o->op_flags & OPf_SPECIAL) {
11942+
/* first arg is a code block */
11943+
OP * const nullop = cLISTOP->op_first->op_sibling;
11944+
OP * kid = cUNOPx(nullop)->op_first;
11945+
11946+
assert(nullop->op_type == OP_NULL);
11947+
assert(kid->op_type == OP_SCOPE
11948+
|| (kid->op_type == OP_NULL && kid->op_targ == OP_LEAVE));
11949+
/* since OP_SORT doesn't have a handy op_other-style
11950+
* field that can point directly to the start of the code
11951+
* block, store it in the otherwise-unused op_next field
11952+
* of the top-level OP_NULL. This will be quicker at
11953+
* run-time, and it will also allow us to remove leading
11954+
* OP_NULLs by just messing with op_nexts without
11955+
* altering the basic op_first/op_sibling layout. */
11956+
kid = kLISTOP->op_first;
11957+
assert(
11958+
(kid->op_type == OP_NULL && kid->op_targ == OP_NEXTSTATE)
11959+
|| kid->op_type == OP_STUB
11960+
|| kid->op_type == OP_ENTER);
11961+
nullop->op_next = kLISTOP->op_next;
11962+
DEFER(nullop->op_next);
1189211963
}
1189311964

1189411965
/* check that RHS of sort is a single plain array */
@@ -12040,6 +12111,23 @@ Perl_rpeep(pTHX_ OP *o)
1204012111
if (OP_GIMME(o,0) == G_VOID) {
1204112112
OP *right = cBINOP->op_first;
1204212113
if (right) {
12114+
/* sassign
12115+
* RIGHT
12116+
* substr
12117+
* pushmark
12118+
* arg1
12119+
* arg2
12120+
* ...
12121+
* becomes
12122+
*
12123+
* ex-sassign
12124+
* substr
12125+
* pushmark
12126+
* RIGHT
12127+
* arg1
12128+
* arg2
12129+
* ...
12130+
*/
1204312131
OP *left = right->op_sibling;
1204412132
if (left->op_type == OP_SUBSTR
1204512133
&& (left->op_private & 7) < 4) {
@@ -12065,8 +12153,16 @@ Perl_rpeep(pTHX_ OP *o)
1206512153
}
1206612154

1206712155
}
12068-
oldoldop = oldop;
12069-
oldop = o;
12156+
/* did we just null the current op? If so, re-process it to handle
12157+
* eliding "empty" ops from the chain */
12158+
if (o->op_type == OP_NULL && oldop && oldop->op_next == o) {
12159+
o->op_opt = 0;
12160+
o = oldop;
12161+
}
12162+
else {
12163+
oldoldop = oldop;
12164+
oldop = o;
12165+
}
1207012166
}
1207112167
LEAVE;
1207212168
}

pp_sort.c

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1512,10 +1512,9 @@ PP(pp_sort)
15121512
SAVEVPTR(PL_sortcop);
15131513
if (flags & OPf_STACKED) {
15141514
if (flags & OPf_SPECIAL) {
1515-
OP *kid = cLISTOP->op_first->op_sibling; /* pass pushmark */
1516-
kid = kUNOP->op_first; /* pass rv2gv */
1517-
kid = kUNOP->op_first; /* pass leave */
1518-
PL_sortcop = kid->op_next;
1515+
OP *nullop = cLISTOP->op_first->op_sibling; /* pass pushmark */
1516+
assert(nullop->op_type == OP_NULL);
1517+
PL_sortcop = nullop->op_next;
15191518
}
15201519
else {
15211520
GV *autogv = NULL;

proto.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6579,10 +6579,9 @@ STATIC I32 S_sv_ncmp(pTHX_ SV *const a, SV *const b)
65796579
#if defined(PERL_IN_PP_SYS_C)
65806580
STATIC OP* S_doform(pTHX_ CV *cv, GV *gv, OP *retop)
65816581
__attribute__nonnull__(pTHX_1)
6582-
__attribute__nonnull__(pTHX_2)
6583-
__attribute__nonnull__(pTHX_3);
6582+
__attribute__nonnull__(pTHX_2);
65846583
#define PERL_ARGS_ASSERT_DOFORM \
6585-
assert(cv); assert(gv); assert(retop)
6584+
assert(cv); assert(gv)
65866585

65876586
STATIC SV * S_space_join_names_mortal(pTHX_ char *const *array)
65886587
__attribute__nonnull__(pTHX_1);

0 commit comments

Comments
 (0)