summaryrefslogtreecommitdiff
path: root/pp_ctl.c
diff options
context:
space:
mode:
authorDavid Mitchell <davem@iabyn.com>2015-05-23 11:12:18 +0100
committerDavid Mitchell <davem@iabyn.com>2015-06-19 08:44:17 +0100
commit6228a1e1a04e2002bf9e76fea0f52a87b43f841a (patch)
treeb87509221da87e19ff4a8c12fa145e5e8579b686 /pp_ctl.c
parentd3e5e568e186db1fcbf1fee0ea8e4e265b90f686 (diff)
downloadperl-6228a1e1a04e2002bf9e76fea0f52a87b43f841a.tar.gz
pp_return: tail call pp_leavesub
For returns within normal (rvalue) subs, handle the bulk of of the work by falling through into pp_leavesub, rather than repeating most of the code. So pp_return is now just responsible for popping any extra contexts and shifting any return args back down to the base of the stack. Return in lvalue subs, evals etc is unaffected.
Diffstat (limited to 'pp_ctl.c')
-rw-r--r--pp_ctl.c71
1 files changed, 30 insertions, 41 deletions
diff --git a/pp_ctl.c b/pp_ctl.c
index f26beb90b2..71d18b2a1a 100644
--- a/pp_ctl.c
+++ b/pp_ctl.c
@@ -2416,8 +2416,9 @@ PP(pp_return)
if (cxix < cxstack_ix)
dounwind(cxix);
- if (CxMULTICALL(&cxstack[cxix])) {
- gimme = cxstack[cxix].blk_gimme;
+ cx = &cxstack[cxix];
+ if (CxMULTICALL(cx)) {
+ gimme = cx->blk_gimme;
if (gimme == G_VOID)
PL_stack_sp = PL_stack_base;
else if (gimme == G_SCALAR) {
@@ -2427,11 +2428,29 @@ PP(pp_return)
return 0;
}
+ if (CxTYPE(cx) == CXt_SUB && !CvLVALUE(cx->blk_sub.cv)) {
+ SV **oldsp = PL_stack_base + cx->blk_oldsp;
+ if (oldsp != MARK) {
+ /* shift return args to base of call stack frame */
+ SSize_t nargs = SP - MARK;
+ if (nargs) {
+ if (cx->blk_gimme == G_ARRAY)
+ Move(MARK + 1, oldsp + 1, nargs, SV**);
+ else if (cx->blk_gimme == G_SCALAR)
+ oldsp[1] = *SP;
+ }
+ PL_stack_sp = oldsp + nargs;
+ }
+ /* fall through to a normal sub exit */
+ return Perl_pp_leavesub(aTHX);
+ }
+
POPBLOCK(cx,newpm);
switch (CxTYPE(cx)) {
case CXt_SUB:
popsub2 = TRUE;
- lval = !!CvLVALUE(cx->blk_sub.cv);
+ assert(CvLVALUE(cx->blk_sub.cv));
+ lval = TRUE;
retop = cx->blk_sub.retop;
cxstack_ix++; /* preserve cx entry on stack for use by POPSUB */
break;
@@ -2465,45 +2484,15 @@ PP(pp_return)
TAINT_NOT;
if (lval) S_return_lvalues(aTHX_ MARK, SP, newsp, gimme, cx, newpm);
else {
- if (gimme == G_SCALAR) {
- if (MARK < SP) {
- if (popsub2) {
- if (cx->blk_sub.cv && CvDEPTH(cx->blk_sub.cv) > 1) {
- if (SvTEMP(TOPs) && SvREFCNT(TOPs) == 1
- && !SvMAGICAL(TOPs)) {
- *++newsp = SvREFCNT_inc(*SP);
- FREETMPS;
- sv_2mortal(*newsp);
- }
- else {
- sv = SvREFCNT_inc(*SP); /* FREETMPS could clobber it */
- FREETMPS;
- *++newsp = sv_mortalcopy(sv);
- SvREFCNT_dec(sv);
- }
- }
- else if (SvTEMP(*SP) && SvREFCNT(*SP) == 1
- && !SvMAGICAL(*SP)) {
- *++newsp = *SP;
- }
- else
- *++newsp = sv_mortalcopy(*SP);
+ if (gimme == G_SCALAR)
+ *++newsp = (MARK < SP) ? sv_mortalcopy(*SP) : &PL_sv_undef;
+ else if (gimme == G_ARRAY) {
+ while (++MARK <= SP) {
+ *++newsp = sv_mortalcopy(*MARK);
+ TAINT_NOT; /* Each item is independent */
}
- else
- *++newsp = sv_mortalcopy(*SP);
- }
- else
- *++newsp = &PL_sv_undef;
- }
- else if (gimme == G_ARRAY) {
- while (++MARK <= SP) {
- *++newsp = popsub2 && SvTEMP(*MARK) && SvREFCNT(*MARK) == 1
- && !SvGMAGICAL(*MARK)
- ? *MARK : sv_mortalcopy(*MARK);
- TAINT_NOT; /* Each item is independent */
- }
- }
- PL_stack_sp = newsp;
+ }
+ PL_stack_sp = newsp;
}
LEAVE;