syphilis has asked for the wisdom of the Perl Monks concerning the following question:

Hi,

For this perl (Inline::C) script:
### try.pl ### use strict; use warnings; use Inline C => Config => FORCE_BUILD => 1, BUILD_NOISY => 1, CLEAN_AFTER_BUILD => 0, ; use Inline C => <<'EOC'; void foo(SV * x, ...) { dXSARGS; int i, ret = 0; for(i = 0; i < items; i++) { ret += (int)SvIV(ST(i)); } printf("%d\n", ret); XSRETURN(0); } EOC # Apart from the building output, this script # finally outputs -5 (== 1 + 2 + 3 - 11) foo(1,2,3,-11);
Inline::C will generate the following XS file:
#include "EXTERN.h" #include "perl.h" #include "XSUB.h" #include "INLINE.h" void foo(SV * x, ...) { dXSARGS; int i, ret = 0; for(i = 0; i < items; i++) { ret += (int)SvIV(ST(i)); } printf("%d\n", ret); XSRETURN(0); } MODULE = try_pl_22db PACKAGE = main PROTOTYPES: DISABLE void foo (x, ...) SV * x PREINIT: I32* temp; PPCODE: temp = PL_markstack_ptr++; foo(x); if (PL_markstack_ptr != temp) { /* truly void, because dXSARGS not invoked */ PL_markstack_ptr = temp; XSRETURN_EMPTY; /* return empty stack */ } /* must have used dXSARGS; list context implied */ return; /* assume stack size is correct */
(That Inline.h file is automatically included. It's not used or needed in this case, and can be ignored.)

It's the PL_markstack_ptr dance with the temp pointer at the end that I'm particularly interested in - with an aim to avoiding it.
Some "void" functions will return nothing, others will return values off the stack.
Inline::C performs that dance to determine whether this void function is returning something, or whether it is "truly void".

I use Inline::C to generate quite a few XS files, and I'm hoping to avoid having to include this dance, especially since it started spitting out warnings in bleadperl recently (but since fixed).
It's not something you see in other XS files, and I figure it shouldn't be too hard to avoid.
The one thing I really don't understand is the need to restore the value of PL_markstack_ptr to the value held by temp (PL_markstack_ptr = temp;), prior to returning empty.

To date, I've had good success in removing this dance. For "truly void" functions that invoke dXSARGS (such as the given example), the implementation at the end of that XS file can be rewritten as
void foo (x, ...) SV * x CODE: PL_markstack_ptr++; foo(x); XSRETURN_EMPTY;
However, in this instance, PL_markstack_ptr has not been reset to the value it held prior to its incrementation.
Under what circumstances might that omission bite ?
I think (needs double checking) that, with the way I've constructed foo(), PL_markstack_ptr does need to be incremented - but I'd prefer not to perform that original dance if I don't have to.
UPDATE: Just double checked and found an instance of a very similar function that does require that PL_markstack_ptr is incremented.

Cheers,
Rob

Replies are listed 'Best First'.
Re: [XS] Manipulating the Stack
by NERDVANA (Priest) on Oct 17, 2023 at 00:52 UTC

    I don't know the semantics of PL_markstack_ptr, but I would guess that it's an internal detail that might be subject to change in the future. If it does, I'd be happy to let the Inline::C maintainers deal with that and not have to re-release all my Inline-derived modules. While the XS you point at might look awkward, that only comes out to about 4 processor instructions more than your improved version, so I doubt there is any performance difference.

    If you think there is a reliable way to avoid that dance, maybe contribute a patch to Inline::C instead? I mean, Inline can look at the C source code and try parsing it to find out if it is actually pushing things onto the stack.... maybe.

      I don't know the semantics of PL_markstack_ptr, but I would guess that it's an internal detail that might be subject to change in the future.

      It's documented in perlguts, though reading that documentation has not helped me any.
      I think that makes it public - at least public enough that any alteration to its usage will be outlined in perlguts. And it has been in Inline::C and Inline::CPP for a very long time, without ever causing problems.

      For me, it's more about removing arcane clutter that is deployed only by Inline::C/CPP, rather than looking for improved running performance.
      Many of the "truly void" functions (eg those that don't invoke dXSARGS) don't even need to increment the ptr - so, for them:
      void foo (x) SV * x PREINIT: I32* temp; PPCODE: temp = PL_markstack_ptr++; foo(x); if (PL_markstack_ptr != temp) { /* truly void, because dXSARGS not invoked */ PL_markstack_ptr = temp; XSRETURN_EMPTY; /* return empty stack */ } /* must have used dXSARGS; list context implied */ return; /* assume stack size is correct */
      can be rewritten as:
      void foo (x) SV * x CODE: foo(x); XSRETURN_EMPTY;
      which is much more readily comprehensible, and much more concise.

      See https://github.com/Perl/perl5/issues/21523 where the issue was first reported. And also see https://github.com/ingydotnet/inline-c-pm/issues/104 for a bit of a discussion, and for Tony Cook's solution that suppresses the warnings shown in the former link.
      I've actually started using a hacked Inline::C that eliminates the need for relying on "the PL_markstack_ptr dance". (It's based on the idea mentioned in that bug report - though it required some additional refining not mentioned there ... and it's probably not everyone's preferred approach.)

      Cheers,
      Rob