Skip to content

Dedicated SV cloning code in place of Perl_sv_setsv_flags #23202

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: blead
Choose a base branch
from

Conversation

richardleach
Copy link
Contributor

Perl_sv_setsv_flags is the heavyweight function for assigning the value(s) of
a source SV to a destination SV. It contains many branches for preparing the
destination SV prior to assignment. However:

  • If the destination SV has just been created, much of that logic isn't needed.
  • When cloning a SV, simple assignments (particularly IVs and PVs) dominate.

This set of commits:

  • Extracts the "is this CoWable?" test from Perl_sv_setsv_flags into a macro.
  • Adds S_sv_freshcopy_flags and S_sv_freshcopy_POK helper functions.
  • Modifies Perl_newSVsv_flags and Perl_sv_mortalcopy_flags to use them -
    and optimise the hot cases in particular.
  • Standardizes a number of call sites that did their own things but really
    should use Perl_newSVsv_flags or Perl_sv_mortalcopy_flags.

Using perl's test harness as a guide:

  • Bodyless code handles 45% of calls to Perl_newSVsv_flags and
    57% of calls to Perl_sv_mortalcopy_flags.
  • The SVt_PV/SVp_POK code handles 32% of calls to
    Perl_newSVsv_flags and 36% of calls to Perl_sv_mortalcopy_flags.
  • S_sv_freshcopy_flags code handles 95% of the remainder in
    Perl_newSVsv_flags and 91% of the remainder in to Perl_sv_mortalcopy_flags.

With these changes compared with a build of blead:

  • perl -e 'for (1..100_000) { my $x = [ (1) x 1000 ]; }' runs 30% faster

  • perl -e 'for (1..100_000) { my $x = [ ("Perl") x 1000 ]; }' runs:

    • 15% faster if newSV_type(SVt_PV) is NOT inlined
    • 30% faster if it IS inlined

  • This set of changes does not require a perldelta entry.

S_sv_freshcopy_flags is a drop-in replacement for `Perl_sv_setsv_flags`.
It is designed for use when the destination SV is being freshly created
and much of the logic in `sv_setsv_flags` is irrelevant.

The intended users for this new function are:
* Perl_sv_mortalcopy_flags
* Perl_newSVsv_flags

Those functions have been modified such that:
* Bodyless destination SVs are created inline
* SVt_PVs also have special casing
* SVt_PVMG and below use S_sv_freshcopy_flags
* Anything else drops back to using Perl_sv_setsv_flags

S_sv_freshcopy_POK is a helper function that concentrates on the string
assignment logic:
* Swipe the buffer
* CoW the buffer
* Copy the buffer

Using perl's test harness as a guide:
* 45% of Perl_newSVsv_flags / 57% of Perl_sv_mortalcopy_flags calls use
  the bodyless code
* 32% of Perl_newSVsv_flags / 36% of Perl_sv_mortalcopy_flags calls use
  the SVt_PV/SVp_POK code
* The S_sv_freshcopy_flags code handles the bulk of the remainder.

With these changes compared with a build of blead:

* `perl -e 'for (1..100_000) { my $x = [ (1) x 1000 ]; }'` runs 30% faster

* `perl -e 'for (1..100_000) { my $x = [ ("Perl") x 1000 ]; }' runs:
   * 15% faster if `newSV_type(SVt_PV)` is NOT inlined
   * 30% faster if it IS inlined

The overall reduction in branches when cloning SVs, and refocusing of branch
prediction within Perl_sv_setsv_flags, will hopefully give a meaningful
boost to realistic Perl applications.
@richardleach richardleach added the defer-next-dev This PR should not be merged yet, but await the next development cycle label Apr 15, 2025
sv_setsv_nomg(sv, *MARK);
if (*MARK) {
SvGETMAGIC(*MARK);
sv = newSVsv_flags(*MARK, SV_DO_COW_SVSETSV);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe cache *MARK to a auto and skip the 2nd -O1/2 deref in sv = newSVsv_flags(*MARK, SV_DO_COW_SVSETSV);? fn call containing macro SvGETMAGIC(*MARK); won't overwrite the SV* on the PL stack with a new SV*.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just calling newSVsv_flags(*MARK, SV_DO_COW_SVSETSV|SV_GMAGIC) should be better

            if (*MARK) {
                sv = newSVsv_flags(*MARK, SV_DO_COW_SVSETSV|SV_GMAGIC);
            }
            else
                sv = newSV_type(SVt_NULL);

does (MARK is %rbx), %rdi is the first function argument:

        .loc 2 6424 13 view .LVU16810
        .loc 2 6424 17 is_stmt 0 view .LVU16811
        movq    (%rbx), %rdi
        .loc 2 6424 16 view .LVU16812
        testq   %rdi, %rdi
        jne     .L5826
        .loc 2 6428 17 is_stmt 1 view .LVU16813
        ; the else with "sv = newSV_type(SVt_NULL);", not shown
...
.L5826:
.LBB7306:
        .loc 2 6425 17 view .LVU16801
        .loc 2 6425 22 is_stmt 0 view .LVU16802
        movl    $1538, %esi
        call    Perl_newSVsv_flags@PLT

Perl_newSVsv_flags() already has the GMAGIC test and mg_get() call, saving space and a duplicate test here, and the source SV goes into a register and stays there.

@@ -5357,8 +5357,7 @@ PP(pp_subst)
if (dstr) {
/* replacement needing upgrading? */
if (DO_UTF8(TARG) && !doutf8) {
nsv = sv_newmortal();
SvSetSV(nsv, dstr);
nsv = sv_mortalcopy_flags(dstr, SV_GMAGIC|SV_DO_COW_SVSETSV);
Copy link
Contributor

@bulk88 bulk88 Apr 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-think this chunk, macro SvSetSV macro has an ultra fast shortcut/bypass in it. That shortcut will be lost if this chinkgoes to production. sv_mortalcopy_flags is a linkable extern C func. The macro's shortcut path doesn't have any fn calls in it. The shortcut branch in that macro may or may not be executable in real life at this line of code. Plz research.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That shortcut/bypass:

#define SvSetSV_and(dst,src,finally) \
        STMT_START {                                    \
            SV * src_ = src;                            \
            SV * dst_ = dst;                            \
            if (LIKELY((dst_) != (src_))) {             \
                sv_setsv(dst_, src_);                   \
                finally;                                \
            }                                           \
        } STMT_END
...
#define SvSetSV(dst,src) \
                SvSetSV_and(dst,src,/*nothing*/;)

dst_ is never equal to src_ here (unless dst_ points to freed SV, which would be a serious bug and shouldn't be optimized for).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That shortcut/bypass:
....
dst_ is never equal to src_ here (unless dst_ points to freed SV, which would be a serious bug and shouldn't be optimized for).

@tonycoz you are correct, That macro's optimization branch is impossible to execute at this particular line.

&& !(SvFLAGS(dsv) & SVf_BREAK) \
)
#endif

Copy link
Contributor

@bulk88 bulk88 Apr 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this macro cover or need to cover SVf_STATIC COWs? Modern Perl C has a total of 4 SVPV COW types right now. COW 255, HEK COW, SVf_STATIC and SvLEN == 0.

There is a future 5th SVPV COW type that needs to get added by someone. Blead's current SVPV COW impl/logic has a very bad sore spot, that RCPV objs can't be boxed/wrapped in a SV* head. Its just a limitation from RCPV's day 1 "minimalist" design, since it was created as low-controversy new API alternative to my heavy weight new API inter-thread SHEK patch that stalled out over lack of review/input/identifier naming/final C prototypes of new macros/funcs some years earlier. RCPV objs are heavily used inside threaded Perl, since they are the backbone of the inter-ithread op-tree related "hint hash" and CopFILE subsystems. So this PR needs to have provisions, or keep it easy/clean/maintainable for someone to add SVPV COW type # 5 at a later date.

Also sv_setsv_foo(dsv, ssv, F_FOO) all P5 versions has never propagated SvLEN == 0 SvPVX pointers AFAIK. Which makes "for speed"/"for perf" usage of RO C strings in interp versions pre-SVf_STATIC, near useless/a benchmarkable perf downgrade in production, for CPAN XS code, since SvLEN == 0 COWs almost instantly get de-COWed in the runloop. In very very rare cases, along with using dXSTARG, and a PP caller, using SvLEN == 0 COWs maybe will be a benchmark win, but I personally refuse to write "SvLEN == 0 COW" code ever again for pre-SVf_STATIC interps, and instead I keep HEKs or PVHEK SVs in my XS lib's MY_CXT struct for pre-SVf_STATIC interps.

SVf_STATIC is most used for public API req bool propagation rn, maybe in the near future, alot of authorized or P5P unauthorized CPAN XS/B::C/B::CC usage, I am addicted to that flag after it was added. SVPVHEKs are slightly slightly bouncing around the interp than SVf_STATIC SVPVs, I remember the diff being as 6 ns or 6 us for SVf_STATIC vs 9 ns or 9 us for SVPVHEK in some private benchmark I did.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SvLEN() == 0 doesn't mean that the SvPVX is freely copiable to other SVs, multiconcat for example sets PAD SVs to SvLEN()==0 that point into the OP's aux data. If these transferred the PV pointer they'd become invalid if the OP were freed.

This macro brings out the existing test, talking about new things that could be done with SV behaviour here seems off-topic to me.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree, the SvLEN() == 0 topic is too complex and too distant from this PR to think about or care about in this PR. SvLEN() == 0 is so rarely seen IME, both PP/core and random CPAN XS libs that, for me and what I care about, it is a very low priority topic. It is a 2nd round of desert in the engine, not the main course.

Only the first 3 types, COW 255, HEK COW, SVf_STATIC, need propagation or meaningful "thinking over" in this PR.

From what I see with 5.41, the COW 255 is # 1 super highest frequency COW type to bounce around VM state in the runloop. HEK COWs are # 2 in frequency, but in my rough guess, their circulation or chance of seeing one at a random breakpoint you made, is 1/4th or 1/10th the chance of a seeing a COW 255. Mostly b/c of missed-optimization opportunities, which can be slowly fixed over time in small steps. # 3 COW type, SVf_STATIC, in real life, they are almost non-existent in the run loop. That COW type only exists for reasons involving SvIMMs, public API, and various CPAN XS JSON/JSON-like modules. Aslong as this PR doesn't break the bare minimum public API behavior of # 3, this PR is good enough to go into blead.

Deep future/long-term strategic outlook, SVf_STATIC has very high use potential and speed benefits.

As you said, SvLEN() == 0 has legacy (?) or a long time CPAN XS public API assumption, that SvLEN() == 0 + MG*->vtbl->svt_free() + SvREADONLY() is the proper way to implement your brand new Kernel MMap XS module. I definitely see how un-stable/high risk it is, to suddenly make the SvPVX() ptr from a SvLEN() == 0, randomly and uncontrollably reproduce throughout the VM when XS mods don't want their 3p/foreign RO or RW SvPVX() to reproduce.

SVf_STATIC solves alot of the "Perl 5 SUXS!!! The PHP interp and Python interp compile their code." gossip problem, on the engineering technical level, not the politics level. newSVpvs() and either libperl.so.dll+DynaLoader::/XS, or libperl.so.dll-only, enough said.

SvHEK COWs, egh, they work, they have their purpose, but on x64 CPUs, they have a 0x21 bytes long header, excluding SvCUR(), excluding libc's malloc header, excluding libc bucket round up. And I've heard this before, and agree, what if U32 HEK_HASH() is never ever used/read, for rest of the proc's lifetime? It easy to guess for any XS dev, what CStr literal they typed WILL, CAN, and NEVER will become a HE.

Off-topic for this PR and its scope, SVf_STATIC, as designed, as intended, has a major design limitation and a tiny runtime performace problem. A 5% or 10% grey zone exists, where SVf_STATIC is wrong, and SvPVHEK is wrong. I ran into it with one of my unpublished experiments.

newSVpvs() ? cool, that is easy enough.

Now think, wait for it, sv_setpvs();. Now what???!!! Not so easy.

f it is not clear, the technical problem is, what if the SV* already has a SvPVX() from Newx() with correct length? dXSTARG, my/PAD vars, sv_catpvn, sv_catpvf, and more. API contract says flag SVf_STATIC SHALL propagate, not MAY propagate. It is not optional for sv_setsv()/sv_setpvs_xtra_sss(); to ignore and drop out that flag, even if it very un-wise, and a total performance downgrade, to propagate the flag, or in other words, do the Safefree(SvPVX()) dance. So very long term strategic, and low priority, this rare 5% grey zone, needs its own SV* metadata permutation in core.

* though, which is why the code below does not try that type
* simplification. Perhaps this might be worth revisiting in the future.
* -- April 2025. */
assert(ssv);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea, just b/c ssv is currently type PVIV/PVNV/PVMG/PVLV, isn't a valid reason for dsv to be 1 of those 4, if there isn't any fOK data or other metadata on ssv, to justify dsv being upgraded to a super-sized body type. ssv could've been blessed or had MAGIC *s, or been PVLV (my ❤️ SV type), could've been a stringified IV -> PV in the past, but got a
PP =, and by now IOK_OK and IOKp_OK, both, are ancient history. Since this C func is always forking a source SV*, its very unlikely IMO the new fork will get independently near-future get upgraded to a large exotic SV body, by the caller C code/API/module/PP or XSUB, that is creating dest SV*, if the source SV* isn't currently defined and formally data-wise blessed/overload.pm/MAGIC */PVLV/etc at the exactly moment,

/* The switch cases are in the order that gcov of make/make test suggests. */
switch(sflags & (SVp_IOK|SVp_NOK|SVf_ROK|SVp_POK|SVf_FAKE|SVppv_STATIC)) {
case SVp_IOK:
SvIV_set(dsv, SvIVX(ssv));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SVt_IV and some build configs SVt_NV, are bodyless, please write direct to the 4th SV head field and skip indirecting through SvANY. Grep sv.c for ideas on how to do the fresh bodyless SV head field 4 assignment.

const char *vstr_pv;
STRLEN vstr_len;
if ((vstr_pv = SvVSTRING(ssv, vstr_len))) {
sv_magic(dsv, NULL, PERL_MAGIC_vstring, vstr_pv, vstr_len);
Copy link
Contributor

@bulk88 bulk88 Apr 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't feel super comfortable with this. This if() test probably needs to be done before SV *dsv = newSV_type(stype); executes at the very top, so sv_magic() doesn't need to execute sv_upgrade() to go from SVt_PV to SVt_PVMG, The right body type for SV* dsv, was picked correctly from the very start. Looking at machine code/single stepping in machine code view, or some C dbgr BP research might change my/other ppls opinion if the macro SvVSTRING( in if ((vstr_pv = SvVSTRING(ssv, vstr_len))) { is super expensive/inefficient, and therefore justify sv_magic() executing sv_upgrade() which slightly inefficient, vs slowing down 100% of all other interp wide calls with a source SV* of any type/contents.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SvVSTRING() is a macro that tests magical-ness via flags before checking the actual magic, so it should be cheap in most circumstances, the most common exception being utf8 length magic.

I don't think the upgrade is an issue, vstrings are generally rare, an extra check early on adds extra instructions (including a branch) in the hot path for every other SV type,

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think the upgrade is an issue, vstrings are generally rare, an extra check early on adds extra instructions (including a branch) in the hot path for every other SV type,

Same here. I am just nit picking, any thing, any flaw, I can think of. Some of the flaws I mention in this PR, are just for academic reasons or academic ritual. A certain small percent % of the flaws I mention in this PR, even I would write back with "No/decline/disagree, here is why .....". Fixing the flaw was not the intention. someone thinking it over and posting "No/decline/disagree, here is why ....." was the point :-)

Besides, I have never in my life seen a SV* VSTRING in my C debugger. I disagree with your opinion that they are generally rare. My opinion is they don't exist. They are some obsolete ancient grammar museum thing from 5.000 alpha or 4.0, and can get the chainsaw. VSTRING lost its SvTYPE() slot and was "sent to CPAN" in 5.9.x or 5.10.1 IIRC. It has been a humble MG* struct "from CPAN" ever since.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have a vstring:

$ perl -MDevel::Peek -e 'Dump(v1.2.3)'
SV = PVMG(0x55a6e40944c0) at 0x55a6e4051ee0
  REFCNT = 1
  FLAGS = (RMG,POK,IsCOW,READONLY,PROTECT,pPOK)
  IV = 0
  NV = 0
  PV = 0x55a6e405a6f0 "\x01\x02\x03"\0
  CUR = 3
  LEN = 10
  COW_REFCNT = 0
  MAGIC = 0x55a6e405b2c0
    MG_VIRTUAL = 0
    MG_TYPE = PERL_MAGIC_vstring(V)
    MG_LEN = 6
    MG_PTR = 0x55a6e4043b60 "v1.2.3"

SvRMAGICAL_on(dsv);
}
}
break;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see same-looking code on case: above and case: below this comment, maybe remove the break; and fall through?

/* Source was SVf_IOK|SVp_IOK|SVp_POK but not SVf_POK, meaning
a value set as an integer and later stringified. So mark
destination the same: */
SvFLAGS(dsv) &= ~SVf_POK;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What code above in this func, already wrote to dsv SvFLAGS , in such a way that public flag not private flag, SVf_POK bit is turned on in dsv right now? Im confused.

{
const STRLEN cur = SvCUR(ssv);

(void)SvPOK_only(dsv);
Copy link
Contributor

@bulk88 bulk88 Apr 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why would there be any ROK+public 3, FAKE + private 3, SV head flag bits, be turned on, in a fresh SV* head of any body type? I hope there isn't a secret sv_backoff() fn call on this line.

SvNV_set(dsv, SvNVX(ssv));
SvFLAGS(dsv) |= SVf_IsCOW|SVppv_STATIC|
(sflags & (SVf_IOK|SVp_IOK|SVf_IVisUV|SVf_NOK|SVp_NOK|SVf_POK|SVf_POK|SVf_UTF8));

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This case: is getting vague and indecisive and unsure of itself own purpose, is this case: a generic SVf_IsCOW|SVppv_STATIC duplicator, or a highly specific "3 SvIMMORTALs()" duplicator, or a duplicator of "3 SvIMMORTALS" + any regular RCed forks/descendants of the 3 IMMs/RO SV bools? We don't need to dynamically & mask out current contents from sflags, if we can prove what flag bits sflags, and use a most efficient literal integer here,

{
const char *vstr_pv;
STRLEN vstr_len;
if ((vstr_pv = SvVSTRING(ssv, vstr_len))) {
Copy link
Contributor

@bulk88 bulk88 Apr 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This VSTRING test is starting to look like excessive copy paste. It can't be in so many case statements.

Is this also true?

if (SvVSTRING(&PL_sv_undef, unused_len))
    warn("true");

case SVf_ROK:
SvRV_set(dsv, SvREFCNT_inc(SvRV(ssv)));
SvFLAGS(dsv) |= sflags & SVf_ROK;
break;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are weak refs??? does this func need special casing for them? I forgot their internals off the top of my head, other that 1 side or the other uses a MAGIC , or hides a void * to the weak ref SV head, somewhere very secretive inside a HV*/AV*.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As below, I don't think we need any special handling of weak refs here.

sv_setsv_flags(dsv,ssv,flags);
return dsv;
case SVp_NOK:
SvNV_set(dsv, SvNVX(ssv));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remember about bodyless NVs, check rest of sv.c for examples. Don't indirect through SvANY.

return dsv;
case SVp_NOK:
SvNV_set(dsv, SvNVX(ssv));
SvFLAGS(dsv) |= (sflags & (SVf_NOK|SVp_NOK));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why read and extract bits from sflags? does SVp_NOK WITHOUT SVf_NOK and no other "OK" bits, does that have any meaning on a PP level? is it ever encountered at runtime in a blead/stable perl running normal production PP and CPAN XS code? do we need to consider buggy/weird/beginner CPAN XS code that would make such a SV* head or not?

|= with a literal on the right side, is much more efficient that bit extraction from var sflags, in CC optimized C code.

} else if (SvFLAGS(old) & SVf_ROK) {
SvFLAGS(dsv) |= SVf_ROK;
SvRV_set(dsv, SvREFCNT_inc(SvRV(old)));
}
Copy link
Contributor

@bulk88 bulk88 Apr 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

weak ref applies here or not? Move SvRV(old) to separate oldsv =; line, move SvREFCNT_inc to a separate line, and use one of the more efficient SvREFCNT_inc macros variants here, like SvREFCNT_inc_simple_void_NN.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A copy of a weak ref isn't weak itself, I don't think there needs to be any special handling of weak references here.

@@ -9635,15 +9906,75 @@ C<L</sv_setsv_flags>>.
SV *
Perl_sv_mortalcopy_flags(pTHX_ SV *const oldstr, U32 flags)
{
SV *sv;
SV * old = (oldstr) ? oldstr : &PL_sv_undef;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this, and don't introduce it as public API. SV * Perl_sv_mortalcopy_flags(interpreter *my_perl, SV *const oldstr, unsigned int flags) in blead/stable perl always has, and should continue to SEGV if oldstr is NULL. The caller frame can type &PL_sv_undef themselves if they want to. 10-25% chance a NULL SV* sitting in a C auto/C expression in the caller, is a uninitialized memory read bug and not a legit PP level undefined value. Also the interp exports Perl_sv_set_undef() (newish) and newSV() for CPAN XS authors if they want a RW PP undef in XS/C, and these 2 are much more efficient for their narrow scoped task vs any other Perl C API func/macro, that obviously that is more universal/dynamic in its purpose.

Note Perl_sv_set_undef() is newish, and is not in ppport.h, IIRC. Most CPAN XS authors are uninformed, or too lazy to write an old perl polyfill and use this func (Perl_sv_set_undef()) in private production XS code or in CPAN XS.

I personally use sv_set_undef() sometimes and I did write that polyfill. The polyfill/fallback for old perls is sv_setpv(sv, NULL) and/or sv_setpv_mg(sv, NULL) for dXSTARGs. I personally type sv_setpv_mg(sv, NULL) more often than sv_set_undef() b/c the 4 calls, sv_set_bool sv_set_bool sv_set_false sv_set_true have no _mg variants for dXSTARG, or returning &PL_sv_* on PL stack is more efficient b/c I know the average caller frame, PP or XS, that is caller my XSUB, isn't going to PP = or sv_setsv my PL stack retval to its own private SV* for long term storage.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this, and don't introduce it as public API. SV * Perl_sv_mortalcopy_flags(interpreter *my_perl, SV *const oldstr, unsigned int flags) in blead/stable perl always has, and should continue to SEGV if oldstr is NULL. The caller frame can type &PL_sv_undef themselves if they want to

I can sort of agree, but sv_mortalcopy_flags():

  • only segfaults on a NULL oldsv when SV_GMAGIC is set in flags
  • the embed.fnc entry has oldsv marked as NULLOK.

That said, rather than conditionally setting old and then going through the copy process to copy from PL_sv_undef, it could just conditionally return sv_newmortal().

@@ -10178,20 +10509,76 @@ parameter.
SV *
Perl_newSVsv_flags(pTHX_ SV *const old, I32 flags)
{
SV *sv;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe Perl_mortalcopy_flags needs to be a thin wrapper around Perl_newSVsv_flags, or a tailcall to Perl_newSVsv_flags with a super secret or minimally secret flag in Perl_newSVsv_flags's I32 flags arg. Alot of duplicate looking code between mortalcopy and newSVsv.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
defer-next-dev This PR should not be merged yet, but await the next development cycle
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants