http://www.perlmonks.org?node_id=927888

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

Why doesn't this work (ie. produce output)?

#! perl -slw use strict; use Inline C => Config => BUILD_NOISY => 1; use Inline C => <<'END_C', NAME => 'rand64fast', CLEAN_AFTER_BUILD => + 0; void rnd( int n ) { dSP; static __int64 y = 2463534242; EXTEND( SP, n ); while( --n ) { y ^= y << 13; y ^= y >> 17; y ^= y << 5; mPUSHi( (IV)y ); } return; } END_C print for rnd( 100 );

Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.

Replies are listed 'Best First'.
Re: XS: EXTEND/mPUSHi
by ikegami (Patriarch) on Sep 26, 2011 at 17:37 UTC
    • You need to use XSRETURN to specify how many items you placed on the stack. Basically, dSP; creates a local stack pointer and mPUSHi modifies this local pointer, so you need to copy this value back to the real stack pointer.

      Instead of figuring out what argument to use for XSRETURN, you can use PUTBACK. Unlike XSRETURN, it doesn't perform a return, so you need to follow it up with return if you use it in the middle of a function.

      XS would normally do this for you, but Inline::C forces you to do it (by using return; /* assume stack size is correct */).

      This was the purpose of Inline_Stack_Done.

    • The scalars passed to the sub are still on the stack, so you need to remove them unless you want to return them.

      In Perl ops, this would be done by popping them as they are read, but one doesn't tend to read them when using Inline. SP = MARK; will pop all of them.

      This was the purpose of Inline_Stack_Reset.

    • If rnd(100) should return 100 numbers, you need to use a post-decrement for your loop counter.

    void rnd( int n ) { static __int64 y = 2463534242; dXSARGS; int i; POPs; EXTEND( SP, n ); for (i = n; i--; ) { y ^= y << 13; y ^= y >> 17; y ^= y << 5; mPUSHi( (IV)y ); } XSRETURN(n); }
    or
    void rnd( int n ) { static __int64 y = 2463534242; dSP; dMARK; SP = MARK; // Remove args from stack. EXTEND( SP, n ); while (n--) { y ^= y << 13; y ^= y >> 17; y ^= y << 5; mPUSHi( (IV)y ); } PUTBACK; // Publish changes to stack. }

      Thanks for the very nice explanation. I was concerned about using PUTBACK myself as I thought t would be duplicating the one in the wrapper sub. On closer inspection I've realised that the one in the wrapper sub will never be reached:

      /* must have used dXSARGS; list context implied */ return; /* assume stack size is correct */ ### Her +e #line 128 "monkeys.c" PUTBACK; return; } }

      Which make me wonder why it (and the second return), are there in the first place?

      I'm also lost to understand the derivation of the name "PUTBACK". It certainly isn't intuitive (to me) as a term that means 'remember how many items were added to the stack'.


      Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
      "Science is about questioning the status quo. Questioning authority".
      In the absence of evidence, opinion is indistinguishable from prejudice.
        I agree. It's probably short for "put sp (aka SP) back into PL_stack_sp" (as it does PL_stack_sp = sp), but it's not a very useful name.
Re: XS: EXTEND/mPUSHi
by Util (Priest) on Sep 26, 2011 at 15:46 UTC

    Inline::C uses its own Inline_Stack_(Vars|Reset|Done) macros in place of XS's dSP|EXTEND macros. Inline_Stack_Push is used instead of mPUSHi, although you can still use the mXPUSHi form to simplify. See "#The_Inline_Stack_Macros" in Inline::C.

    The Inline docs say that you should be able to use the old macros, but I cannot see how to make that work.

    Having made the changes mentioned above, this code now works for me:

    #! perl -slw use strict; use Inline C => Config => BUILD_NOISY => 1; use Inline C => <<'END_C', NAME => 'rand64fast', CLEAN_AFTER_BUILD => + 0; void rnd( int n ) { // dSP; // static __int64 y = 2463534242; static int y = 2463534242; // My machine is 32-bit. // EXTEND( SP, n ); Inline_Stack_Vars; Inline_Stack_Reset; while( --n ) { y ^= y << 13; y ^= y >> 17; y ^= y << 5; // mPUSHi( (IV)y ); Inline_Stack_Push( sv_2mortal( newSViv(y) ) ); // mXPUSHi(y); // Could have used this instead. Note the added + X } // return; Inline_Stack_Done; } END_C print for rnd( 100 );
    Caveat: All my XS knowledge predates Inline::C, so any of the above could be flawed in minor ways.

      Thanks.

      Yes. I know it can be done using the ludicrously verbose, Camel_Case_And_Underscores inline stack macros. I was trying to understand why I can't use the neater and more concise XS macros from Inline C. I've found I can reduce the requirements to:

      #! perl -slw use strict; use Inline C => Config => BUILD_NOISY => 1; use Inline C => <<'END_C', NAME => 'monkeys', CLEAN_AFTER_BUILD => 0; void rnd64( int n ) { Inline_Stack_Vars; static unsigned __int64 y = 88172645463325252i64; EXTEND( SP, n ); while( n-- ) { y ^= y << 13; y ^= y >> 7; y ^= y << 17; mPUSHu( y ); } Inline_Stack_Done; return; }

      But looking at the C produced by the above, it looks like there is a path through the generated wrapper function that avoids both the XSRETURN_EMPTY and the PUTBACK, thus returning whatever has been pushed:

      XS(XS_main_rnd64); /* prototype to pass -Wmissing-prototypes */ XS(XS_main_rnd64) { #ifdef dVAR dVAR; dXSARGS; #else dXSARGS; #endif if (items != 1) croak_xs_usage(cv, "n"); PERL_UNUSED_VAR(ax); /* -Wall */ SP -= items; { int n = (int)SvIV(ST(0)); #line 46 "monkeys.xs" I32* temp; #line 117 "monkeys.c" #line 48 "monkeys.xs" temp = PL_markstack_ptr++; rnd64(n); 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 */ ### Her +e #line 128 "monkeys.c" PUTBACK; return; } }

      But I'm obviously missing something in the OP code that would cause (or allow) it to follow that path?


      Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
      "Science is about questioning the status quo. Questioning authority".
      In the absence of evidence, opinion is indistinguishable from prejudice.
        I know it can be done using the ludicrously verbose, Camel_Case_And_Underscores inline stack macros

        The camel case can be avoided - use either INLINE_STACK_VARS or inline_stack_vars instead of Inline_Stack_Vars (etc, etc ...).
        But Inline currently offers no alternative to the verbosity or the underscores - which is one of a number of reasons that I, too, often prefer to use the XS equivalents.

        If you ever want to check on what they are, these macros can be found in Inline/C.pm in the "Generate the INLINE.h file" section, or in any Inline-generated Inline.h that you can lay your hands on.

        I think these macros are mostly useful for beginners in that they provide a mantra that gets most jobs done - and despite their verbosity, are easier to remember than the corresponding list of XS symbols. (After a while away from doing any Inline::C or XS stuff, I'm flat out remembering whether I want to start my Inline::C script with "dSP" or "dXSARGS", but I can always remember "Inline_Stack_Vars" :-)

        Cheers,
        Rob