diff --git a/src/thingdef/thingdef.cpp b/src/thingdef/thingdef.cpp index 3b68851c83..4fa0356331 100644 --- a/src/thingdef/thingdef.cpp +++ b/src/thingdef/thingdef.cpp @@ -300,8 +300,7 @@ static void FinishThingdef() p->Emit(&buildit); delete p; } - buildit.Emit(OP_CALL_K, buildit.GetConstantAddress(tcall->Function, ATAG_OBJECT), NAP + j, 0); - buildit.Emit(OP_RET, 0, REGT_NIL, 0); + buildit.Emit(OP_TAIL_K, buildit.GetConstantAddress(tcall->Function, ATAG_OBJECT), NAP + j, 0); VMScriptFunction *sfunc = buildit.MakeFunction(); sfunc->NumArgs = NAP; func = sfunc; diff --git a/src/zscript/vmbuilder.cpp b/src/zscript/vmbuilder.cpp index 60fe7817c2..85e8c04697 100644 --- a/src/zscript/vmbuilder.cpp +++ b/src/zscript/vmbuilder.cpp @@ -430,7 +430,7 @@ size_t VMFunctionBuilder::Emit(int opcode, int opa, int opb, int opc) { ParamChange(1); } - else if (opcode == OP_CALL || opcode == OP_CALL_K) + else if (opcode == OP_CALL || opcode == OP_CALL_K || opcode == OP_TAIL || opcode == OP_TAIL_K) { ParamChange(-opb); } diff --git a/src/zscript/vmdisasm.cpp b/src/zscript/vmdisasm.cpp index e81b4087b3..b68d3eb876 100644 --- a/src/zscript/vmdisasm.cpp +++ b/src/zscript/vmdisasm.cpp @@ -44,6 +44,8 @@ #define I24 MODE_ABCJOINT #define I8 MODE_AIMMZ | MODE_BUNUSED | MODE_CUNUSED #define __BCP MODE_AUNUSED | MODE_BCJOINT | MODE_BCPARAM +#define RPI8 MODE_AP | MODE_BIMMZ | MODE_CUNUSED +#define KPI8 MODE_AKP | MODE_BIMMZ | MODE_CUNUSED #define RPI8I8 MODE_AP | MODE_BIMMZ | MODE_CIMMZ #define KPI8I8 MODE_AKP | MODE_BIMMZ | MODE_CIMMZ #define I8BCP MODE_AIMMZ | MODE_BCJOINT | MODE_BCPARAM @@ -215,9 +217,14 @@ void VMDisasm(FILE *out, const VMOP *code, int codesize, const VMScriptFunction break; case OP_CALL_K: + case OP_TAIL_K: callfunc = (VMFunction *)func->KonstA[code[i].a].o; callname = callfunc->Name != NAME_None ? callfunc->Name : "[anonfunc]"; - col = printf_wrapper(out, "%.23s,%d,%d", callname, code[i].b, code[i].c); + col = printf_wrapper(out, "%.23s,%d", callname, code[i].b); + if (code[i].op == OP_CALL_K) + { + printf_wrapper(out, ",%d", code[i].c); + } break; case OP_RET: @@ -316,13 +323,17 @@ void VMDisasm(FILE *out, const VMOP *code, int codesize, const VMScriptFunction { printf_wrapper(out, "%d\n", code[i].i24); } - else if (code[i].op == OP_CALL_K) - { - printf_wrapper(out, "%d,%d,%d [%p]\n", code[i].a, code[i].b, code[i].c, callfunc); - } else { - printf_wrapper(out, "%d,%d,%d\n", code[i].a, code[i].b, code[i].c); + printf_wrapper(out, "%d,%d,%d", code[i].a, code[i].b, code[i].c); + if (code[i].op == OP_CALL_K || code[i].op == OP_TAIL_K) + { + printf_wrapper(out, " [%p]\n", callfunc); + } + else + { + printf_wrapper(out, "\n"); + } } } } diff --git a/src/zscript/vmexec.h b/src/zscript/vmexec.h index e9847946f5..b59b44e947 100644 --- a/src/zscript/vmexec.h +++ b/src/zscript/vmexec.h @@ -539,6 +539,43 @@ begin: pc += C; // Skip RESULTs } NEXTOP; + OP(TAIL_K): + ASSERTKA(a); + assert(konstatag[a] == ATAG_OBJECT); + ptr = konsta[a].o; + goto Do_TAILCALL; + OP(TAIL): + ASSERTA(a); + ptr = reg.a[a]; + Do_TAILCALL: + assert(B <= f->NumParam); + assert(C <= MAX_RETURNS); + { + VMFunction *call = (VMFunction *)ptr; + + if (call->Native) + { + return static_cast(call)->NativeCall(stack, reg.param + f->NumParam - B, B, ret, numret); + } + else + { // FIXME: Not a true tail call + VMScriptFunction *script = static_cast(call); + VMFrame *newf = stack->AllocFrame(script); + VMFillParams(reg.param + f->NumParam - B, newf, B); + try + { + numret = Exec(stack, script->Code, ret, numret); + } + catch(...) + { + stack->PopFrame(); + throw; + } + stack->PopFrame(); + return numret; + } + } + NEXTOP; OP(RET): if (B == REGT_NIL) { // No return values diff --git a/src/zscript/vmops.h b/src/zscript/vmops.h index 20178dfb0b..78ef5116f3 100644 --- a/src/zscript/vmops.h +++ b/src/zscript/vmops.h @@ -77,6 +77,8 @@ xx(PARAM, param, __BCP), // push parameter encoded in BC for function call (B=r xx(PARAMI, parami, I24), // push immediate, signed integer for function call xx(CALL, call, RPI8I8), // Call function pkA with parameter count B and expected result count C xx(CALL_K, call, KPI8I8), +xx(TAIL, tail, RPI8), // Call+Ret in a single instruction +xx(TAIL_K, tail, KPI8), xx(RESULT, result, __BCP), // Result should go in register encoded in BC (in caller, after CALL) xx(RET, ret, I8BCP), // Copy value from register encoded in BC to return value A, possibly returning xx(TRY, try, I24), // When an exception is thrown, start searching for a handler at pc + ABC