develooper Front page | perl.perl5.porters | Postings from February 2017

Stack refcount synopsis

Thread Next
From:
hv
Date:
February 9, 2017 14:59
Subject:
Stack refcount synopsis
Message ID:
201702091451.v19EpXP15268@crypt.org
There's bits and pieces of discussion over the years scattered all over
the place. I started trying to pull them together, with the intent of
organising them into some sort of synopsis, but it's proving hard.

Before I put too much effort into this, has anyone already started such
a thing (or more), or have a suggestion for how and where such a thing
could be done sanely?

In particular, I had hoped to make up for the limitations of RT by
rearranging some of the conversations in more of a Mediawiki style, but
by the time I got to [perl #114372] it quickly became too complex even
for that.

Lots of thinking has gone into this over the years, and I fear much of
it has been duplicate, and more will be.

Below is where I got to before stalling, fwiw.

Hugo
---
Meta ticket: {{perl|77706}}

On 2012-03-28 in {{perl|18489}}:
:I have thought about ways to make the stack reference-counted, and it might actually be doable in the end. (Father Chrysostomos)

On 2004-09-01 in {{perl|31403}}:
:I've often wondered if it would actually *increase* efficiency to start refcounting the stack. We go to an awful lot of work to avoid doing so. (Yitzchak Scott-Thoennes)
::yes, such as adding half the stuff we push onto the tmps stack too, just so we are sure they get freed at the end.
::If it was just a case of changing the various PP macros, I'd consider trying it. But I think in practice it would break a whole bunch of XS code. (Dave Mitchell)
:::If we've done things right, it should in principle be possible to leave a legacy interface to allow old XS code to continue working and support a new way of doing things with a new set of macros. Have we done things as right as that?
:::I'd be quite interested to see what sort of improvements we might be able to get with this approach, but unless a compatibility mode is possible without throwing away all the theoretical advantages it seems like a lot of work to get a small amount of extra data - I suspect it would be difficult to remove the old macros even after an extensive deprecation cycle. (Hugo van der Sanden)

On 2007-07-04 in {{perl|43501}}:
:I envison duplicating the stack frame for old XS - ie just before calling into an XS function, do a block copy of the top N addresses on the stack - so the function gets its own non-refcounted list of SVs to play with and dispose of. (Dave Mitchell)

On 2012-09-05 in {{perl|114372}}:
:Simply making the stack ref-counted would break XS modules. So we would have to provide two stacks, and switch over to the 'compatibility' stack whenever calling XSUBs. The same may have to apply to custom ops.
:All Perl operators take care of stack manipulation themselves. So they would all need to be changed. Presumably we would have to create a new set of macros to manipulate the 'new stack'. Then all uses of the old macros will have to switch over to the new--not a trivial task, as the new macros would be used differently. Everything would have to be audited carefully to avoid memory leaks or double frees. (Father Chrysostomos)
::At least for XSUBs that weren't flagged as expecting the "new stack" semantics. Which would be all of them initially. (Nicholas Clark)
:::And making the stack refcounted would be a major, major slow-down. Let's not forget that. (Steffen Mueller)
::::I wouldn't assume that without actually testing it. Certainly not "major major". There is currently a lot of to-and-fro with the mortals stack to ensure that SVs on the (main) stack are referenced somewhere.
::::All that would be abolished with a properly reference counted stack. So it's not purely a speed loss. (Nicholas Clark)
:::::Data point: python has a refcounted stack, and that doesn't seem to have mortally wounded its performance. (Leon Timmermans)
::I suspect we can provide a compatibility API that DWIMs on both systems for most common usage patterns. It would still be a lot of work, but it could easy such a transition. (Leon Timmermans)
:::If we have a refcounted stack, allowing existing XS subs to operate is fairly trivial. If an XS sub isn't marked as being 'refcounted stack aware', we simply memcopy the existing stack frame to make a new frame that contains the same SV pointers, but not refcount-imcremented. After the XSUB returns, we decrement the refcounts of the all the SVs in the original stack frame, then shuffle down the returned list, obliterating the original stack frame.
:::So the only extra cost to support an old-style XSUB over a new-style one is two block copies: one an array of pointers equal to the size of the arg list, and one equal to the size of the return list. (Dave Mitchell)
:::E.g., a POPs that mortalizes the value when removing it from the stack, instead of freeing it. It would still behave as expected, though it would be less efficient.
:::But having it work only for the 'most common usage patterns' is not acceptable, considering that all these macros have long been part of the public API. (Father Chrysostomos)
::::Yes, it can be made to work trivially for XSUBs. But what about custom pp functions? There seem to be more and more modules these days that use them, so we do need some way to have things work under both systems.
:::::There aren't too many at the moment. If an incompatible change is needed, I'm willing to update all of mine for the new way. We can hold the authors of pp functions to higher standards than general XS authors. But if the pp interface changes incompatibly, we should change the name of the op_ppaddr struct member, to catch the modules that haven't updated. (Same deal as with op_sibling.) (Zefram)
::::::For op_sibling, we provided a transitional period in which defining PERL_OP_PARENT would flag which modules needed updated, without them breaking just yet.
::::::A simple define will not be practical, because perl's own pp functions would be littered with #ifdefs.
::::::The only way I can think of providing a transitional phase is to
  #define op_ppaddr op_rppaddr /* or whatever we call it */
::::::and provide a define that disables that. Affected modules will leak memory, but it may not be the end of the world. We will have a define to catch them.
::::::dSP would have to be redefined to use the refcounted stack. eupxs uses something other than dSP for the old stack.
::::::It's ugly. It probably breaks some XS modules that do dSP on their own. (Father Chrysostomos)
::::What is particularly difficult is that the same .xs file may have pp functions and XSUBs. If POPs in an XSUB is old-style, it has to be defined as (*sp--). pp functions have to use whichever stack perl itself uses, so that would be sv_2mortal(*sp--) on a newer perl. 
::::How can we have POPs defined both ways in the same file? 
:::::Wrong question. pp functions require more update than just different macro definitions, because they may access the stack directly or do other things that rely on actually knowing the refcounting scheme. Given the need for more substantial revision, the new stack interface could perfectly well use new macro names.
:::::Though if you're concerned about mixing pp styles in one file, the same issue arises with just xsubs. It doesn't seem a good idea for the "I know about the refcounted stack" flag to be per module; that breaks moving an xsub definition between XS files. It should be a per-xsub flag in the source. (Zefram)
:::::Like this...
:::::Core headers define macros to operate both ways based on a switch:
  #define POPs (PERL_STACK_LOCALLY_REFCOUNTED ? \
      sv_2mortal(*sp--) : (*sp--))
  #define PUSHs(s) (PERL_STACK_LOCALLY_REFCOUNTED ? \
      (*++sp = SvREFCNT_inc(s)) : (*++sp = (s)))
  #define TOPs (*sp) /* same either way */
:::::Core headers also define a macro saying how the stack really behaves in the core:
  #define PERL_STACK_GLOBALLY_REFCOUNTED 1 /* or 0 */
:::::Code that needs to be portable to older Perl versions then has a reserve definition:
  #ifndef PERL_STACK_GLOBALLY_REFCOUNTED
  # define PERL_STACK_GLOBALLY_REFCOUNTED 0
  #endif
:::::There's then some dance around redefining PERL_STACK_LOCALLY_REFCOUNTED. I'm not sure whether the `normal' state for it should be, but it's clear how it needs to be defined for the compilation of any pp code. For pp code that must operate on the native stack, including the whole core and probably all custom op pp functions, we define:
  #define PERL_STACK_LOCALLY_REFCOUNTED PERL_STACK_GLOBALLY_REFCOUNTED
:::::When compiling an XS definition of an xsub, how to define it depends on declarations made in the source. By default, the xsub code is only expected to be correct for an uncounted stack, so we define:
  #define PERL_STACK_LOCALLY_REFCOUNTED 0
:::::and if the core stack is refcounted then we also invoke the magic to copy the stack back and forth between the two conventions. If the xsub code declares that it can operate with either stack type, then we have the switch macro defined as for a native pp function, and do not apply any copying magic.
:::::On Perl versions predating this mechanism, with the above reserve definition of PERL_STACK_GLOBALLY_REFCOUNTED in place, both of the above settings of PERL_STACK_LOCALLY_REFCOUNTED are equivalent, which is just as well because the POPs et al macros don't pay attention to it and always act as if it is set that way.
:::::Theoretically we could also have xsub code that will only operate with a refcounted stack. To do this we define the switch macro to 1, and if the core stack is not refcounted then we invoke the inverse stack copying magic. If this is to be portable to core versions predating the switch, the XS framework would have to redefine POPs et al in the switch-sensitive way, and the stack copying magic would have to come from the XS framework rather than a core feature.
:::::With the switch in place, the usual way to write pp functions would be to cope with both styles, not to write only for a refcounted stack. Most pp functions wouldn't need any edit from their current form, because POPs et al are conceptually equivalent across both styles. Code that performs custom stack work, such as bulk copying between an AV and the stack, would sometimes need conditional code, guarded by "if(PERL_STACK_LOCALLY_REFCOUNTED)" or equivalent. (The switch macro can also be used in a #if, but that's more cumbersome than a true C conditional.) (Zefram)
::::Alternatively, if we use new macro names, then the pp function would have to use rPOPs on newer perls and POPs on older perls, which is a major API breakage. 
:::::The simpler pp functions and xsub bodies, which work equivalently on either pp style, can just do
  #ifdef rPOPs
  # define myPOPs rPOPs
  #else
  # define myPOPs POPs
  #endif
:::::and xsub source can likewise have a flag state saying that the code works either way. (Zefram)
::::Another thought I had is that a final pass when compiling a sub could wrap custom pp functions with a wrapper that copies things to a new stack and copies them back, just as we would do for XSUBs. I don't like it, but it may be the only way. Though I still don't think it would work for custom pp functions that wrap perl's own pp functions.
:::::No, you can't do that, at least not the way you would for an xsub. The portion of the stack that an xsub is liable to operate on is objectively delimited by the top mark at the moment it is called, so you'd only need to copy that part of the stack back and forth, and it must pop exactly one mark. A pp function has no such restriction: it might not pop a mark, might even push one, might use arguments beyond a mark, and so on. So to provide compatibility to an old pp functions you'd need to copy the whole stack back and forth, which sounds much worse. (Zefram)
::::I'm stuck. (Father Chrysostomos)

Thread Next


nntp.perl.org: Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at ask@perl.org | Group listing | About