Skip to content
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

S_fold_constants: remove early SvREADONLY(sv) to allow SvIsCOW(sv) #20595

Merged
merged 1 commit into from
Jun 11, 2024

Conversation

richardleach
Copy link
Contributor

Standard CONST PVs have the IsCOW flag set, meaning that COW can be used when assigning the CONST to a variable, rather than making a copy of the buffer. CONST PVs arising from constant folding have been lacking this flag, leading to unnecessary copying of PV buffers.

This seems to have occurred because a common branch in S_fold_constants marks SVs as READONLY before the new CONST OP is created. When the OP is created, the Perl_ck_svconst() check function is called - this is the same as when a standard CONST OP is created. If the SV is not already marked as READONLY, the check function will try to set IsCOW if it is safe to do so, then in either case will make sure that the READONLY flag is set.

This commit therefore removes the SvREADONLY(sv) statement from S_fold_constants(), allowing Perl_ck_svconst() to set the IsCOW and READONLY flags itself. Minor test updates are also included.

This is a possible method for addressing part of #20586. (This PR does not address the point about not constant folding when the resulting constant would be too large.)

Copy link
Collaborator

@demerphq demerphq left a comment

Choose a reason for hiding this comment

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

IM a touch concerned we should know why we stopped doing this in 5.20. Are we walking into a trap we should know about?

Im approving anyway, but imo a comment in the PR thread explaining why we turned this off in the past, and why its ok to turn it back on now would be good.

@@ -575,7 +575,7 @@ do_test('reference to hash containing Unicode',
PV = $ADDR "' . $cp200_bytes . '"\\\0 \[UTF8 "\\\x\{200\}"\]
CUR = 2
LEN = \\d+
COW_REFCNT = 1 # $] < 5.019007
COW_REFCNT = 1 # $] < 5.019007 || $] >=5.037007
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do we know why we stopped doing this in 5.019? That suggests there is a trap we need to be wary of no?

@richardleach
Copy link
Contributor Author

IM a touch concerned we should know why we stopped doing this in 5.20. Are we walking into a trap we should know about?

Yes, I don't know whether it was a deliberate omission or a bug that then was codified in Peek.t. Am hoping that @iabyn might know the answer to this. Will not merge until he has a chance to weigh in.

@iabyn
Copy link
Contributor

iabyn commented Dec 12, 2022 via email

@richardleach
Copy link
Contributor Author

It looks like swiping the string buffer from PADTMPs is still preferable to COWing in some cases, so those two expressions suddenly becoming COW might indicate pad-swiping is inadvertently being disabled. Or it might be that the disabling happened by accident (or design) some time after 5.20 and no one noticed, since the Peek test doesn't distinguish between not COWing and not pad swiping.

I can definitely envisage buffer swiping being preferable to COW in some circumstances, such as the example given at the top of FC's commit.

However:

  • There seems to be no reason why simple CONSTs are IsCOW and folded CONSTs are not. Surely what's best for one is best for the other? Unless I'm just not seeing the reason, still feels like there's a bug.
  • [In blead] In $a = "$b$c" the RHS flags are (PADTMP,POK,pPOK) and pass the swiping test in Perl_sv_setsv_flags(). In my $x = "A"x10 the flags are (PADTMP,POK,READONLY,PROTECT,pPOK) and fail the swiping test, so a full buffer copy occurs.

I can't see how the PV of a constant can ever be swiped, so was the effect of FC's commit on folded constants unintentional, or is there still an argument for leaving things as-is? (Assignment of a folded constant to a PADTMP, via buffer copy, and then in subsequent operations it's beneficial to be able to swipe the PV buffer??)

@iabyn
Copy link
Contributor

iabyn commented Dec 13, 2022 via email

@Leont
Copy link
Contributor

Leont commented Dec 13, 2022

s/SvREADME/SvREADONLY/ ;-)

@richardleach richardleach changed the title S_fold_constants: remove early SvREADME(sv) to allow SvIsCOW(sv) S_fold_constants: remove early SvREADONLY(sv) to allow SvIsCOW(sv) Dec 13, 2022
@richardleach
Copy link
Contributor Author

@Leont - thanks for spotting the typo.
@iabyn - I've added a commit to turn the PADTMP flag off for folded SVs. Such SVs will still have the READONLY flag set, of course, and won't be eligible for buffer swiping.

IOW the set of flags on a const folded SV with this PR would now be:

  FLAGS = (POK,IsCOW,READONLY,PROTECT,pPOK)

@richardleach
Copy link
Contributor Author

I forgot to check tests. 🤦‍♂️ Will look tonight.

@richardleach
Copy link
Contributor Author

The test failures when PADTMP is removed seem to relate to folding of references to the consts.

In t/op/join.t, there's for(1,2) { push @_, \join "x", 1 }, which in blead gives:

e                 <@> push[t4] vK/2 ->f
                     ...
d                    <1> srefgen sK/1 ->e
-                       <1> ex-list lKRM ->d
c                          <$> const[PV "1"] sRM/FOLD ->d

but without PADTMP yields:

                     <@> push[t3] vK/2 ->e
                     ...
c                    <$> const(IV \"1") s/FOLD ->d

Folding of references into literal constants also seems a bit funky. Have not had enough time to dig more.

@jkeenan
Copy link
Contributor

jkeenan commented Jan 17, 2023

@richardleach, this pull request has languished since last month. In addition, it failed tests in the GH CI "sanity check" run, so it hasn't been through most of the CI runs. If you want to pursue this p.r., could you address those failures? Thank you very much.

@richardleach
Copy link
Contributor Author

@jkeenan - This has languished because of a lack of time on my part and uncertainty as to whether there were unintentional side-effects in 9ffd39a (as discussed above.)

The logical next step is to figure out what function is producing (what seem to be) invalid OP trees when the PADTMP flag is removed. I'll try to work on this in the coming month or so.

@richardleach
Copy link
Contributor Author

Tests that fail when the PADTMP flag is removed seem to do so because of the assumption that the result of folding is as mutable or non-static as the result of the unfolded OPs would have been. That is to say, there is an expectation that folded CONSTs behave differently to non-folded CONSTs.

For example, the failing test in t/comp/old.t, boils down to the difference between: $ perl -MDevel::Peek -e 'for (1+2) { Dump($_) }' and $ perl -MDevel::Peek -e 'for (3) { Dump($_) }' Because of the PADTMP flag, the former outputs the following in blead:

SV = IV(0x5613452834c8) at 0x5613452834d8
  REFCNT = 1
  FLAGS = (IOK,pIOK)
  IV = 3

and the latter gives:

SV = IV(0x5605e40b8118) at 0x5605e40b8128
  REFCNT = 2
  FLAGS = (IOK,READONLY,PROTECT,pIOK)
  IV = 3

With the PADTMP flag removed, both give the latter result and the test fails because $_ cannot be modified.

And as already mentioned a couple of messages above, t/op/join.t has a failing test which boils down to for(1,2) { push @_, \join "x", 1 } and the PADTMP flag controls whether the resulting OP tree looks like this (blead):

d                    <1> srefgen sK/1 ->e
-                       <1> ex-list lKRM ->d
c                          <$> const[PV "1"] sRM/FOLD ->d

or this (patched):

c                    <$> const(IV \"1") s/FOLD ->d

Specifically, this test in S_fold_constants is affected:

    case OP_SREFGEN:
        if (cUNOPx(cUNOPo->op_first)->op_first->op_type != OP_CONST
         || SvPADTMP(cSVOPx_sv(cUNOPx(cUNOPo->op_first)->op_first)))
            goto nope;

With a literal constant, e.g. for(1,2) { push @_, \1 }, the result is always a single CONST OP where the SV contains a RV to a SV. The test fails because while it seems to be expected that a reference to a literal constant always returns the exact same RV, the expectation is that a reference to a folded constant gives a different RV each time.

Coming back to iabyn's comment from earlier:

A PADTMP SV is just a private destination for intermediate ops, so in
$a = $b + $c*$d, the multiply op will have a PADTMP reserved for it to
store the result of $c*$d, so it doesn't have to allocate and mortalise an
SV for that purpose. Similarly, the add op has a PADTMP to store the
result of the addition, which is then copied to $a by the assign op.

It seems like the expected behaviour for constant-folded SVs is that they behave like intermediate SVs that would have otherwise been produced by OPs. The way that PADTMP and READONLY are both handled within the assign OPs is what allows that to happen.

So I'm suggesting that:

  1. Having PADTMP on folded constants isn't a bug after all. The second commit in the PR should be removed.
  2. 9ffd39a was all about the efficiency of pad-swiping rather than COW, the buffer from a folded CONST SV will never be swipable because of the READONLY flag. In blead, my $x = <some_folded_pv> is a string copy operation because of the absence of IsCOW. I think the first commit in the PR is still useful and doesn't fly in the face of what FC was doing.

@iabyn or anyone else, thoughts?

@richardleach
Copy link
Contributor Author

Looking back at this after nearly a year, I can't do anything with it without a steer on whether (1) PADTMP is undesirable on folded constants and therefore the existing core tests noted above are actually wrong (2) PADTMP is desirable.

Either way, I still hold with the note that 9ffd39a was all about the efficiency of pad-swiping rather than COW, and the buffer from a folded CONST SV will never be swipable because of the READONLY flag. In blead, my $x = <some_folded_pv> is a string copy operation because of the absence of IsCOW. Should we decide that PADTMP should be on folded constants, that doesn't fly in the face of what FC was doing in 9ffd39a.

@iabyn
Copy link
Contributor

iabyn commented Feb 12, 2024 via email

@richardleach
Copy link
Contributor Author

Thanks @iabyn. I'll update the PR tonight/soon and mark it defer-next-dev.

@richardleach richardleach added the defer-next-dev This PR should not be merged yet, but await the next development cycle label Feb 12, 2024
@richardleach
Copy link
Contributor Author

Changes made as discussed. The Devel/Peek tests deliberately won't pass until we start the next development cycle.

@jkeenan jkeenan removed the defer-next-dev This PR should not be merged yet, but await the next development cycle label Jun 10, 2024
Standard CONST PVs have the IsCOW flag set, meaning that COW can
be used when assigning the CONST to a variable, rather than making
a copy of the buffer. CONST PVs arising from constant folding have
been lacking this flag, leading to unnecessary copying of PV buffers.

This seems to have occurred because a common branch in S_fold_constants
marks SVs as READONLY before the new CONST OP is created. When the OP
is created, the Perl_ck_svconst() check function is called - this is
the same as when a standard CONST OP is created. If the SV is not
already marked as READONLY, the check function will try to set IsCOW
if it is safe to do so, then in either case will make sure that the
READONLY flag is set.

This commit therefore removes the SvREADONLY(sv) statement from
S_fold_constants(), allowing Perl_ck_svconst() to set the IsCOW
and READONLY flags itself. Minor test updates are also included.
@richardleach richardleach merged commit 06e421c into Perl:blead Jun 11, 2024
29 checks passed
@richardleach richardleach deleted the hydahy/COW_consts branch June 11, 2024 20:20
@richardleach richardleach restored the hydahy/COW_consts branch June 11, 2024 20:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants