- Added the TAIL instruction to perform a CALL and a RET in the same instruction. The called

function will pass its results directly to this function's caller. Eventually, this should
  be changed to do a proper tail call for scripted functions.

SVN r3769 (scripting)
This commit is contained in:
Randy Heit 2012-07-17 04:06:28 +00:00
parent 644f0c0e05
commit e1a2f3b546
5 changed files with 58 additions and 9 deletions

View file

@ -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;

View file

@ -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);
}

View file

@ -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)
else
{
printf_wrapper(out, "%d,%d,%d [%p]\n", code[i].a, code[i].b, code[i].c, callfunc);
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, "%d,%d,%d\n", code[i].a, code[i].b, code[i].c);
printf_wrapper(out, "\n");
}
}
}
}

View file

@ -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<VMNativeFunction *>(call)->NativeCall(stack, reg.param + f->NumParam - B, B, ret, numret);
}
else
{ // FIXME: Not a true tail call
VMScriptFunction *script = static_cast<VMScriptFunction *>(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

View file

@ -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