@@ -146,6 +146,7 @@ static const uint8_t* disassemble_instruction(const uint8_t* ip) {
146146 VECTOR_DISASSEMBLE (kRemainderF , " remainderf" )
147147 VECTOR_DISASSEMBLE (kRemainderS , " remainders" )
148148 VECTOR_DISASSEMBLE (kRemainderU , " remainderu" )
149+ case ByteCodeInstruction::kReserve : printf (" reserve %d" , READ8 ()); break ;
149150 case ByteCodeInstruction::kReturn : printf (" return %d" , READ8 ()); break ;
150151 case ByteCodeInstruction::kScalarToMatrix : {
151152 int cols = READ8 ();
@@ -334,6 +335,7 @@ struct StackFrame {
334335 const uint8_t * fCode ;
335336 const uint8_t * fIP ;
336337 VValue* fStack ;
338+ int fParameterCount ;
337339};
338340
339341static F32 mix (F32 start, F32 end, F32 t) {
@@ -404,19 +406,16 @@ void innerRun(const ByteCode* byteCode, const ByteCodeFunction* f, VValue* stack
404406 break ;
405407
406408 case ByteCodeInstruction::kCall : {
407- // Precursor code has pushed all parameters to the stack. Update our bottom of
408- // stack to point at the first parameter, and our sp to point past those parameters
409- // (plus space for locals).
409+ // Precursor code reserved space for the return value, and pushed all parameters to
410+ // the stack. Update our bottom of stack to point at the first parameter, and our
411+ // sp to point past those parameters (plus space for locals).
410412 int target = READ8 ();
411413 const ByteCodeFunction* fun = byteCode->fFunctions [target].get ();
412414 if (skvx::any (mask ())) {
413- frames.push_back ({ code, ip, stack });
415+ frames.push_back ({ code, ip, stack, fun-> fParameterCount });
414416 ip = code = fun->fCode .data ();
415417 stack = sp - fun->fParameterCount + 1 ;
416418 sp = stack + fun->fParameterCount + fun->fLocalCount - 1 ;
417- } else {
418- sp -= fun->fParameterCount ;
419- sp += fun->fReturnCount ;
420419 }
421420 break ;
422421 }
@@ -715,6 +714,10 @@ void innerRun(const ByteCode* byteCode, const ByteCodeFunction* f, VValue* stack
715714 VECTOR_BINARY_FN (kRemainderS , fSigned , vec_mod<I32>)
716715 VECTOR_BINARY_FN (kRemainderU , fUnsigned , vec_mod<U32>)
717716
717+ case ByteCodeInstruction::kReserve :
718+ sp += READ8 ();
719+ break ;
720+
718721 case ByteCodeInstruction::kReturn : {
719722 int count = READ8 ();
720723 if (frames.empty ()) {
@@ -742,14 +745,17 @@ void innerRun(const ByteCode* byteCode, const ByteCodeFunction* f, VValue* stack
742745 }
743746 return ;
744747 } else {
745- // When we were called, 'stack' was positioned at the old top-of-stack (where
746- // our parameters were placed). So copy our return values to that same spot.
747- memmove (stack, sp - count + 1 , count * sizeof (VValue));
748-
749- // Now move the stack pointer to the end of the just-pushed return values,
750- // and restore everything else.
748+ // When we were called, the caller reserved stack space for their copy of our
749+ // return value, then 'stack' was positioned after that, where our parameters
750+ // were placed. Copy our return values to their reserved area.
751+ memcpy (stack - count, sp - count + 1 , count * sizeof (VValue));
752+
753+ // Now move the stack pointer to the end of the passed-in parameters. This odd
754+ // calling convention requires the caller to pop the arguments after calling,
755+ // but allows them to store any out-parameters back during that unwinding.
756+ // After that sequence finishes, the return value will be the top of the stack.
751757 const StackFrame& frame (frames.back ());
752- sp = stack + count - 1 ;
758+ sp = stack + frame. fParameterCount - 1 ;
753759 stack = frame.fStack ;
754760 code = frame.fCode ;
755761 ip = frame.fIP ;
0 commit comments