vkdoom_m/src/scripting/vm/vmexec.h
Christoph Oelckers f343d36ea9 - implemented the basics of a working metadata system.
This will store class meta properties in a separate memory block so that it won't have to muck around with PClass - which made the implementation from the scripting branch relatively useless because extending the data wasn't particularly easy and also not well implemented. This can now be handled just like the defaults.
2017-02-27 23:28:19 +01:00

2025 lines
44 KiB
C++

#ifndef IMPLEMENT_VMEXEC
#error vmexec.h must not be #included outside vmexec.cpp. Use vm.h instead.
#endif
static int Exec(VMFrameStack *stack, const VMOP *pc, VMReturn *ret, int numret)
{
#if COMPGOTO
static const void * const ops[256] =
{
#define xx(op,sym,mode,alt,kreg,ktype) &&op
#include "vmops.h"
};
#endif
const VMOP *exception_frames[MAX_TRY_DEPTH];
int try_depth = 0;
VMFrame *f = stack->TopFrame();
VMScriptFunction *sfunc;
const VMRegisters reg(f);
const int *konstd;
const double *konstf;
const FString *konsts;
const FVoidObj *konsta;
const VM_ATAG *konstatag;
if (f->Func != NULL && !f->Func->Native)
{
sfunc = static_cast<VMScriptFunction *>(f->Func);
konstd = sfunc->KonstD;
konstf = sfunc->KonstF;
konsts = sfunc->KonstS;
konsta = sfunc->KonstA;
konstatag = sfunc->KonstATags();
}
else
{
sfunc = NULL;
konstd = NULL;
konstf = NULL;
konsts = NULL;
konsta = NULL;
konstatag = NULL;
}
void *ptr;
double fb, fc;
const double *fbp, *fcp;
int a, b, c;
begin:
try
{
#if !COMPGOTO
VM_UBYTE op;
for(;;) switch(op = pc->op, a = pc->a, op)
#else
pc--;
NEXTOP;
#endif
{
#if !COMPGOTO
default:
assert(0 && "Undefined opcode hit");
NEXTOP;
#endif
OP(LI):
ASSERTD(a);
reg.d[a] = BCs;
NEXTOP;
OP(LK):
ASSERTD(a); ASSERTKD(BC);
reg.d[a] = konstd[BC];
NEXTOP;
OP(LKF):
ASSERTF(a); ASSERTKF(BC);
reg.f[a] = konstf[BC];
NEXTOP;
OP(LKS):
ASSERTS(a); ASSERTKS(BC);
reg.s[a] = konsts[BC];
NEXTOP;
OP(LKP):
ASSERTA(a); ASSERTKA(BC);
reg.a[a] = konsta[BC].v;
reg.atag[a] = konstatag[BC];
NEXTOP;
OP(LK_R) :
ASSERTD(a); ASSERTD(B);
reg.d[a] = konstd[reg.d[B] + C];
NEXTOP;
OP(LKF_R) :
ASSERTF(a); ASSERTD(B);
reg.f[a] = konstf[reg.d[B] + C];
NEXTOP;
OP(LKS_R) :
ASSERTS(a); ASSERTD(B);
reg.s[a] = konsts[reg.d[B] + C];
NEXTOP;
OP(LKP_R) :
ASSERTA(a); ASSERTD(B);
b = reg.d[B] + C;
reg.a[a] = konsta[b].v;
reg.atag[a] = konstatag[b];
NEXTOP;
OP(LFP):
ASSERTA(a); assert(sfunc != NULL); assert(sfunc->ExtraSpace > 0);
reg.a[a] = f->GetExtra();
reg.atag[a] = ATAG_GENERIC; // using ATAG_FRAMEPOINTER will cause endless asserts.
NEXTOP;
OP(CLSS):
ASSERTA(a); ASSERTO(B);
reg.a[a] = ((DObject*)reg.a[B])->GetClass(); // I wish this could be done without a special opcode but there's really no good way to guarantee initialization of the Class pointer...
reg.atag[a] = ATAG_OBJECT;
NEXTOP;
OP(META):
ASSERTA(a); ASSERTO(B);
reg.a[a] = ((DObject*)reg.a[B])->GetClass()->Meta; // I wish this could be done without a special opcode but there's really no good way to guarantee initialization of the Class pointer...
reg.atag[a] = ATAG_OBJECT;
NEXTOP;
OP(LB):
ASSERTD(a); ASSERTA(B); ASSERTKD(C);
GETADDR(PB,KC,X_READ_NIL);
reg.d[a] = *(VM_SBYTE *)ptr;
NEXTOP;
OP(LB_R):
ASSERTD(a); ASSERTA(B); ASSERTD(C);
GETADDR(PB,RC,X_READ_NIL);
reg.d[a] = *(VM_SBYTE *)ptr;
NEXTOP;
OP(LH):
ASSERTD(a); ASSERTA(B); ASSERTKD(C);
GETADDR(PB,KC,X_READ_NIL);
reg.d[a] = *(VM_SHALF *)ptr;
NEXTOP;
OP(LH_R):
ASSERTD(a); ASSERTA(B); ASSERTD(C);
GETADDR(PB,RC,X_READ_NIL);
reg.d[a] = *(VM_SHALF *)ptr;
NEXTOP;
OP(LW):
ASSERTD(a); ASSERTA(B); ASSERTKD(C);
GETADDR(PB,KC,X_READ_NIL);
reg.d[a] = *(VM_SWORD *)ptr;
NEXTOP;
OP(LW_R):
ASSERTD(a); ASSERTA(B); ASSERTD(C);
GETADDR(PB,RC,X_READ_NIL);
reg.d[a] = *(VM_SWORD *)ptr;
NEXTOP;
OP(LBU):
ASSERTD(a); ASSERTA(B); ASSERTKD(C);
GETADDR(PB,KC,X_READ_NIL);
reg.d[a] = *(VM_UBYTE *)ptr;
NEXTOP;
OP(LBU_R):
ASSERTD(a); ASSERTA(B); ASSERTD(C);
GETADDR(PB,RC,X_READ_NIL);
reg.d[a] = *(VM_UBYTE *)ptr;
NEXTOP;
OP(LHU):
ASSERTD(a); ASSERTA(B); ASSERTKD(C);
GETADDR(PB,KC,X_READ_NIL);
reg.d[a] = *(VM_UHALF *)ptr;
NEXTOP;
OP(LHU_R):
ASSERTD(a); ASSERTA(B); ASSERTD(C);
GETADDR(PB,RC,X_READ_NIL);
reg.d[a] = *(VM_UHALF *)ptr;
NEXTOP;
OP(LSP):
ASSERTF(a); ASSERTA(B); ASSERTKD(C);
GETADDR(PB,KC,X_READ_NIL);
reg.f[a] = *(float *)ptr;
NEXTOP;
OP(LSP_R):
ASSERTF(a); ASSERTA(B); ASSERTD(C);
GETADDR(PB,RC,X_READ_NIL);
reg.f[a] = *(float *)ptr;
NEXTOP;
OP(LDP):
ASSERTF(a); ASSERTA(B); ASSERTKD(C);
GETADDR(PB,KC,X_READ_NIL);
reg.f[a] = *(double *)ptr;
NEXTOP;
OP(LDP_R):
ASSERTF(a); ASSERTA(B); ASSERTD(C);
GETADDR(PB,RC,X_READ_NIL);
reg.f[a] = *(double *)ptr;
NEXTOP;
OP(LS):
ASSERTS(a); ASSERTA(B); ASSERTKD(C);
GETADDR(PB,KC,X_READ_NIL);
reg.s[a] = *(FString *)ptr;
NEXTOP;
OP(LS_R):
ASSERTS(a); ASSERTA(B); ASSERTD(C);
GETADDR(PB,RC,X_READ_NIL);
reg.s[a] = *(FString *)ptr;
NEXTOP;
OP(LCS):
ASSERTS(a); ASSERTA(B); ASSERTKD(C);
GETADDR(PB,KC,X_READ_NIL);
reg.s[a] = *(const char **)ptr;
NEXTOP;
OP(LCS_R):
ASSERTS(a); ASSERTA(B); ASSERTD(C);
GETADDR(PB,RC,X_READ_NIL);
reg.s[a] = *(const char **)ptr;
NEXTOP;
OP(LO):
ASSERTA(a); ASSERTA(B); ASSERTKD(C);
GETADDR(PB,KC,X_READ_NIL);
reg.a[a] = GC::ReadBarrier(*(DObject **)ptr);
reg.atag[a] = ATAG_OBJECT;
NEXTOP;
OP(LO_R):
ASSERTA(a); ASSERTA(B); ASSERTD(C);
GETADDR(PB,RC,X_READ_NIL);
reg.a[a] = GC::ReadBarrier(*(DObject **)ptr);
reg.atag[a] = ATAG_OBJECT;
NEXTOP;
OP(LOS):
ASSERTA(a); ASSERTA(B); ASSERTKD(C);
GETADDR(PB,KC,X_READ_NIL);
reg.a[a] = *(DObject **)ptr;
reg.atag[a] = ATAG_OBJECT;
NEXTOP;
OP(LOS_R):
ASSERTA(a); ASSERTA(B); ASSERTD(C);
GETADDR(PB,RC,X_READ_NIL);
reg.a[a] = *(DObject **)ptr;
reg.atag[a] = ATAG_OBJECT;
NEXTOP;
OP(LP):
ASSERTA(a); ASSERTA(B); ASSERTKD(C);
GETADDR(PB,KC,X_READ_NIL);
reg.a[a] = *(void **)ptr;
reg.atag[a] = ATAG_GENERIC;
NEXTOP;
OP(LP_R):
ASSERTA(a); ASSERTA(B); ASSERTD(C);
GETADDR(PB,RC,X_READ_NIL);
reg.a[a] = *(void **)ptr;
reg.atag[a] = ATAG_GENERIC;
NEXTOP;
OP(LV2):
ASSERTF(a+1); ASSERTA(B); ASSERTKD(C);
GETADDR(PB,KC,X_READ_NIL);
{
auto v = (double *)ptr;
reg.f[a] = v[0];
reg.f[a+1] = v[1];
}
NEXTOP;
OP(LV2_R):
ASSERTF(a+1); ASSERTA(B); ASSERTD(C);
GETADDR(PB,RC,X_READ_NIL);
{
auto v = (double *)ptr;
reg.f[a] = v[0];
reg.f[a+1] = v[1];
}
NEXTOP;
OP(LV3):
ASSERTF(a+2); ASSERTA(B); ASSERTKD(C);
GETADDR(PB,KC,X_READ_NIL);
{
auto v = (double *)ptr;
reg.f[a] = v[0];
reg.f[a+1] = v[1];
reg.f[a+2] = v[2];
}
NEXTOP;
OP(LV3_R):
ASSERTF(a+2); ASSERTA(B); ASSERTD(C);
GETADDR(PB,RC,X_READ_NIL);
{
auto v = (double *)ptr;
reg.f[a] = v[0];
reg.f[a+1] = v[1];
reg.f[a+2] = v[2];
}
NEXTOP;
OP(LBIT):
ASSERTD(a); ASSERTA(B);
GETADDR(PB,0,X_READ_NIL);
reg.d[a] = !!(*(VM_UBYTE *)ptr & C);
NEXTOP;
OP(SB):
ASSERTA(a); ASSERTD(B); ASSERTKD(C);
GETADDR(PA,KC,X_WRITE_NIL);
*(VM_SBYTE *)ptr = reg.d[B];
NEXTOP;
OP(SB_R):
ASSERTA(a); ASSERTD(B); ASSERTD(C);
GETADDR(PA,RC,X_WRITE_NIL);
*(VM_SBYTE *)ptr = reg.d[B];
NEXTOP;
OP(SH):
ASSERTA(a); ASSERTD(B); ASSERTKD(C);
GETADDR(PA,KC,X_WRITE_NIL);
*(VM_SHALF *)ptr = reg.d[B];
NEXTOP;
OP(SH_R):
ASSERTA(a); ASSERTD(B); ASSERTD(C);
GETADDR(PA,RC,X_WRITE_NIL);
*(VM_SHALF *)ptr = reg.d[B];
NEXTOP;
OP(SW):
ASSERTA(a); ASSERTD(B); ASSERTKD(C);
GETADDR(PA,KC,X_WRITE_NIL);
*(VM_SWORD *)ptr = reg.d[B];
NEXTOP;
OP(SW_R):
ASSERTA(a); ASSERTD(B); ASSERTD(C);
GETADDR(PA,RC,X_WRITE_NIL);
*(VM_SWORD *)ptr = reg.d[B];
NEXTOP;
OP(SSP):
ASSERTA(a); ASSERTF(B); ASSERTKD(C);
GETADDR(PA,KC,X_WRITE_NIL);
*(float *)ptr = (float)reg.f[B];
NEXTOP;
OP(SSP_R):
ASSERTA(a); ASSERTF(B); ASSERTD(C);
GETADDR(PA,RC,X_WRITE_NIL);
*(float *)ptr = (float)reg.f[B];
NEXTOP;
OP(SDP):
ASSERTA(a); ASSERTF(B); ASSERTKD(C);
GETADDR(PA,KC,X_WRITE_NIL);
*(double *)ptr = reg.f[B];
NEXTOP;
OP(SDP_R):
ASSERTA(a); ASSERTF(B); ASSERTD(C);
GETADDR(PA,RC,X_WRITE_NIL);
*(double *)ptr = reg.f[B];
NEXTOP;
OP(SS):
ASSERTA(a); ASSERTS(B); ASSERTKD(C);
GETADDR(PA,KC,X_WRITE_NIL);
*(FString *)ptr = reg.s[B];
NEXTOP;
OP(SS_R):
ASSERTA(a); ASSERTS(B); ASSERTD(C);
GETADDR(PA,RC,X_WRITE_NIL);
*(FString *)ptr = reg.s[B];
NEXTOP;
OP(SP):
ASSERTA(a); ASSERTA(B); ASSERTKD(C);
GETADDR(PA,KC,X_WRITE_NIL);
*(void **)ptr = reg.a[B];
NEXTOP;
OP(SP_R):
ASSERTA(a); ASSERTA(B); ASSERTD(C);
GETADDR(PA,RC,X_WRITE_NIL);
*(void **)ptr = reg.a[B];
NEXTOP;
OP(SO):
ASSERTA(a); ASSERTA(B); ASSERTKD(C);
GETADDR(PA,KC,X_WRITE_NIL);
*(void **)ptr = reg.a[B];
GC::WriteBarrier((DObject*)*(void **)ptr);
NEXTOP;
OP(SO_R):
ASSERTA(a); ASSERTA(B); ASSERTD(C);
GETADDR(PA,RC,X_WRITE_NIL);
GC::WriteBarrier((DObject*)*(void **)ptr);
NEXTOP;
OP(SV2):
ASSERTA(a); ASSERTF(B+1); ASSERTKD(C);
GETADDR(PA,KC,X_WRITE_NIL);
{
auto v = (double *)ptr;
v[0] = reg.f[B];
v[1] = reg.f[B+1];
}
NEXTOP;
OP(SV2_R):
ASSERTA(a); ASSERTF(B+1); ASSERTD(C);
GETADDR(PA,RC,X_WRITE_NIL);
{
auto v = (double *)ptr;
v[0] = reg.f[B];
v[1] = reg.f[B+1];
}
NEXTOP;
OP(SV3):
ASSERTA(a); ASSERTF(B+2); ASSERTKD(C);
GETADDR(PA,KC,X_WRITE_NIL);
{
auto v = (double *)ptr;
v[0] = reg.f[B];
v[1] = reg.f[B+1];
v[2] = reg.f[B+2];
}
NEXTOP;
OP(SV3_R):
ASSERTA(a); ASSERTF(B+2); ASSERTD(C);
GETADDR(PA,RC,X_WRITE_NIL);
{
auto v = (double *)ptr;
v[0] = reg.f[B];
v[1] = reg.f[B+1];
v[2] = reg.f[B+2];
}
NEXTOP;
OP(SBIT):
ASSERTA(a); ASSERTD(B);
GETADDR(PA,0,X_WRITE_NIL);
if (reg.d[B])
{
*(VM_UBYTE *)ptr |= C;
}
else
{
*(VM_UBYTE *)ptr &= ~C;
}
NEXTOP;
OP(MOVE):
ASSERTD(a); ASSERTD(B);
reg.d[a] = reg.d[B];
NEXTOP;
OP(MOVEF):
ASSERTF(a); ASSERTF(B);
reg.f[a] = reg.f[B];
NEXTOP;
OP(MOVES):
ASSERTS(a); ASSERTS(B);
reg.s[a] = reg.s[B];
NEXTOP;
OP(MOVEA):
{
ASSERTA(a); ASSERTA(B);
b = B;
reg.a[a] = reg.a[b];
reg.atag[a] = reg.atag[b];
NEXTOP;
}
OP(MOVEV2):
{
ASSERTF(a); ASSERTF(B);
b = B;
reg.f[a] = reg.f[b];
reg.f[a + 1] = reg.f[b + 1];
NEXTOP;
}
OP(MOVEV3):
{
ASSERTF(a); ASSERTF(B);
b = B;
reg.f[a] = reg.f[b];
reg.f[a + 1] = reg.f[b + 1];
reg.f[a + 2] = reg.f[b + 2];
NEXTOP;
}
OP(DYNCAST_R) :
ASSERTA(a); ASSERTA(B); ASSERTA(C);
b = B;
reg.a[a] = (reg.a[b] && ((DObject*)(reg.a[b]))->IsKindOf((PClass*)(reg.a[C]))) ? reg.a[b] : nullptr;
reg.atag[a] = ATAG_OBJECT;
NEXTOP;
OP(DYNCAST_K) :
ASSERTA(a); ASSERTA(B); ASSERTKA(C);
b = B;
reg.a[a] = (reg.a[b] && ((DObject*)(reg.a[b]))->IsKindOf((PClass*)(konsta[C].o))) ? reg.a[b] : nullptr;
reg.atag[a] = ATAG_OBJECT;
NEXTOP;
OP(DYNCASTC_R) :
ASSERTA(a); ASSERTA(B); ASSERTA(C);
b = B;
reg.a[a] = (reg.a[b] && ((PClass*)(reg.a[b]))->IsDescendantOf((PClass*)(reg.a[C]))) ? reg.a[b] : nullptr;
reg.atag[a] = ATAG_OBJECT;
NEXTOP;
OP(DYNCASTC_K) :
ASSERTA(a); ASSERTA(B); ASSERTKA(C);
b = B;
reg.a[a] = (reg.a[b] && ((PClass*)(reg.a[b]))->IsDescendantOf((PClass*)(konsta[C].o))) ? reg.a[b] : nullptr;
reg.atag[a] = ATAG_OBJECT;
NEXTOP;
OP(CAST):
if (C == CAST_I2F)
{
ASSERTF(a); ASSERTD(B);
reg.f[a] = reg.d[B];
}
else if (C == CAST_F2I)
{
ASSERTD(a); ASSERTF(B);
reg.d[a] = (int)reg.f[B];
}
else
{
DoCast(reg, f, a, B, C);
}
NEXTOP;
OP(CASTB):
if (C == CASTB_I)
{
ASSERTD(a); ASSERTD(B);
reg.d[a] = !!reg.d[B];
}
else if (C == CASTB_F)
{
ASSERTD(a); ASSERTF(B);
reg.d[a] = reg.f[B] != 0;
}
else if (C == CASTB_A)
{
ASSERTD(a); ASSERTA(B);
reg.d[a] = reg.a[B] != nullptr;
}
else
{
ASSERTD(a); ASSERTS(B);
reg.d[a] = reg.s[B].Len() > 0;
}
NEXTOP;
OP(TEST):
ASSERTD(a);
if (reg.d[a] != BC)
{
pc++;
}
NEXTOP;
OP(TESTN):
ASSERTD(a);
if (-reg.d[a] != BC)
{
pc++;
}
NEXTOP;
OP(JMP):
pc += JMPOFS(pc);
NEXTOP;
OP(IJMP):
ASSERTD(a);
pc += (BCs + reg.d[a]);
assert(pc[1].op == OP_JMP);
pc += 1 + JMPOFS(pc+1);
NEXTOP;
OP(PARAMI):
assert(f->NumParam < sfunc->MaxParam);
{
VMValue *param = &reg.param[f->NumParam++];
::new(param) VMValue(ABCs);
}
NEXTOP;
OP(PARAM):
assert(f->NumParam < sfunc->MaxParam);
{
VMValue *param = &reg.param[f->NumParam++];
b = B;
if (b == REGT_NIL)
{
::new(param) VMValue();
}
else
{
switch(b)
{
case REGT_INT:
assert(C < f->NumRegD);
::new(param) VMValue(reg.d[C]);
break;
case REGT_INT | REGT_ADDROF:
assert(C < f->NumRegD);
::new(param) VMValue(&reg.d[C], ATAG_GENERIC);
break;
case REGT_INT | REGT_KONST:
assert(C < sfunc->NumKonstD);
::new(param) VMValue(konstd[C]);
break;
case REGT_STRING:
assert(C < f->NumRegS);
::new(param) VMValue(reg.s[C]);
break;
case REGT_STRING | REGT_ADDROF:
assert(C < f->NumRegS);
::new(param) VMValue(&reg.s[C], ATAG_GENERIC);
break;
case REGT_STRING | REGT_KONST:
assert(C < sfunc->NumKonstS);
::new(param) VMValue(konsts[C]);
break;
case REGT_POINTER:
assert(C < f->NumRegA);
::new(param) VMValue(reg.a[C], reg.atag[C]);
break;
case REGT_POINTER | REGT_ADDROF:
assert(C < f->NumRegA);
::new(param) VMValue(&reg.a[C], ATAG_GENERIC);
break;
case REGT_POINTER | REGT_KONST:
assert(C < sfunc->NumKonstA);
::new(param) VMValue(konsta[C].v, konstatag[C]);
break;
case REGT_FLOAT:
assert(C < f->NumRegF);
::new(param) VMValue(reg.f[C]);
break;
case REGT_FLOAT | REGT_MULTIREG2:
assert(C < f->NumRegF - 1);
assert(f->NumParam < sfunc->MaxParam);
::new(param) VMValue(reg.f[C]);
::new(param + 1) VMValue(reg.f[C + 1]);
f->NumParam++;
break;
case REGT_FLOAT | REGT_MULTIREG3:
assert(C < f->NumRegF - 2);
assert(f->NumParam < sfunc->MaxParam - 1);
::new(param) VMValue(reg.f[C]);
::new(param + 1) VMValue(reg.f[C + 1]);
::new(param + 2) VMValue(reg.f[C + 2]);
f->NumParam += 2;
break;
case REGT_FLOAT | REGT_ADDROF:
assert(C < f->NumRegF);
::new(param) VMValue(&reg.f[C], ATAG_GENERIC);
break;
case REGT_FLOAT | REGT_KONST:
assert(C < sfunc->NumKonstF);
::new(param) VMValue(konstf[C]);
break;
default:
assert(0);
break;
}
}
}
NEXTOP;
OP(VTBL):
ASSERTA(a); ASSERTA(B);
{
auto o = (DObject*)reg.a[B];
auto p = o->GetClass();
assert(C < p->Virtuals.Size());
reg.a[a] = p->Virtuals[C];
}
NEXTOP;
OP(CALL_K):
ASSERTKA(a);
assert(konstatag[a] == ATAG_OBJECT);
ptr = konsta[a].o;
goto Do_CALL;
OP(CALL):
ASSERTA(a);
ptr = reg.a[a];
Do_CALL:
assert(B <= f->NumParam);
assert(C <= MAX_RETURNS);
{
VMFunction *call = (VMFunction *)ptr;
VMReturn returns[MAX_RETURNS];
int numret;
FillReturns(reg, f, returns, pc+1, C);
if (call->Native)
{
try
{
VMCycles[0].Unclock();
numret = static_cast<VMNativeFunction *>(call)->NativeCall(reg.param + f->NumParam - B, call->DefaultArgs, B, returns, C);
VMCycles[0].Clock();
}
catch (CVMAbortException &err)
{
err.MaybePrintMessage();
err.stacktrace.AppendFormat("Called from %s\n", call->PrintableName.GetChars());
// PrintParameters(reg.param + f->NumParam - B, B);
throw;
}
}
else
{
VMCalls[0]++;
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, returns, C);
}
catch(...)
{
stack->PopFrame();
throw;
}
stack->PopFrame();
}
assert(numret == C && "Number of parameters returned differs from what was expected by the caller");
for (b = B; b != 0; --b)
{
reg.param[--f->NumParam].~VMValue();
}
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:
// Whereas the CALL instruction uses its third operand to specify how many return values
// it expects, TAIL ignores its third operand and uses whatever was passed to this Exec call.
assert(B <= f->NumParam);
assert(C <= MAX_RETURNS);
{
VMFunction *call = (VMFunction *)ptr;
if (call->Native)
{
try
{
VMCycles[0].Unclock();
auto r = static_cast<VMNativeFunction *>(call)->NativeCall(reg.param + f->NumParam - B, call->DefaultArgs, B, ret, numret);
VMCycles[0].Clock();
return r;
}
catch (CVMAbortException &err)
{
err.MaybePrintMessage();
err.stacktrace.AppendFormat("Called from %s\n", call->PrintableName.GetChars());
// PrintParameters(reg.param + f->NumParam - B, B);
throw;
}
}
else
{ // FIXME: Not a true tail call
VMCalls[0]++;
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
return 0;
}
assert(ret != NULL || numret == 0);
{
int retnum = a & ~RET_FINAL;
if (retnum < numret)
{
SetReturn(reg, f, &ret[retnum], B, C);
}
if (a & RET_FINAL)
{
return retnum < numret ? retnum + 1 : numret;
}
}
NEXTOP;
OP(RETI):
assert(ret != NULL || numret == 0);
{
int retnum = a & ~RET_FINAL;
if (retnum < numret)
{
ret[retnum].SetInt(BCs);
}
if (a & RET_FINAL)
{
return retnum < numret ? retnum + 1 : numret;
}
}
NEXTOP;
OP(RESULT):
// This instruction is just a placeholder to indicate where a return
// value should be stored. It does nothing on its own and should not
// be executed.
assert(0);
NEXTOP;
OP(NEW_K):
OP(NEW):
{
b = B;
PClass *cls = (PClass*)(pc->op == OP_NEW ? reg.a[b] : konsta[b].v);
if (cls->ObjectFlags & OF_Abstract) ThrowAbortException(X_OTHER, "Cannot instantiate abstract class %s", cls->TypeName.GetChars());
reg.a[a] = cls->CreateNew();
reg.atag[a] = ATAG_OBJECT;
NEXTOP;
}
OP(TRY):
assert(try_depth < MAX_TRY_DEPTH);
if (try_depth >= MAX_TRY_DEPTH)
{
ThrowAbortException(X_TOO_MANY_TRIES, nullptr);
}
assert((pc + JMPOFS(pc) + 1)->op == OP_CATCH);
exception_frames[try_depth++] = pc + JMPOFS(pc) + 1;
NEXTOP;
OP(UNTRY):
assert(a <= try_depth);
try_depth -= a;
NEXTOP;
OP(THROW):
if (a == 0)
{
ASSERTA(B);
ThrowVMException((VMException *)reg.a[B]);
}
else if (a == 1)
{
ASSERTKA(B);
assert(konstatag[B] == ATAG_OBJECT);
ThrowVMException((VMException *)konsta[B].o);
}
else
{
ThrowAbortException(EVMAbortException(BC), nullptr);
}
NEXTOP;
OP(CATCH):
// This instruction is handled by our own catch handler and should
// not be executed by the normal VM code.
assert(0);
NEXTOP;
OP(BOUND):
if (reg.d[a] >= BC)
{
ThrowAbortException(X_ARRAY_OUT_OF_BOUNDS, "Max.index = %u, current index = %u\n", BC, reg.d[a]);
}
NEXTOP;
OP(BOUND_K):
ASSERTKD(BC);
if (reg.d[a] >= konstd[BC])
{
ThrowAbortException(X_ARRAY_OUT_OF_BOUNDS, "Max.index = %u, current index = %u\n", konstd[BC], reg.d[a]);
}
NEXTOP;
OP(BOUND_R):
ASSERTD(B);
if (reg.d[a] >= reg.d[B])
{
ThrowAbortException(X_ARRAY_OUT_OF_BOUNDS, "Max.index = %u, current index = %u\n", reg.d[B], reg.d[a]);
}
NEXTOP;
OP(CONCAT):
ASSERTS(a); ASSERTS(B); ASSERTS(C);
reg.s[a] = reg.s[B] + reg.s[C];
NEXTOP;
OP(LENS):
ASSERTD(a); ASSERTS(B);
reg.d[a] = (int)reg.s[B].Len();
NEXTOP;
OP(CMPS):
// String comparison is a fairly expensive operation, so I've
// chosen to conserve a few opcodes by condensing all the
// string comparisons into a single one.
{
const FString *b, *c;
int test, method;
bool cmp;
if (a & CMP_BK)
{
ASSERTKS(B);
b = &konsts[B];
}
else
{
ASSERTS(B);
b = &reg.s[B];
}
if (a & CMP_CK)
{
ASSERTKS(C);
c = &konsts[C];
}
else
{
ASSERTS(C);
c = &reg.s[C];
}
test = (a & CMP_APPROX) ? b->CompareNoCase(*c) : b->Compare(*c);
method = a & CMP_METHOD_MASK;
if (method == CMP_EQ)
{
cmp = !test;
}
else if (method == CMP_LT)
{
cmp = (test < 0);
}
else
{
assert(method == CMP_LE);
cmp = (test <= 0);
}
if (cmp == (a & CMP_CHECK))
{
assert(pc[1].op == OP_JMP);
pc += 1 + JMPOFS(pc+1);
}
else
{
pc += 1;
}
}
NEXTOP;
OP(SLL_RR):
ASSERTD(a); ASSERTD(B); ASSERTD(C);
reg.d[a] = reg.d[B] << reg.d[C];
NEXTOP;
OP(SLL_RI):
ASSERTD(a); ASSERTD(B); assert(C <= 31);
reg.d[a] = reg.d[B] << C;
NEXTOP;
OP(SLL_KR):
ASSERTD(a); ASSERTKD(B); ASSERTD(C);
reg.d[a] = konstd[B] << reg.d[C];
NEXTOP;
OP(SRL_RR):
ASSERTD(a); ASSERTD(B); ASSERTD(C);
reg.d[a] = (unsigned)reg.d[B] >> reg.d[C];
NEXTOP;
OP(SRL_RI):
ASSERTD(a); ASSERTD(B); assert(C <= 31);
reg.d[a] = (unsigned)reg.d[B] >> C;
NEXTOP;
OP(SRL_KR):
ASSERTD(a); ASSERTKD(B); ASSERTD(C);
reg.d[a] = (unsigned)konstd[B] >> C;
NEXTOP;
OP(SRA_RR):
ASSERTD(a); ASSERTD(B); ASSERTD(C);
reg.d[a] = reg.d[B] >> reg.d[C];
NEXTOP;
OP(SRA_RI):
ASSERTD(a); ASSERTD(B); assert(C <= 31);
reg.d[a] = reg.d[B] >> C;
NEXTOP;
OP(SRA_KR):
ASSERTD(a); ASSERTKD(B); ASSERTD(C);
reg.d[a] = konstd[B] >> reg.d[C];
NEXTOP;
OP(ADD_RR):
ASSERTD(a); ASSERTD(B); ASSERTD(C);
reg.d[a] = reg.d[B] + reg.d[C];
NEXTOP;
OP(ADD_RK):
ASSERTD(a); ASSERTD(B); ASSERTKD(C);
reg.d[a] = reg.d[B] + konstd[C];
NEXTOP;
OP(ADDI):
ASSERTD(a); ASSERTD(B);
reg.d[a] = reg.d[B] + Cs;
NEXTOP;
OP(SUB_RR):
ASSERTD(a); ASSERTD(B); ASSERTD(C);
reg.d[a] = reg.d[B] - reg.d[C];
NEXTOP;
OP(SUB_RK):
ASSERTD(a); ASSERTD(B); ASSERTKD(C);
reg.d[a] = reg.d[B] - konstd[C];
NEXTOP;
OP(SUB_KR):
ASSERTD(a); ASSERTKD(B); ASSERTD(C);
reg.d[a] = konstd[B] - reg.d[C];
NEXTOP;
OP(MUL_RR):
ASSERTD(a); ASSERTD(B); ASSERTD(C);
reg.d[a] = reg.d[B] * reg.d[C];
NEXTOP;
OP(MUL_RK):
ASSERTD(a); ASSERTD(B); ASSERTKD(C);
reg.d[a] = reg.d[B] * konstd[C];
NEXTOP;
OP(DIV_RR):
ASSERTD(a); ASSERTD(B); ASSERTD(C);
if (reg.d[C] == 0)
{
ThrowAbortException(X_DIVISION_BY_ZERO, nullptr);
}
reg.d[a] = reg.d[B] / reg.d[C];
NEXTOP;
OP(DIV_RK):
ASSERTD(a); ASSERTD(B); ASSERTKD(C);
if (konstd[C] == 0)
{
ThrowAbortException(X_DIVISION_BY_ZERO, nullptr);
}
reg.d[a] = reg.d[B] / konstd[C];
NEXTOP;
OP(DIV_KR):
ASSERTD(a); ASSERTKD(B); ASSERTD(C);
if (reg.d[C] == 0)
{
ThrowAbortException(X_DIVISION_BY_ZERO, nullptr);
}
reg.d[a] = konstd[B] / reg.d[C];
NEXTOP;
OP(DIVU_RR):
ASSERTD(a); ASSERTD(B); ASSERTD(C);
if (reg.d[C] == 0)
{
ThrowAbortException(X_DIVISION_BY_ZERO, nullptr);
}
reg.d[a] = int((unsigned)reg.d[B] / (unsigned)reg.d[C]);
NEXTOP;
OP(DIVU_RK):
ASSERTD(a); ASSERTD(B); ASSERTKD(C);
if (konstd[C] == 0)
{
ThrowAbortException(X_DIVISION_BY_ZERO, nullptr);
}
reg.d[a] = int((unsigned)reg.d[B] / (unsigned)konstd[C]);
NEXTOP;
OP(DIVU_KR):
ASSERTD(a); ASSERTKD(B); ASSERTD(C);
if (reg.d[C] == 0)
{
ThrowAbortException(X_DIVISION_BY_ZERO, nullptr);
}
reg.d[a] = int((unsigned)konstd[B] / (unsigned)reg.d[C]);
NEXTOP;
OP(MOD_RR):
ASSERTD(a); ASSERTD(B); ASSERTD(C);
if (reg.d[C] == 0)
{
ThrowAbortException(X_DIVISION_BY_ZERO, nullptr);
}
reg.d[a] = reg.d[B] % reg.d[C];
NEXTOP;
OP(MOD_RK):
ASSERTD(a); ASSERTD(B); ASSERTKD(C);
if (konstd[C] == 0)
{
ThrowAbortException(X_DIVISION_BY_ZERO, nullptr);
}
reg.d[a] = reg.d[B] % konstd[C];
NEXTOP;
OP(MOD_KR):
ASSERTD(a); ASSERTKD(B); ASSERTD(C);
if (reg.d[C] == 0)
{
ThrowAbortException(X_DIVISION_BY_ZERO, nullptr);
}
reg.d[a] = konstd[B] % reg.d[C];
NEXTOP;
OP(MODU_RR):
ASSERTD(a); ASSERTD(B); ASSERTD(C);
if (reg.d[C] == 0)
{
ThrowAbortException(X_DIVISION_BY_ZERO, nullptr);
}
reg.d[a] = int((unsigned)reg.d[B] % (unsigned)reg.d[C]);
NEXTOP;
OP(MODU_RK):
ASSERTD(a); ASSERTD(B); ASSERTKD(C);
if (konstd[C] == 0)
{
ThrowAbortException(X_DIVISION_BY_ZERO, nullptr);
}
reg.d[a] = int((unsigned)reg.d[B] % (unsigned)konstd[C]);
NEXTOP;
OP(MODU_KR):
ASSERTD(a); ASSERTKD(B); ASSERTD(C);
if (reg.d[C] == 0)
{
ThrowAbortException(X_DIVISION_BY_ZERO, nullptr);
}
reg.d[a] = int((unsigned)konstd[B] % (unsigned)reg.d[C]);
NEXTOP;
OP(AND_RR):
ASSERTD(a); ASSERTD(B); ASSERTD(C);
reg.d[a] = reg.d[B] & reg.d[C];
NEXTOP;
OP(AND_RK):
ASSERTD(a); ASSERTD(B); ASSERTKD(C);
reg.d[a] = reg.d[B] & konstd[C];
NEXTOP;
OP(OR_RR):
ASSERTD(a); ASSERTD(B); ASSERTD(C);
reg.d[a] = reg.d[B] | reg.d[C];
NEXTOP;
OP(OR_RK):
ASSERTD(a); ASSERTD(B); ASSERTKD(C);
reg.d[a] = reg.d[B] | konstd[C];
NEXTOP;
OP(XOR_RR):
ASSERTD(a); ASSERTD(B); ASSERTD(C);
reg.d[a] = reg.d[B] ^ reg.d[C];
NEXTOP;
OP(XOR_RK):
ASSERTD(a); ASSERTD(B); ASSERTKD(C);
reg.d[a] = reg.d[B] ^ konstd[C];
NEXTOP;
OP(MIN_RR):
ASSERTD(a); ASSERTD(B); ASSERTD(C);
reg.d[a] = reg.d[B] < reg.d[C] ? reg.d[B] : reg.d[C];
NEXTOP;
OP(MIN_RK):
ASSERTD(a); ASSERTD(B); ASSERTKD(C);
reg.d[a] = reg.d[B] < konstd[C] ? reg.d[B] : konstd[C];
NEXTOP;
OP(MAX_RR):
ASSERTD(a); ASSERTD(B); ASSERTD(C);
reg.d[a] = reg.d[B] > reg.d[C] ? reg.d[B] : reg.d[C];
NEXTOP;
OP(MAX_RK):
ASSERTD(a); ASSERTD(B); ASSERTKD(C);
reg.d[a] = reg.d[B] > konstd[C] ? reg.d[B] : konstd[C];
NEXTOP;
OP(ABS):
ASSERTD(a); ASSERTD(B);
reg.d[a] = abs(reg.d[B]);
NEXTOP;
OP(NEG):
ASSERTD(a); ASSERTD(B);
reg.d[a] = -reg.d[B];
NEXTOP;
OP(NOT):
ASSERTD(a); ASSERTD(B);
reg.d[a] = ~reg.d[B];
NEXTOP;
OP(SEXT):
ASSERTD(a); ASSERTD(B);
reg.d[a] = (VM_SWORD)(reg.d[B] << C) >> C;
NEXTOP;
OP(ZAP_R):
ASSERTD(a); ASSERTD(B); ASSERTD(C);
reg.d[a] = reg.d[B] & ZapTable[(reg.d[C] & 15) ^ 15];
NEXTOP;
OP(ZAP_I):
ASSERTD(a); ASSERTD(B);
reg.d[a] = reg.d[B] & ZapTable[(C & 15) ^ 15];
NEXTOP;
OP(ZAPNOT_R):
ASSERTD(a); ASSERTD(B); ASSERTD(C);
reg.d[a] = reg.d[B] & ZapTable[reg.d[C] & 15];
NEXTOP;
OP(ZAPNOT_I):
ASSERTD(a); ASSERTD(B);
reg.d[a] = reg.d[B] & ZapTable[C & 15];
NEXTOP;
OP(EQ_R):
ASSERTD(B); ASSERTD(C);
CMPJMP(reg.d[B] == reg.d[C]);
NEXTOP;
OP(EQ_K):
ASSERTD(B); ASSERTKD(C);
CMPJMP(reg.d[B] == konstd[C]);
NEXTOP;
OP(LT_RR):
ASSERTD(B); ASSERTD(C);
CMPJMP(reg.d[B] < reg.d[C]);
NEXTOP;
OP(LT_RK):
ASSERTD(B); ASSERTKD(C);
CMPJMP(reg.d[B] < konstd[C]);
NEXTOP;
OP(LT_KR):
ASSERTKD(B); ASSERTD(C);
CMPJMP(konstd[B] < reg.d[C]);
NEXTOP;
OP(LE_RR):
ASSERTD(B); ASSERTD(C);
CMPJMP(reg.d[B] <= reg.d[C]);
NEXTOP;
OP(LE_RK):
ASSERTD(B); ASSERTKD(C);
CMPJMP(reg.d[B] <= konstd[C]);
NEXTOP;
OP(LE_KR):
ASSERTKD(B); ASSERTD(C);
CMPJMP(konstd[B] <= reg.d[C]);
NEXTOP;
OP(LTU_RR):
ASSERTD(B); ASSERTD(C);
CMPJMP((VM_UWORD)reg.d[B] < (VM_UWORD)reg.d[C]);
NEXTOP;
OP(LTU_RK):
ASSERTD(B); ASSERTKD(C);
CMPJMP((VM_UWORD)reg.d[B] < (VM_UWORD)konstd[C]);
NEXTOP;
OP(LTU_KR):
ASSERTKD(B); ASSERTD(C);
CMPJMP((VM_UWORD)konstd[B] < (VM_UWORD)reg.d[C]);
NEXTOP;
OP(LEU_RR):
ASSERTD(B); ASSERTD(C);
CMPJMP((VM_UWORD)reg.d[B] <= (VM_UWORD)reg.d[C]);
NEXTOP;
OP(LEU_RK):
ASSERTD(B); ASSERTKD(C);
CMPJMP((VM_UWORD)reg.d[B] <= (VM_UWORD)konstd[C]);
NEXTOP;
OP(LEU_KR):
ASSERTKD(B); ASSERTD(C);
CMPJMP((VM_UWORD)konstd[B] <= (VM_UWORD)reg.d[C]);
NEXTOP;
OP(ADDF_RR):
ASSERTF(a); ASSERTF(B); ASSERTF(C);
reg.f[a] = reg.f[B] + reg.f[C];
NEXTOP;
OP(ADDF_RK):
ASSERTF(a); ASSERTF(B); ASSERTKF(C);
reg.f[a] = reg.f[B] + konstf[C];
NEXTOP;
OP(SUBF_RR):
ASSERTF(a); ASSERTF(B); ASSERTF(C);
reg.f[a] = reg.f[B] - reg.f[C];
NEXTOP;
OP(SUBF_RK):
ASSERTF(a); ASSERTF(B); ASSERTKF(C);
reg.f[a] = reg.f[B] - konstf[C];
NEXTOP;
OP(SUBF_KR):
ASSERTF(a); ASSERTKF(B); ASSERTF(C);
reg.f[a] = konstf[B] - reg.f[C];
NEXTOP;
OP(MULF_RR):
ASSERTF(a); ASSERTF(B); ASSERTF(C);
reg.f[a] = reg.f[B] * reg.f[C];
NEXTOP;
OP(MULF_RK):
ASSERTF(a); ASSERTF(B); ASSERTKF(C);
reg.f[a] = reg.f[B] * konstf[C];
NEXTOP;
OP(DIVF_RR):
ASSERTF(a); ASSERTF(B); ASSERTF(C);
if (reg.f[C] == 0.)
{
ThrowAbortException(X_DIVISION_BY_ZERO, nullptr);
}
reg.f[a] = reg.f[B] / reg.f[C];
NEXTOP;
OP(DIVF_RK):
ASSERTF(a); ASSERTF(B); ASSERTKF(C);
if (konstf[C] == 0.)
{
ThrowAbortException(X_DIVISION_BY_ZERO, nullptr);
}
reg.f[a] = reg.f[B] / konstf[C];
NEXTOP;
OP(DIVF_KR):
ASSERTF(a); ASSERTKF(B); ASSERTF(C);
if (reg.f[C] == 0.)
{
ThrowAbortException(X_DIVISION_BY_ZERO, nullptr);
}
reg.f[a] = konstf[B] / reg.f[C];
NEXTOP;
OP(MODF_RR):
ASSERTF(a); ASSERTF(B); ASSERTF(C);
fb = reg.f[B]; fc = reg.f[C];
Do_MODF:
if (fc == 0.)
{
ThrowAbortException(X_DIVISION_BY_ZERO, nullptr);
}
reg.f[a] = luai_nummod(fb, fc);
NEXTOP;
OP(MODF_RK):
ASSERTF(a); ASSERTF(B); ASSERTKF(C);
fb = reg.f[B]; fc = konstf[C];
goto Do_MODF;
NEXTOP;
OP(MODF_KR):
ASSERTF(a); ASSERTKF(B); ASSERTF(C);
fb = konstf[B]; fc = reg.f[C];
goto Do_MODF;
NEXTOP;
OP(POWF_RR):
ASSERTF(a); ASSERTF(B); ASSERTF(C);
reg.f[a] = g_pow(reg.f[B], reg.f[C]);
NEXTOP;
OP(POWF_RK):
ASSERTF(a); ASSERTF(B); ASSERTKF(C);
reg.f[a] = g_pow(reg.f[B], konstf[C]);
NEXTOP;
OP(POWF_KR):
ASSERTF(a); ASSERTKF(B); ASSERTF(C);
reg.f[a] = g_pow(konstf[B], reg.f[C]);
NEXTOP;
OP(MINF_RR):
ASSERTF(a); ASSERTF(B); ASSERTF(C);
reg.f[a] = reg.f[B] < reg.f[C] ? reg.f[B] : reg.f[C];
NEXTOP;
OP(MINF_RK):
ASSERTF(a); ASSERTF(B); ASSERTKF(C);
reg.f[a] = reg.f[B] < konstf[C] ? reg.f[B] : konstf[C];
NEXTOP;
OP(MAXF_RR):
ASSERTF(a); ASSERTF(B); ASSERTF(C);
reg.f[a] = reg.f[B] > reg.f[C] ? reg.f[B] : reg.f[C];
NEXTOP;
OP(MAXF_RK):
ASSERTF(a); ASSERTF(B); ASSERTKF(C);
reg.f[a] = reg.f[B] > konstf[C] ? reg.f[B] : konstf[C];
NEXTOP;
OP(ATAN2):
ASSERTF(a); ASSERTF(B); ASSERTF(C);
reg.f[a] = g_atan2(reg.f[B], reg.f[C]) * (180 / M_PI);
NEXTOP;
OP(FLOP):
ASSERTF(a); ASSERTF(B);
fb = reg.f[B];
reg.f[a] = (C == FLOP_ABS) ? fabs(fb) : (C == FLOP_NEG) ? -fb : DoFLOP(C, fb);
NEXTOP;
OP(EQF_R):
ASSERTF(B); ASSERTF(C);
if (a & CMP_APPROX)
{
CMPJMP(fabs(reg.f[C] - reg.f[B]) < VM_EPSILON);
}
else
{
CMPJMP(reg.f[C] == reg.f[B]);
}
NEXTOP;
OP(EQF_K):
ASSERTF(B); ASSERTKF(C);
if (a & CMP_APPROX)
{
CMPJMP(fabs(konstf[C] - reg.f[B]) < VM_EPSILON);
}
else
{
CMPJMP(konstf[C] == reg.f[B]);
}
NEXTOP;
OP(LTF_RR):
ASSERTF(B); ASSERTF(C);
if (a & CMP_APPROX)
{
CMPJMP((reg.f[B] - reg.f[C]) < -VM_EPSILON);
}
else
{
CMPJMP(reg.f[B] < reg.f[C]);
}
NEXTOP;
OP(LTF_RK):
ASSERTF(B); ASSERTKF(C);
if (a & CMP_APPROX)
{
CMPJMP((reg.f[B] - konstf[C]) < -VM_EPSILON);
}
else
{
CMPJMP(reg.f[B] < konstf[C]);
}
NEXTOP;
OP(LTF_KR):
ASSERTKF(B); ASSERTF(C);
if (a & CMP_APPROX)
{
CMPJMP((konstf[B] - reg.f[C]) < -VM_EPSILON);
}
else
{
CMPJMP(konstf[B] < reg.f[C]);
}
NEXTOP;
OP(LEF_RR):
ASSERTF(B); ASSERTF(C);
if (a & CMP_APPROX)
{
CMPJMP((reg.f[B] - reg.f[C]) <= -VM_EPSILON);
}
else
{
CMPJMP(reg.f[B] <= reg.f[C]);
}
NEXTOP;
OP(LEF_RK):
ASSERTF(B); ASSERTKF(C);
if (a & CMP_APPROX)
{
CMPJMP((reg.f[B] - konstf[C]) <= -VM_EPSILON);
}
else
{
CMPJMP(reg.f[B] <= konstf[C]);
}
NEXTOP;
OP(LEF_KR):
ASSERTKF(B); ASSERTF(C);
if (a & CMP_APPROX)
{
CMPJMP((konstf[B] - reg.f[C]) <= -VM_EPSILON);
}
else
{
CMPJMP(konstf[B] <= reg.f[C]);
}
NEXTOP;
OP(NEGV2):
ASSERTF(a+1); ASSERTF(B+1);
reg.f[a] = -reg.f[B];
reg.f[a+1] = -reg.f[B+1];
NEXTOP;
OP(ADDV2_RR):
ASSERTF(a+1); ASSERTF(B+1); ASSERTF(C+1);
fcp = &reg.f[C];
fbp = &reg.f[B];
reg.f[a] = fbp[0] + fcp[0];
reg.f[a+1] = fbp[1] + fcp[1];
NEXTOP;
OP(SUBV2_RR):
ASSERTF(a+1); ASSERTF(B+1); ASSERTF(C+1);
fbp = &reg.f[B];
fcp = &reg.f[C];
reg.f[a] = fbp[0] - fcp[0];
reg.f[a+1] = fbp[1] - fcp[1];
NEXTOP;
OP(DOTV2_RR):
ASSERTF(a); ASSERTF(B+1); ASSERTF(C+1);
reg.f[a] = reg.f[B] * reg.f[C] + reg.f[B+1] * reg.f[C+1];
NEXTOP;
OP(MULVF2_RR):
ASSERTF(a+1); ASSERTF(B+1); ASSERTF(C);
fc = reg.f[C];
fbp = &reg.f[B];
Do_MULV2:
reg.f[a] = fbp[0] * fc;
reg.f[a+1] = fbp[1] * fc;
NEXTOP;
OP(MULVF2_RK):
ASSERTF(a+1); ASSERTF(B+1); ASSERTKF(C);
fc = konstf[C];
fbp = &reg.f[B];
goto Do_MULV2;
OP(DIVVF2_RR):
ASSERTF(a+1); ASSERTF(B+1); ASSERTF(C);
fc = reg.f[C];
fbp = &reg.f[B];
Do_DIVV2:
reg.f[a] = fbp[0] / fc;
reg.f[a+1] = fbp[1] / fc;
NEXTOP;
OP(DIVVF2_RK):
ASSERTF(a+1); ASSERTF(B+1); ASSERTKF(C);
fc = konstf[C];
fbp = &reg.f[B];
goto Do_DIVV2;
OP(LENV2):
ASSERTF(a); ASSERTF(B+1);
reg.f[a] = g_sqrt(reg.f[B] * reg.f[B] + reg.f[B+1] * reg.f[B+1]);
NEXTOP;
OP(EQV2_R):
ASSERTF(B+1); ASSERTF(C+1);
fcp = &reg.f[C];
Do_EQV2:
if (a & CMP_APPROX)
{
CMPJMP(fabs(reg.f[B ] - fcp[0]) < VM_EPSILON &&
fabs(reg.f[B+1] - fcp[1]) < VM_EPSILON);
}
else
{
CMPJMP(reg.f[B] == fcp[0] && reg.f[B+1] == fcp[1]);
}
NEXTOP;
OP(EQV2_K):
ASSERTF(B+1); ASSERTKF(C+1);
fcp = &konstf[C];
goto Do_EQV2;
OP(NEGV3):
ASSERTF(a+2); ASSERTF(B+2);
reg.f[a] = -reg.f[B];
reg.f[a+1] = -reg.f[B+1];
reg.f[a+2] = -reg.f[B+2];
NEXTOP;
OP(ADDV3_RR):
ASSERTF(a+2); ASSERTF(B+2); ASSERTF(C+2);
fcp = &reg.f[C];
fbp = &reg.f[B];
reg.f[a] = fbp[0] + fcp[0];
reg.f[a+1] = fbp[1] + fcp[1];
reg.f[a+2] = fbp[2] + fcp[2];
NEXTOP;
OP(SUBV3_RR):
ASSERTF(a+2); ASSERTF(B+2); ASSERTF(C+2);
fbp = &reg.f[B];
fcp = &reg.f[C];
reg.f[a] = fbp[0] - fcp[0];
reg.f[a+1] = fbp[1] - fcp[1];
reg.f[a+2] = fbp[2] - fcp[2];
NEXTOP;
OP(DOTV3_RR):
ASSERTF(a); ASSERTF(B+2); ASSERTF(C+2);
reg.f[a] = reg.f[B] * reg.f[C] + reg.f[B+1] * reg.f[C+1] + reg.f[B+2] * reg.f[C+2];
NEXTOP;
OP(CROSSV_RR):
ASSERTF(a+2); ASSERTF(B+2); ASSERTF(C+2);
fbp = &reg.f[B];
fcp = &reg.f[C];
{
double t[3];
t[2] = fbp[0] * fcp[1] - fbp[1] * fcp[0];
t[1] = fbp[2] * fcp[0] - fbp[0] * fcp[2];
t[0] = fbp[1] * fcp[2] - fbp[2] * fcp[1];
reg.f[a] = t[0]; reg.f[a+1] = t[1]; reg.f[a+2] = t[2];
}
NEXTOP;
OP(MULVF3_RR):
ASSERTF(a+2); ASSERTF(B+2); ASSERTF(C);
fc = reg.f[C];
fbp = &reg.f[B];
Do_MULV3:
reg.f[a] = fbp[0] * fc;
reg.f[a+1] = fbp[1] * fc;
reg.f[a+2] = fbp[2] * fc;
NEXTOP;
OP(MULVF3_RK):
ASSERTF(a+2); ASSERTF(B+2); ASSERTKF(C);
fc = konstf[C];
fbp = &reg.f[B];
goto Do_MULV3;
OP(DIVVF3_RR):
ASSERTF(a+2); ASSERTF(B+2); ASSERTF(C);
fc = reg.f[C];
fbp = &reg.f[B];
Do_DIVV3:
reg.f[a] = fbp[0] / fc;
reg.f[a+1] = fbp[1] / fc;
reg.f[a+2] = fbp[2] / fc;
NEXTOP;
OP(DIVVF3_RK):
ASSERTF(a+2); ASSERTF(B+2); ASSERTKF(C);
fc = konstf[C];
fbp = &reg.f[B];
goto Do_DIVV3;
OP(LENV3):
ASSERTF(a); ASSERTF(B+2);
reg.f[a] = g_sqrt(reg.f[B] * reg.f[B] + reg.f[B+1] * reg.f[B+1] + reg.f[B+2] * reg.f[B+2]);
NEXTOP;
OP(EQV3_R):
ASSERTF(B+2); ASSERTF(C+2);
fcp = &reg.f[C];
Do_EQV3:
if (a & CMP_APPROX)
{
CMPJMP(fabs(reg.f[B ] - fcp[0]) < VM_EPSILON &&
fabs(reg.f[B+1] - fcp[1]) < VM_EPSILON &&
fabs(reg.f[B+2] - fcp[2]) < VM_EPSILON);
}
else
{
CMPJMP(reg.f[B] == fcp[0] && reg.f[B+1] == fcp[1] && reg.f[B+2] == fcp[2]);
}
NEXTOP;
OP(EQV3_K):
ASSERTF(B+2); ASSERTKF(C+2);
fcp = &konstf[C];
goto Do_EQV3;
OP(ADDA_RR):
ASSERTA(a); ASSERTA(B); ASSERTD(C);
c = reg.d[C];
Do_ADDA:
if (reg.a[B] == NULL) // Leave NULL pointers as NULL pointers
{
c = 0;
}
reg.a[a] = (VM_UBYTE *)reg.a[B] + c;
reg.atag[a] = c == 0 ? reg.atag[B] : (int)ATAG_GENERIC;
NEXTOP;
OP(ADDA_RK):
ASSERTA(a); ASSERTA(B); ASSERTKD(C);
c = konstd[C];
goto Do_ADDA;
OP(SUBA):
ASSERTD(a); ASSERTA(B); ASSERTA(C);
reg.d[a] = (VM_UWORD)((VM_UBYTE *)reg.a[B] - (VM_UBYTE *)reg.a[C]);
NEXTOP;
OP(EQA_R):
ASSERTA(B); ASSERTA(C);
CMPJMP(reg.a[B] == reg.a[C]);
NEXTOP;
OP(EQA_K):
ASSERTA(B); ASSERTKA(C);
CMPJMP(reg.a[B] == konsta[C].v);
NEXTOP;
OP(NOP):
NEXTOP;
}
}
catch(VMException *exception)
{
// Try to find a handler for the exception.
PClass *extype = exception->GetClass();
while(--try_depth >= 0)
{
pc = exception_frames[try_depth];
assert(pc->op == OP_CATCH);
while (pc->a > 1)
{
// CATCH must be followed by JMP if it doesn't terminate a catch chain.
assert(pc[1].op == OP_JMP);
PClass *type;
int b = pc->b;
if (pc->a == 2)
{
ASSERTA(b);
type = (PClass *)reg.a[b];
}
else
{
assert(pc->a == 3);
ASSERTKA(b);
assert(konstatag[b] == ATAG_OBJECT);
type = (PClass *)konsta[b].o;
}
ASSERTA(pc->c);
if (type == extype)
{
// Found a handler. Store the exception in pC, skip the JMP,
// and begin executing its code.
reg.a[pc->c] = exception;
reg.atag[pc->c] = ATAG_OBJECT;
pc += 2;
goto begin;
}
// This catch didn't handle it. Try the next one.
pc += 1 + JMPOFS(pc + 1);
assert(pc->op == OP_CATCH);
}
if (pc->a == 1)
{
// Catch any type of VMException. This terminates the chain.
ASSERTA(pc->c);
reg.a[pc->c] = exception;
reg.atag[pc->c] = ATAG_OBJECT;
pc += 1;
goto begin;
}
// This frame failed. Try the next one out.
}
// Nothing caught it. Rethrow and let somebody else deal with it.
throw;
}
catch (CVMAbortException &err)
{
err.MaybePrintMessage();
err.stacktrace.AppendFormat("Called from %s at %s, line %d\n", sfunc->PrintableName.GetChars(), sfunc->SourceFileName.GetChars(), sfunc->PCToLine(pc));
// PrintParameters(reg.param + f->NumParam - B, B);
throw;
}
return 0;
}
static double DoFLOP(int flop, double v)
{
switch(flop)
{
case FLOP_ABS: return fabs(v);
case FLOP_NEG: return -v;
case FLOP_EXP: return g_exp(v);
case FLOP_LOG: return g_log(v);
case FLOP_LOG10: return g_log10(v);
case FLOP_SQRT: return g_sqrt(v);
case FLOP_CEIL: return ceil(v);
case FLOP_FLOOR: return floor(v);
case FLOP_ACOS: return g_acos(v);
case FLOP_ASIN: return g_asin(v);
case FLOP_ATAN: return g_atan(v);
case FLOP_COS: return g_cos(v);
case FLOP_SIN: return g_sin(v);
case FLOP_TAN: return g_tan(v);
case FLOP_ACOS_DEG: return g_acos(v) * (180 / M_PI);
case FLOP_ASIN_DEG: return g_asin(v) * (180 / M_PI);
case FLOP_ATAN_DEG: return g_atan(v) * (180 / M_PI);
case FLOP_COS_DEG: return g_cosdeg(v);
case FLOP_SIN_DEG: return g_sindeg(v);
case FLOP_TAN_DEG: return g_tan(v * (M_PI / 180));
case FLOP_COSH: return g_cosh(v);
case FLOP_SINH: return g_sinh(v);
case FLOP_TANH: return g_tanh(v);
}
assert(0);
return 0;
}
static void DoCast(const VMRegisters &reg, const VMFrame *f, int a, int b, int cast)
{
switch (cast)
{
case CAST_I2F:
ASSERTF(a); ASSERTD(b);
reg.f[a] = reg.d[b];
break;
case CAST_U2F:
ASSERTF(a); ASSERTD(b);
reg.f[a] = unsigned(reg.d[b]);
break;
case CAST_I2S:
ASSERTS(a); ASSERTD(b);
reg.s[a].Format("%d", reg.d[b]);
break;
case CAST_U2S:
ASSERTS(a); ASSERTD(b);
reg.s[a].Format("%u", reg.d[b]);
break;
case CAST_F2I:
ASSERTD(a); ASSERTF(b);
reg.d[a] = (int)reg.f[b];
break;
case CAST_F2U:
ASSERTD(a); ASSERTF(b);
reg.d[a] = (int)(unsigned)reg.f[b];
break;
case CAST_F2S:
ASSERTS(a); ASSERTF(b);
reg.s[a].Format("%.5f", reg.f[b]); // keep this small. For more precise conversion there should be a conversion function.
break;
case CAST_V22S:
ASSERTS(a); ASSERTF(b+1);
reg.s[a].Format("(%.5f, %.5f)", reg.f[b], reg.f[b + 1]);
break;
case CAST_V32S:
ASSERTS(a); ASSERTF(b + 2);
reg.s[a].Format("(%.5f, %.5f, %.5f)", reg.f[b], reg.f[b + 1], reg.f[b + 2]);
break;
case CAST_P2S:
{
ASSERTS(a); ASSERTA(b);
if (reg.a[b] == nullptr) reg.s[a] = "null";
else if (reg.atag[b] == ATAG_OBJECT)
{
auto op = static_cast<DObject*>(reg.a[b]);
if (op->IsKindOf(RUNTIME_CLASS(PClass))) reg.s[a].Format("Class<%s>", static_cast<PClass*>(op)->TypeName.GetChars());
else reg.s[a].Format("Object<%p>", ((DObject*)reg.a[b])->GetClass()->TypeName.GetChars());
}
else
{
reg.s[a].Format("%s<%p>", "Pointer", reg.a[b]);
}
break;
}
case CAST_S2I:
ASSERTD(a); ASSERTS(b);
reg.d[a] = (VM_SWORD)reg.s[b].ToLong();
break;
case CAST_S2F:
ASSERTF(a); ASSERTS(b);
reg.f[a] = reg.s[b].ToDouble();
break;
case CAST_S2N:
ASSERTD(a); ASSERTS(b);
reg.d[a] = reg.s[b].Len() == 0? FName(NAME_None) : FName(reg.s[b]);
break;
case CAST_N2S:
{
ASSERTS(a); ASSERTD(b);
FName name = FName(ENamedName(reg.d[b]));
reg.s[a] = name.IsValidName() ? name.GetChars() : "";
break;
}
case CAST_S2Co:
ASSERTD(a); ASSERTS(b);
reg.d[a] = V_GetColor(NULL, reg.s[b]);
break;
case CAST_Co2S:
ASSERTS(a); ASSERTD(b);
reg.s[a].Format("%02x %02x %02x", PalEntry(reg.d[b]).r, PalEntry(reg.d[b]).g, PalEntry(reg.d[b]).b);
break;
case CAST_S2So:
ASSERTD(a); ASSERTS(b);
reg.d[a] = FSoundID(reg.s[b]);
break;
case CAST_So2S:
ASSERTS(a); ASSERTD(b);
reg.s[a] = S_sfx[reg.d[b]].name;
break;
case CAST_SID2S:
ASSERTS(a); ASSERTD(b);
reg.s[a] = unsigned(reg.d[b]) >= sprites.Size() ? "TNT1" : sprites[reg.d[b]].name;
break;
case CAST_TID2S:
{
ASSERTS(a); ASSERTD(b);
auto tex = TexMan[*(FTextureID*)&(reg.d[b])];
reg.s[a] = tex == nullptr ? "(null)" : tex->Name.GetChars();
break;
}
default:
assert(0);
}
}
//===========================================================================
//
// FillReturns
//
// Fills in an array of pointers to locations to store return values in.
//
//===========================================================================
static void FillReturns(const VMRegisters &reg, VMFrame *frame, VMReturn *returns, const VMOP *retval, int numret)
{
int i, type, regnum;
VMReturn *ret;
assert(REGT_INT == 0 && REGT_FLOAT == 1 && REGT_STRING == 2 && REGT_POINTER == 3);
for (i = 0, ret = returns; i < numret; ++i, ++ret, ++retval)
{
assert(retval->op == OP_RESULT); // opcode
ret->TagOfs = 0;
ret->RegType = type = retval->b;
regnum = retval->c;
assert(!(type & REGT_KONST));
type &= REGT_TYPE;
if (type < REGT_STRING)
{
if (type == REGT_INT)
{
assert(regnum < frame->NumRegD);
ret->Location = &reg.d[regnum];
}
else // type == REGT_FLOAT
{
assert(regnum < frame->NumRegF);
ret->Location = &reg.f[regnum];
}
}
else if (type == REGT_STRING)
{
assert(regnum < frame->NumRegS);
ret->Location = &reg.s[regnum];
}
else
{
assert(type == REGT_POINTER);
assert(regnum < frame->NumRegA);
ret->Location = &reg.a[regnum];
ret->TagOfs = (VM_SHALF)(&frame->GetRegATag()[regnum] - (VM_ATAG *)ret->Location);
}
}
}
//===========================================================================
//
// SetReturn
//
// Used by script code to set a return value.
//
//===========================================================================
static void SetReturn(const VMRegisters &reg, VMFrame *frame, VMReturn *ret, VM_UBYTE regtype, int regnum)
{
const void *src;
VMScriptFunction *func = static_cast<VMScriptFunction *>(frame->Func);
assert(func != NULL && !func->Native);
assert((regtype & ~REGT_KONST) == ret->RegType);
switch (regtype & REGT_TYPE)
{
case REGT_INT:
assert(!(regtype & REGT_MULTIREG));
if (regtype & REGT_KONST)
{
assert(regnum < func->NumKonstD);
src = &func->KonstD[regnum];
}
else
{
assert(regnum < frame->NumRegD);
src = &reg.d[regnum];
}
ret->SetInt(*(int *)src);
break;
case REGT_FLOAT:
if (regtype & REGT_KONST)
{
assert(regnum < func->NumKonstF);
src = &func->KonstF[regnum];
}
else
{
assert(regnum < frame->NumRegF);
src = &reg.f[regnum];
}
if (regtype & REGT_MULTIREG3)
{
ret->SetVector((double *)src);
}
else if (regtype & REGT_MULTIREG2)
{
ret->SetVector2((double *)src);
}
else
{
ret->SetFloat(*(double *)src);
}
break;
case REGT_STRING:
assert(!(regtype & REGT_MULTIREG));
if (regtype & REGT_KONST)
{
assert(regnum < func->NumKonstS);
src = &func->KonstS[regnum];
}
else
{
assert(regnum < frame->NumRegS);
src = &reg.s[regnum];
}
ret->SetString(*(const FString *)src);
break;
case REGT_POINTER:
assert(!(regtype & REGT_MULTIREG));
if (regtype & REGT_KONST)
{
assert(regnum < func->NumKonstA);
ret->SetPointer(func->KonstA[regnum].v, func->KonstATags()[regnum]);
}
else
{
assert(regnum < frame->NumRegA);
ret->SetPointer(reg.a[regnum], reg.atag[regnum]);
}
break;
}
}