Found the crib I was looking for in pp_anonlist in pp.c
dVAR; dSP; dMARK;
const I32 items = SP - MARK;
SV * const args = MUTABLE_SV(av_make(items, MARK+1));
SPAGAIN;
mXPUSHs(newRV_noinc(args));
It took me a great number of tries before I finally settled on this:
#define NUMARGS (SP - (PL_stack_base + TOPMARK))
AV *
Perl_get_args(pTHX) {
dSP;
AV * args;
args = av_make(NUMARGS, SP - NUMARGS + 1);
return args;
}
This is similar to pp_anonlist, but not quite. dMARK expands to SV **mark = PL_stack_base + (*PL_markstack_ptr--)
. MARK is used extensively but is ill defined in the documentation. Because dMARK modifies the state of the stack, it is unusable in my function which should have no side effects. TOPMARK is just *PL_markstack_ptr, no decrement. NUMARGS is effectively SP - MARK
without the side effect.
SP points at the top of the stack, but av_make() works on lists. So it's necessary to pass in SP - NUMARGS
to ensure av_make() can read two items off the stack. Why its necessary to add one, I'm not sure.