Skip to content

Commit 68191eb

Browse files
committed
mro.xs more SVPV COW HEK * optimizations
-S_mro_get_linear_isa_c3() dont call av_count() many times -the fn call mro_newSVsvhekok(), or any ISO C fn call located between static inlines newAV_alloc_xz() and av_push_simple() severely degrades or destroys any chance of anything const folding or any thing being removed by the CC for common sub exp elimination AV* isalin = newAV_alloc_xz(4); av_push_simple(isalin, mro_newSVsvhekok(classname)); Changing it to SV* nsv = mro_newSVsvhekok(classname); AV* isalin = newAV_alloc_xz(4); av_push_simple(isalin, nsv); fixes the problem. The CC is now allowed to fuse bottom half of newAV_alloc_xz() and top half of av_push_simple(). -"const I32 throw_nomethod = SvIVX(ST(1));" silence CC truncate warning
1 parent 09b7de2 commit 68191eb

File tree

1 file changed

+53
-22
lines changed

1 file changed

+53
-22
lines changed

ext/mro/mro.xs

Lines changed: 53 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ static struct mro_alg c3_alg =
1515
typedef struct {
1616
SV *sv_UNIVERSAL;
1717
SV *sv_dfs;
18+
SV *sv_c3;
1819
SV *sv_ISA;
1920
} my_cxt_t;
2021

@@ -27,6 +28,8 @@ init_MY_CXT(pTHX_ pMY_CXT)
2728
SvREADONLY_on(MY_CXT.sv_UNIVERSAL);
2829
MY_CXT.sv_dfs = newSVpvs_share("dfs");
2930
SvREADONLY_on(MY_CXT.sv_dfs);
31+
MY_CXT.sv_c3 = newSVpvn_share("c3", sizeof("c3")-1, c3_alg.hash);
32+
SvREADONLY_on(MY_CXT.sv_c3);
3033
MY_CXT.sv_ISA = newSVpvs_share("ISA");
3134
SvREADONLY_on(MY_CXT.sv_ISA);
3235
}
@@ -121,8 +124,9 @@ S_mro_get_linear_isa_c3(pTHX_ HV* stash, U32 level)
121124
if(!isa_item_stash) {
122125
/* if no stash, make a temporary fake MRO
123126
containing just itself */
127+
SV* nsv = mro_newSVsvhekok(isa_item);
124128
AV* const isa_lin = newAV_alloc_xz(4);
125-
av_push_simple(isa_lin, mro_newSVsvhekok(isa_item));
129+
av_push_simple(isa_lin, nsv);
126130
av_push_simple(seqs, MUTABLE_SV(isa_lin));
127131
}
128132
else {
@@ -202,11 +206,12 @@ S_mro_get_linear_isa_c3(pTHX_ HV* stash, U32 level)
202206
}
203207
}
204208
}
205-
209+
{
206210
/* Initialize retval to build the return value in */
207-
retval = newAV_alloc_xz(4);
208-
av_push_simple(retval, newSVhek(stashhek)); /* us first */
209-
211+
SV* nsv = newSVhek(stashhek);
212+
retval = newAV_alloc_xz(4);
213+
av_push_simple(retval, nsv); /* us first */
214+
}
210215
/* This loop won't terminate until we either finish building
211216
the MRO, or get an exception. */
212217
while(1) {
@@ -280,14 +285,12 @@ S_mro_get_linear_isa_c3(pTHX_ HV* stash, U32 level)
280285
/* If we had candidates, but nobody won, then the @ISA
281286
hierarchy is not C3-incompatible */
282287
if(!winner) {
283-
SV *errmsg;
284-
Size_t i;
285-
286-
errmsg = newSVpvf(
288+
SV *errmsg = newSVpvf(
287289
"Inconsistent hierarchy during C3 merge of class '%" HEKf "':\n\t"
288290
"current merge results [\n",
289291
HEKfARG(stashhek));
290-
for (i = 0; i < av_count(retval); i++) {
292+
SSize_t count = av_count(retval);
293+
for (SSize_t i = 0; i < count; i++) {
291294
SV **elem = av_fetch(retval, i, 0);
292295
sv_catpvf(errmsg, "\t\t%" SVf ",\n", SVfARG(*elem));
293296
}
@@ -303,9 +306,12 @@ S_mro_get_linear_isa_c3(pTHX_ HV* stash, U32 level)
303306
}
304307
}
305308
else { /* @ISA was undefined or empty */
309+
/* Do this 1st, the next 2 AV* API calls are likely to be inlined and
310+
optimize away alot of AvFILL/memset/Renew logic if nothing is between them. */
311+
SV* nsv = newSVhek(stashhek);
306312
/* build a retval containing only ourselves */
307313
retval = newAV_alloc_xz(4);
308-
av_push_simple(retval, newSVhek(stashhek));
314+
av_push_simple(retval, nsv);
309315
}
310316

311317
done:
@@ -362,6 +368,9 @@ PPCODE:
362368
sv = MY_CXT.sv_dfs;
363369
MY_CXT.sv_dfs = NULL;
364370
SvREFCNT_dec_NN(sv);
371+
sv = MY_CXT.sv_c3;
372+
MY_CXT.sv_c3 = NULL;
373+
SvREFCNT_dec_NN(sv);
365374
sv = MY_CXT.sv_ISA;
366375
MY_CXT.sv_ISA = NULL;
367376
SvREFCNT_dec_NN(sv);
@@ -385,8 +394,9 @@ mro_get_linear_isa(...)
385394

386395
if(!class_stash) {
387396
/* No stash exists yet, give them just the classname */
397+
SV* nsv = mro_newSVsvhekok(classname);
388398
AV* isalin = newAV_alloc_xz(4);
389-
av_push_simple(isalin, mro_newSVsvhekok(classname));
399+
av_push_simple(isalin, nsv);
390400
ST(0) = sv_2mortal(newRV_noinc(MUTABLE_SV(isalin)));
391401
XSRETURN(1);
392402
}
@@ -430,6 +440,7 @@ mro_get_mro(...)
430440
SV* classname;
431441
HV* class_stash;
432442
SV* retsv;
443+
U32 which_my_cxt; /* rel offset MY_CXT, prevents 2 sep dMY_CXT deref lines */
433444
PPCODE:
434445
if (items != 1)
435446
croak_xs_usage(cv, "classname");
@@ -439,12 +450,28 @@ mro_get_mro(...)
439450

440451
if (class_stash) {
441452
const struct mro_alg *const meta = HvMROMETA(class_stash)->mro_which;
442-
retsv = newSVpvn_flags(meta->name, meta->length,
443-
SVs_TEMP
444-
| ((meta->kflags & HVhek_UTF8) ? SVf_UTF8 : 0));
453+
if (memEQs(meta->name, meta->length, "dfs")) /* skipping meta->kflags & HVhek_UTF8 */
454+
goto ret_dfs;
455+
/* "c3" shows up here running mro's .t'es */
456+
else if (memEQs(meta->name, meta->length, "c3")) {
457+
which_my_cxt = STRUCT_OFFSET(my_cxt_t, sv_c3);
458+
goto ret_my_cxt_hek;
459+
}
460+
else { /* pretty sure this string already exists inside PL_strtab by now */
461+
I32 i32len = meta->kflags & HVhek_UTF8 ? -(I32)meta->length : (I32)meta->length;
462+
retsv = sv_2mortal(newSVpvn_share(meta->name, i32len, meta->hash));
463+
}
445464
} else {
446-
dMY_CXT;
447-
retsv = newSVhek_mortal(SvSHARED_HEK_FROM_PV(SvPVX(MY_CXT.sv_dfs)));
465+
ret_dfs:
466+
which_my_cxt = STRUCT_OFFSET(my_cxt_t, sv_dfs);
467+
468+
ret_my_cxt_hek:
469+
{
470+
dMY_CXT;
471+
SV** svp = NUM2PTR(SV**,(PTR2nat(&MY_CXT)+which_my_cxt));
472+
SV* svhek = *svp;
473+
retsv = newSVhek_mortal(SvSHARED_HEK_FROM_PV(SvPVX(svhek)));
474+
}
448475
}
449476
PUSHs(retsv);
450477

@@ -547,7 +574,7 @@ void
547574
mro__nextcan(...)
548575
PREINIT:
549576
SV* self = ST(0);
550-
const I32 throw_nomethod = SvIVX(ST(1));
577+
const bool throw_nomethod = cBOOL(SvIVX(ST(1)));
551578
I32 cxix = cxstack_ix;
552579
const PERL_CONTEXT *ccstack = cxstack;
553580
const PERL_SI *top_si = PL_curstackinfo;
@@ -650,8 +677,10 @@ mro__nextcan(...)
650677
/* Initialize the next::method cache for this stash
651678
if necessary */
652679
selfmeta = HvMROMETA(selfstash);
653-
if(!(nmcache = selfmeta->mro_nextmethod)) {
654-
nmcache = selfmeta->mro_nextmethod = newHV();
680+
nmcache = selfmeta->mro_nextmethod;
681+
if (!nmcache) {
682+
nmcache = newHV();
683+
selfmeta->mro_nextmethod = nmcache;
655684
}
656685
else { /* Use the cached coderef if it exists */
657686
HE* cache_entry = hv_fetch_ent(nmcache, sv, 0, 0);
@@ -674,8 +703,10 @@ mro__nextcan(...)
674703
/* beyond here is just for cache misses, so perf isn't as critical */
675704

676705
stashname_len = subname - fq_subname - 2;
677-
stashname = newSVpvn_flags(fq_subname, stashname_len,
678-
SVs_TEMP | (subname_utf8 ? SVf_UTF8 : 0));
706+
{ /* strs like "Qux::foo" "TTop::foo" show up here */
707+
I32 i32len = subname_utf8 ? -(I32)stashname_len : (I32)stashname_len;
708+
stashname = sv_2mortal(newSVpvn_share(fq_subname, i32len, 0));
709+
}
679710

680711
/* has ourselves at the top of the list */
681712
linear_av = S_mro_get_linear_isa_c3(aTHX_ selfstash, 0);

0 commit comments

Comments
 (0)