From fefc306a546c0a2216aa9dc9c45f61efd0305e09 Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Wed, 16 Sep 2009 01:39:44 +0000 Subject: [PATCH] - Make PClass derive from DObject so that it can participate in garbage collection. - Import VM. SVN r1841 (scripting) --- src/d_dehacked.cpp | 12 +- src/dobjgc.cpp | 9 + src/dobjtype.cpp | 20 +- src/dobjtype.h | 29 +- src/v_video.h | 2 +- zdoom.vcproj | 28 + zscript/vm.h | 791 ++++++++++++++++++++++ zscript/vmdisasm.cpp | 343 ++++++++++ zscript/vmexec.cpp | 185 ++++++ zscript/vmexec.h | 1495 ++++++++++++++++++++++++++++++++++++++++++ zscript/vmframe.cpp | 294 +++++++++ zscript/vmops.h | 210 ++++++ 12 files changed, 3399 insertions(+), 19 deletions(-) create mode 100644 zscript/vm.h create mode 100644 zscript/vmdisasm.cpp create mode 100644 zscript/vmexec.cpp create mode 100644 zscript/vmexec.h create mode 100644 zscript/vmframe.cpp create mode 100644 zscript/vmops.h diff --git a/src/d_dehacked.cpp b/src/d_dehacked.cpp index f639781905..1ac3ff287f 100644 --- a/src/d_dehacked.cpp +++ b/src/d_dehacked.cpp @@ -149,10 +149,10 @@ static TArray WeaponNames; // List of states that are hacked to use a codepointer struct MBFParamState { - FState * state; + FState *state; int pointer; }; -static TArray MBFParamStates; +static TArray MBFParamStates; // Data on how to correctly modify the codepointers struct CodePointerAlias { @@ -1610,9 +1610,9 @@ static void SetPointer(FState *state, PSymbol *sym, int frame = 0) { if (!symname.CompareNoCase(MBFCodePointers[i].name)) { - MBFParamState * newstate = new MBFParamState; - newstate->state = state; - newstate->pointer = i; + MBFParamState newstate; + newstate.state = state; + newstate.pointer = i; MBFParamStates.Push(newstate); break; // No need to cycle through the rest of the list. } @@ -2494,7 +2494,7 @@ static void UnloadDehSupp () // Handle MBF params here, before the required arrays are cleared for (unsigned int i=0; i < MBFParamStates.Size(); i++) { - SetDehParams(MBFParamStates[i]->state, MBFParamStates[i]->pointer); + SetDehParams(MBFParamStates[i].state, MBFParamStates[i].pointer); } MBFParamStates.Clear(); MBFParamStates.ShrinkToFit(); diff --git a/src/dobjgc.cpp b/src/dobjgc.cpp index 1c8e618bdd..0ba5d7c81b 100644 --- a/src/dobjgc.cpp +++ b/src/dobjgc.cpp @@ -327,6 +327,15 @@ static void MarkRoot() SectorMarker->SecNum = 0; } Mark(SectorMarker); + // Mark symbol tables + for (unsigned j = 0; j < PClass::m_Types.Size(); ++j) + { + PClass *cls = PClass::m_Types[j]; + if (cls != NULL) + { + cls->Symbols.MarkSymbols(); + } + } // Mark bot stuff. Mark(bglobal.firstthing); Mark(bglobal.body1); diff --git a/src/dobjtype.cpp b/src/dobjtype.cpp index 03eecc7a95..93586bcbf4 100644 --- a/src/dobjtype.cpp +++ b/src/dobjtype.cpp @@ -467,21 +467,37 @@ const PClass *PClass::NativeClass() const // Symbol tables ------------------------------------------------------------ +IMPLEMENT_ABSTRACT_CLASS(PSymbol); +IMPLEMENT_CLASS(PSymbolConst); +IMPLEMENT_CLASS(PSymbolVariable); +IMPLEMENT_CLASS(PSymbolActionFunction); + PSymbol::~PSymbol() { } +PSymbolTable::PSymbolTable() +: ParentSymbolTable(NULL) +{ +} + PSymbolTable::~PSymbolTable () { ReleaseSymbols(); } -void PSymbolTable::ReleaseSymbols() +void PSymbolTable::MarkSymbols() { for (unsigned int i = 0; i < Symbols.Size(); ++i) { - delete Symbols[i]; + GC::Mark(Symbols[i]); } +} + +void PSymbolTable::ReleaseSymbols() +{ + // The GC will take care of deleting the symbols. We just need to + // clear our references to them. Symbols.Clear(); } diff --git a/src/dobjtype.h b/src/dobjtype.h index 88defaefee..d58ce2f009 100644 --- a/src/dobjtype.h +++ b/src/dobjtype.h @@ -16,8 +16,10 @@ enum ESymbolType SYM_ActionFunction }; -struct PSymbol +class PSymbol : public DObject { + DECLARE_ABSTRACT_CLASS(PSymbol, DObject); +public: virtual ~PSymbol(); ESymbolType SymbolType; @@ -29,8 +31,10 @@ protected: // A constant value --------------------------------------------------------- -struct PSymbolConst : public PSymbol +class PSymbolConst : public PSymbol { + DECLARE_CLASS(PSymbolConst, PSymbol); +public: int ValueType; union { @@ -39,17 +43,21 @@ struct PSymbolConst : public PSymbol }; PSymbolConst(FName name) : PSymbol(name, SYM_Const) {} + PSymbolConst() : PSymbol(NAME_None, SYM_Const) {} }; // A variable --------------------------------------------------------- -struct PSymbolVariable : public PSymbol +class PSymbolVariable : public PSymbol { + DECLARE_CLASS(PSymbolVariable, PSymbol); +public: FExpressionType ValueType; int size; intptr_t offset; PSymbolVariable(FName name) : PSymbol(name, SYM_Variable) {} + PSymbolVariable() : PSymbol(NAME_None, SYM_Variable) {} }; // An action function ------------------------------------------------------- @@ -74,26 +82,27 @@ struct FState; struct StateCallData; typedef void (*actionf_p)(AActor *self, AActor *stateowner, FState *state, int parameters, StateCallData *statecall); -struct PSymbolActionFunction : public PSymbol +class PSymbolActionFunction : public PSymbol { + DECLARE_CLASS(PSymbolActionFunction, PSymbol); +public: FString Arguments; actionf_p Function; int defaultparameterindex; PSymbolActionFunction(FName name) : PSymbol(name, SYM_ActionFunction) {} + PSymbolActionFunction() : PSymbol(NAME_None, SYM_ActionFunction) {} }; // A symbol table ----------------------------------------------------------- -class PSymbolTable +struct PSymbolTable { -public: - PSymbolTable() : ParentSymbolTable(NULL) - { - } - + PSymbolTable(); ~PSymbolTable(); + void MarkSymbols(); + // Sets the table to use for searches if this one doesn't contain the // requested symbol. void SetParentTable (PSymbolTable *parent); diff --git a/src/v_video.h b/src/v_video.h index c6857d55a4..84a59db509 100644 --- a/src/v_video.h +++ b/src/v_video.h @@ -382,7 +382,7 @@ protected: DFrameBuffer () {} private: - DWORD LastMS, LastSec, FrameCount, LastCount, LastTic; + uint32 LastMS, LastSec, FrameCount, LastCount, LastTic; }; diff --git a/zdoom.vcproj b/zdoom.vcproj index 2b0c1b8994..a416fa6a8a 100644 --- a/zdoom.vcproj +++ b/zdoom.vcproj @@ -6446,6 +6446,34 @@ > + + + + + + + + + + + + + + diff --git a/zscript/vm.h b/zscript/vm.h new file mode 100644 index 0000000000..8a8b9e9f8a --- /dev/null +++ b/zscript/vm.h @@ -0,0 +1,791 @@ +#ifndef VM_H +#define VM_H + +#include "zstring.h" +#include "dobject.h" + +#define MAX_RETURNS 8 // Maximum number of results a function called by script code can return +#define MAX_TRY_DEPTH 8 // Maximum number of nested TRYs in a single function + + +typedef unsigned char VM_UBYTE; +typedef signed char VM_SBYTE; +typedef unsigned short VM_UHALF; +typedef signed short VM_SHALF; +typedef unsigned int VM_UWORD; +typedef signed int VM_SWORD; + +#define VM_EPSILON (1/1024.0) + +enum +{ +#include "vmops.h" +NUM_OPS +}; + +// Flags for A field of CMPS +enum +{ + CMP_CHECK = 1, + + CMP_EQ = 0, + CMP_LT = 2, + CMP_LE = 4, + CMP_METHOD_MASK = 6, + + CMP_BK = 8, + CMP_CK = 16, + CMP_APPROX = 32, +}; + +// Floating point operations for FLOP +enum +{ + FLOP_ABS, + FLOP_NEG, + FLOP_ACOS, + FLOP_ASIN, + FLOP_ATAN, + FLOP_COS, + FLOP_COSH, + FLOP_EXP, + FLOP_LOG, + FLOP_LOG10, + FLOP_SIN, + FLOP_SINH, + FLOP_TAN, + FLOP_TANH, + FLOP_SQRT, + FLOP_CEIL, + FLOP_FLOOR, +}; + +// Cast operations +enum +{ + CAST_I2F, + CAST_I2S, + CAST_F2I, + CAST_F2S, + CAST_P2S, + CAST_S2I, + CAST_S2F, +}; + +// Register types for VMParam +enum +{ + REGT_INT = 0, + REGT_FLOAT = 1, + REGT_STRING = 2, + REGT_POINTER = 3, + REGT_TYPE = 3, + + REGT_KONST = 4, + REGT_MULTIREG = 8, // (e.g. a vector) + REGT_FINAL = 16, // used with RET: this is the final return value + REGT_ADDROF = 32, // used with PARAM: pass address of this register + + REGT_NIL = 255 // parameter was omitted +}; + +// Tags for address registers +enum +{ + ATAG_GENERIC, // pointer to something; we don't care what + ATAG_OBJECT, // pointer to an object; will be followed by GC + + // The following are all for documentation during debugging and are + // functionally no different than ATAG_GENERIC. + + ATAG_FRAMEPOINTER, // pointer to extra stack frame space for this function + ATAG_DREGISTER, // pointer to a data register + ATAG_FREGISTER, // pointer to a float register + ATAG_SREGISTER, // pointer to a string register + ATAG_AREGISTER, // pointer to an address register + +}; + +class VMFunction : public DObject +{ + DECLARE_CLASS(VMFunction, DObject); +public: + bool Native; +}; + +enum EVMOpMode +{ + MODE_ASHIFT = 0, + MODE_BSHIFT = 4, + MODE_CSHIFT = 8, + MODE_BCSHIFT = 12, + + MODE_ATYPE = 15 << MODE_ASHIFT, + MODE_BTYPE = 15 << MODE_BSHIFT, + MODE_CTYPE = 15 << MODE_CSHIFT, + MODE_BCTYPE = 31 << MODE_BCSHIFT, + + MODE_I = 0, + MODE_F, + MODE_S, + MODE_P, + MODE_V, + MODE_X, + MODE_KI, + MODE_KF, + MODE_KS, + MODE_KP, + MODE_KV, + MODE_UNUSED, + MODE_IMMS, + MODE_IMMZ, + MODE_JOINT, + + MODE_PARAM, + MODE_THROW, + MODE_CATCH, + MODE_CAST, + + MODE_AI = MODE_I << MODE_ASHIFT, + MODE_AF = MODE_F << MODE_ASHIFT, + MODE_AS = MODE_S << MODE_ASHIFT, + MODE_AP = MODE_P << MODE_ASHIFT, + MODE_AV = MODE_V << MODE_ASHIFT, + MODE_AX = MODE_X << MODE_ASHIFT, + MODE_AKP = MODE_KP << MODE_ASHIFT, + MODE_AUNUSED = MODE_UNUSED << MODE_ASHIFT, + MODE_AIMMS = MODE_IMMS << MODE_ASHIFT, + MODE_AIMMZ = MODE_IMMZ << MODE_ASHIFT, + + MODE_BI = MODE_I << MODE_BSHIFT, + MODE_BF = MODE_F << MODE_BSHIFT, + MODE_BS = MODE_S << MODE_BSHIFT, + MODE_BP = MODE_P << MODE_BSHIFT, + MODE_BV = MODE_V << MODE_BSHIFT, + MODE_BX = MODE_X << MODE_BSHIFT, + MODE_BKI = MODE_KI << MODE_BSHIFT, + MODE_BKF = MODE_KF << MODE_BSHIFT, + MODE_BKS = MODE_KS << MODE_BSHIFT, + MODE_BKP = MODE_KP << MODE_BSHIFT, + MODE_BKV = MODE_KV << MODE_BSHIFT, + MODE_BUNUSED = MODE_UNUSED << MODE_BSHIFT, + MODE_BIMMS = MODE_IMMS << MODE_BSHIFT, + MODE_BIMMZ = MODE_IMMZ << MODE_BSHIFT, + + MODE_CI = MODE_I << MODE_CSHIFT, + MODE_CF = MODE_F << MODE_CSHIFT, + MODE_CS = MODE_S << MODE_CSHIFT, + MODE_CP = MODE_P << MODE_CSHIFT, + MODE_CV = MODE_V << MODE_CSHIFT, + MODE_CX = MODE_X << MODE_CSHIFT, + MODE_CKI = MODE_KI << MODE_CSHIFT, + MODE_CKF = MODE_KF << MODE_CSHIFT, + MODE_CKS = MODE_KS << MODE_CSHIFT, + MODE_CKP = MODE_KP << MODE_CSHIFT, + MODE_CKV = MODE_KV << MODE_CSHIFT, + MODE_CUNUSED = MODE_UNUSED << MODE_CSHIFT, + MODE_CIMMS = MODE_IMMS << MODE_CSHIFT, + MODE_CIMMZ = MODE_IMMZ << MODE_CSHIFT, + + MODE_BCJOINT = (MODE_JOINT << MODE_BSHIFT) | (MODE_JOINT << MODE_CSHIFT), + MODE_BCKI = MODE_KI << MODE_BCSHIFT, + MODE_BCKF = MODE_KF << MODE_BCSHIFT, + MODE_BCKS = MODE_KS << MODE_BCSHIFT, + MODE_BCKP = MODE_KP << MODE_BCSHIFT, + MODE_BCIMMS = MODE_IMMS << MODE_BCSHIFT, + MODE_BCIMMZ = MODE_IMMZ << MODE_BCSHIFT, + MODE_BCPARAM = MODE_PARAM << MODE_BCSHIFT, + MODE_BCTHROW = MODE_THROW << MODE_BCSHIFT, + MODE_BCCATCH = MODE_CATCH << MODE_BCSHIFT, + MODE_BCCAST = MODE_CAST << MODE_BCSHIFT, + + MODE_ABCJOINT = (MODE_JOINT << MODE_ASHIFT) | MODE_BCJOINT, +}; + +struct VMOpInfo +{ + const char *Name; + int Mode; +}; + +extern const VMOpInfo OpInfo[NUM_OPS]; + +struct VMReturn +{ + void *Location; + VM_SHALF RegNum; // Used to find ObjFlag index for pointers; set negative if the caller is native code and doesn't care + VM_UBYTE RegType; // Same as VMParam RegType, except REGT_KONST is invalid; only used by asserts + + void SetInt(int val) + { + *(int *)Location = val; + } + void SetFloat(double val) + { + *(double *)Location = val; + } + void SetVector(const double val[3]) + { + ((double *)Location)[0] = val[0]; + ((double *)Location)[1] = val[1]; + ((double *)Location)[2] = val[2]; + } + void SetString(const FString &val) + { + *(FString *)Location = val; + } + void SetPointer(void *val) + { + *(void **)Location = val; + } + + void IntAt(int *loc) + { + Location = loc; + RegNum = -1; + RegType = REGT_INT; + } + void FloatAt(double *loc) + { + Location = loc; + RegNum = -1; + RegType = REGT_FLOAT; + } + void StringAt(FString *loc) + { + Location = loc; + RegNum = -1; + RegType = REGT_STRING; + } + void PointerAt(void **loc) + { + Location = loc; + RegNum = -1; + RegType = REGT_POINTER; + } +}; + +struct VMRegisters; + + +struct VMValue +{ + union + { + int i; + struct { void *a; int atag; }; + double f; + struct { int pad[3]; int Type; }; + struct { int foo[4]; } biggest; + }; + + // Unfortunately, FString cannot be used directly. + // Fortunately, it is relatively simple. + FString &s() { return *(FString *)&a; } + const FString &s() const { return *(FString *)&a; } + + VMValue() + { + a = NULL; + Type = REGT_NIL; + } + ~VMValue() + { + Kill(); + } + VMValue(const VMValue &o) + { + biggest = o.biggest; + if (Type == REGT_STRING) + { + ::new(&s()) FString(o.s()); + } + } + VMValue(int v) + { + i = v; + Type = REGT_INT; + } + VMValue(double v) + { + f = v; + Type = REGT_FLOAT; + } + VMValue(const char *s) + { + ::new(&a) FString(s); + Type = REGT_STRING; + } + VMValue(const FString &s) + { + ::new(&a) FString(s); + Type = REGT_STRING; + } + VMValue(DObject *v) + { + a = v; + atag = ATAG_OBJECT; + Type = REGT_POINTER; + } + VMValue(void *v) + { + a = v; + atag = ATAG_GENERIC; + Type = REGT_POINTER; + } + VMValue(void *v, int tag) + { + a = v; + atag = tag; + Type = REGT_POINTER; + } + VMValue &operator=(const VMValue &o) + { + if (o.Type == REGT_STRING) + { + if (Type == REGT_STRING) + { + s() = o.s(); + } + else + { + new(&s()) FString(o.s()); + Type = REGT_STRING; + } + } + else + { + Kill(); + biggest = o.biggest; + } + return *this; + } + VMValue &operator=(int v) + { + Kill(); + i = v; + Type = REGT_INT; + return *this; + } + VMValue &operator=(double v) + { + Kill(); + f = v; + Type = REGT_FLOAT; + return *this; + } + VMValue &operator=(const FString &v) + { + if (Type == REGT_STRING) + { + s() = v; + } + else + { + ::new(&s()) FString(v); + Type = REGT_STRING; + } + return *this; + } + VMValue &operator=(const char *v) + { + if (Type == REGT_STRING) + { + s() = v; + } + else + { + ::new(&s()) FString(v); + Type = REGT_STRING; + } + return *this; + } + VMValue &operator=(DObject *v) + { + Kill(); + a = v; + atag = ATAG_OBJECT; + Type = REGT_POINTER; + return *this; + } + VMValue &operator=(void *v) + { + Kill(); + a = v; + atag = ATAG_GENERIC; + Type = REGT_POINTER; + return *this; + } + void SetNil() + { + Kill(); + Type = REGT_NIL; + } + bool operator==(const VMValue &o) + { + return Test(o) == 0; + } + bool operator!=(const VMValue &o) + { + return Test(o) != 0; + } + bool operator< (const VMValue &o) + { + return Test(o) < 0; + } + bool operator<=(const VMValue &o) + { + return Test(o) <= 0; + } + bool operator> (const VMValue &o) + { + return Test(o) > 0; + } + bool operator>=(const VMValue &o) + { + return Test(o) >= 0; + } + int Test(const VMValue &o, int inexact=false) + { + double diff; + + if (Type == o.Type) + { + switch(Type) + { + case REGT_NIL: + return 0; + + case REGT_INT: + return i - o.i; + + case REGT_FLOAT: + diff = f - o.f; +do_double: if (inexact) + { + return diff < -VM_EPSILON ? -1 : diff > VM_EPSILON ? 1 : 0; + } + return diff < 0 ? -1 : diff > 0 ? 1 : 0; + + case REGT_STRING: + return inexact ? s().CompareNoCase(o.s()) : s().Compare(o.s()); + + case REGT_POINTER: + return int((const VM_UBYTE *)a - (const VM_UBYTE *)o.a); + } + assert(0); // Should not get here + return 2; + } + if (Type == REGT_FLOAT && o.Type == REGT_INT) + { + diff = f - o.i; + goto do_double; + } + if (Type == REGT_INT && o.Type == REGT_FLOAT) + { + diff = i - o.f; + goto do_double; + } + // Bad comparison + return 2; + } + FString ToString() + { + if (Type == REGT_STRING) + { + return s(); + } + else if (Type == REGT_NIL) + { + return "nil"; + } + FString t; + if (Type == REGT_INT) + { + t.Format ("%d", i); + } + else if (Type == REGT_FLOAT) + { + t.Format ("%.14g", f); + } + else if (Type == REGT_POINTER) + { + // FIXME + t.Format ("Object: %p", a); + } + return t; + } + int ToInt() + { + if (Type == REGT_INT) + { + return i; + } + if (Type == REGT_FLOAT) + { + return int(f); + } + if (Type == REGT_STRING) + { + return s().ToLong(); + } + // FIXME + return 0; + } + double ToDouble() + { + if (Type == REGT_FLOAT) + { + return f; + } + if (Type == REGT_INT) + { + return i; + } + if (Type == REGT_STRING) + { + return s().ToDouble(); + } + // FIXME + return 0; + } + void Kill() + { + if (Type == REGT_STRING) + { + s().~FString(); + } + } +}; + +// VM frame layout: +// VMFrame header +// parameter stack - 16 byte boundary, 16 bytes each +// double registers - 8 bytes each +// string registers - 4 or 8 bytes each +// address registers - 4 or 8 bytes each +// data registers - 4 bytes each +// address register tags-1 byte each +// extra space - 16 byte boundary +struct VMFrame +{ + VMFrame *ParentFrame; + VMFunction *Func; + VM_UBYTE NumRegD; + VM_UBYTE NumRegF; + VM_UBYTE NumRegS; + VM_UBYTE NumRegA; + VM_UHALF MaxParam; + VM_UHALF NumParam; // current number of parameters + + static int FrameSize(int numregd, int numregf, int numregs, int numrega, int numparam, int numextra) + { + int size = (sizeof(VMFrame) + 15) & ~15; + size += numparam * sizeof(VMValue); + size += numregf * sizeof(double); + size += numrega * (sizeof(void *) + sizeof(VM_UBYTE)); + size += numregs * sizeof(FString); + size += numregd * sizeof(int); + if (numextra != 0) + { + size = (size + 15) & ~15; + size += numextra; + } + return size; + } + + int *GetRegD() const + { + return (int *)(GetRegA() + NumRegA); + } + + double *GetRegF() const + { + return (double *)(GetParam() + MaxParam); + } + + FString *GetRegS() const + { + return (FString *)(GetRegF() + NumRegF); + } + + void **GetRegA() const + { + return (void **)(GetRegS() + NumRegS); + } + + VM_UBYTE *GetRegATag() const + { + return (VM_UBYTE *)(GetRegD() + NumRegD); + } + + VMValue *GetParam() const + { + return (VMValue *)(((size_t)(this + 1) + 15) & ~15); + } + + void *GetExtra() const + { + VM_UBYTE *ptag = GetRegATag(); + ptrdiff_t ofs = ptag - (VM_UBYTE *)this; + return (VM_UBYTE *)this + ((ofs + NumRegA + 15) & ~15); + } + + void GetAllRegs(int *&d, double *&f, FString *&s, void **&a, VM_UBYTE *&atag, VMValue *¶m) const + { + // Calling the individual functions produces suboptimal code. :( + param = GetParam(); + f = (double *)(param + MaxParam); + s = (FString *)(f + NumRegF); + a = (void **)(s + NumRegS); + d = (int *)(a + NumRegA); + atag = (VM_UBYTE *)(d + NumRegD); + } + + void InitRegS(); +}; + +struct VMRegisters +{ + VMRegisters(const VMFrame *frame) + { + frame->GetAllRegs(d, f, s, a, atag, param); + } + + VMRegisters(const VMRegisters &o) + : d(o.d), f(o.f), s(o.s), a(o.a), atag(o.atag), param(o.param) + { } + + int *d; + double *f; + FString *s; + void **a; + VM_UBYTE *atag; + VMValue *param; +}; + +struct VMException : public DObject +{ + DECLARE_CLASS(VMFunction, DObject); +}; + +class VMScriptFunction : public VMFunction +{ + DECLARE_CLASS(VMScriptFunction, VMFunction); +public: + const VM_UBYTE *Code; + int *KonstD; + double *KonstF; + FString *KonstS; + void **KonstA; + int ExtraSpace; + int NumCodeBytes; + VM_UBYTE NumRegD; + VM_UBYTE NumRegF; + VM_UBYTE NumRegS; + VM_UBYTE NumRegA; + VM_UBYTE NumKonstD; + VM_UBYTE NumKonstF; + VM_UBYTE NumKonstS; + VM_UBYTE NumKonstA; + VM_UHALF MaxParam; // Maximum number of parameters this function has on the stack at once + VM_UBYTE NumArgs; // Number of arguments this function takes +}; + +class VMFrameStack +{ +public: + VMFrameStack(); + ~VMFrameStack(); + VMFrame *AllocFrame(int numregd, int numregf, int numregs, int numrega); + VMFrame *AllocFrame(VMScriptFunction *func); + VMFrame *PopFrame(); + VMFrame *TopFrame() + { + assert(Blocks != NULL && Blocks->LastFrame != NULL); + return Blocks->LastFrame; + } + int Call(VMFunction *func, VMValue *params, int numparams, VMReturn *results, int numresults, VMException **trap=NULL); +private: + enum { BLOCK_SIZE = 4096 }; // Default block size + struct BlockHeader + { + BlockHeader *NextBlock; + VMFrame *LastFrame; + VM_UBYTE *FreeSpace; + int BlockSize; + }; + BlockHeader *Blocks; + BlockHeader *UnusedBlocks; + VMFrame *Alloc(int size); +}; + +class VMNativeFunction : public VMFunction +{ + DECLARE_CLASS(VMNativeFunction, VMFunction); +public: + // Return value is the number of results. + int (*NativeCall)(VMFrameStack *stack, VMValue *param, int numparam, VMReturn *ret, int numret); +}; + + +class VMParamFiller +{ +public: + VMParamFiller(const VMFrame *frame) : Reg(frame), RegD(0), RegF(0), RegS(0), RegA(0) {} + VMParamFiller(const VMRegisters *reg) : Reg(*reg), RegD(0), RegF(0), RegS(0), RegA(0) {} + + void ParamInt(int val) + { + Reg.d[RegD++] = val; + } + + void ParamFloat(double val) + { + Reg.f[RegF++] = val; + } + + void ParamString(FString &val) + { + Reg.s[RegS++] = val; + } + + void ParamString(const char *val) + { + Reg.s[RegS++] = val; + } + + void ParamObject(DObject *obj) + { + Reg.a[RegA] = obj; + Reg.atag[RegA] = true; + RegA++; + } + + void ParamPointer(void *ptr) + { + Reg.a[RegA] = ptr; + Reg.atag[RegA] = false; + RegA++; + } + +private: + const VMRegisters Reg; + int RegD, RegF, RegS, RegA; +}; + + +enum EVMEngine +{ + VMEngine_Default, + VMEngine_Unchecked, + VMEngine_Checked +}; + +void VMSelectEngine(EVMEngine engine); +extern int (*VMExec)(VMFrameStack *stack, const VM_UBYTE *pc, VMReturn *ret, int numret); +void VMFillParams(VMValue *params, VMFrame *callee, int numparam); + +void VMDisasm(const VM_UBYTE *code, int codesize, const VMScriptFunction *func); + +#endif diff --git a/zscript/vmdisasm.cpp b/zscript/vmdisasm.cpp new file mode 100644 index 0000000000..7ca2a3dc33 --- /dev/null +++ b/zscript/vmdisasm.cpp @@ -0,0 +1,343 @@ +#include "vm.h" + +#define LI MODE_AI | MODE_BCJOINT | MODE_BCIMMS +#define LKI MODE_AI | MODE_BCJOINT | MODE_BCKI +#define LKF MODE_AF | MODE_BCJOINT | MODE_BCKF +#define LKS MODE_AS | MODE_BCJOINT | MODE_BCKS +#define LKP MODE_AP | MODE_BCJOINT | MODE_BCKP +#define LFP MODE_AP | MODE_BUNUSED | MODE_CUNUSED + +#define RIRPKI MODE_AI | MODE_BP | MODE_CKI +#define RIRPRI MODE_AI | MODE_BP | MODE_CI +#define RFRPKI MODE_AF | MODE_BP | MODE_CKI +#define RFRPRI MODE_AF | MODE_BP | MODE_CI +#define RSRPKI MODE_AS | MODE_BP | MODE_CKI +#define RSRPRI MODE_AS | MODE_BP | MODE_CI +#define RPRPKI MODE_AP | MODE_BP | MODE_CKI +#define RPRPRI MODE_AP | MODE_BP | MODE_CI +#define RVRPKI MODE_AV | MODE_BP | MODE_CKI +#define RVRPRI MODE_AV | MODE_BP | MODE_CI +#define RIRPI8 MODE_AI | MODE_BP | MODE_CIMMZ + +#define RPRIKI MODE_AP | MODE_BI | MODE_CKI +#define RPRIRI MODE_AP | MODE_BI | MODE_CI +#define RPRFKI MODE_AP | MODE_BF | MODE_CKI +#define RPRFRI MODE_AP | MODE_BF | MODE_CI +#define RPRSKI MODE_AP | MODE_BS | MODE_CKI +#define RPRSRI MODE_AP | MODE_BS | MODE_CI +#define RPRPKI MODE_AP | MODE_BP | MODE_CKI +#define RPRPRI MODE_AP | MODE_BP | MODE_CI +#define RPRVKI MODE_AP | MODE_BV | MODE_CKI +#define RPRVRI MODE_AP | MODE_BV | MODE_CI +#define RPRII8 MODE_AP | MODE_BI | MODE_CIMMZ + +#define RIRI MODE_AI | MODE_BI | MODE_CUNUSED +#define RFRF MODE_AF | MODE_BF | MODE_CUNUSED +#define RSRS MODE_AS | MODE_BS | MODE_CUNUSED +#define RPRP MODE_AP | MODE_BP | MODE_CUNUSED +#define RXRXI8 MODE_AX | MODE_BX | MODE_CIMMZ +#define RPRPRP MODE_AP | MODE_BP | MODE_CP +#define RPRPKP MODE_AP | MODE_BP | MODE_CKP + +#define RII16 MODE_AI | MODE_BCJOINT | MODE_BCIMMS +#define I24 MODE_ABCJOINT +#define I8 MODE_AIMMZ | MODE_BUNUSED | MODE_CUNUSED +#define __BCP MODE_AUNUSED | MODE_BCJOINT | MODE_BCPARAM +#define RPI8I8 MODE_AP | MODE_BIMMZ | MODE_CIMMZ +#define KPI8I8 MODE_AKP | MODE_BIMMZ | MODE_CIMMZ +#define I8BCP MODE_AIMMZ | MODE_BCJOINT | MODE_BCPARAM +#define THROW MODE_AIMMZ | MODE_BCTHROW +#define CATCH MODE_AIMMZ | MODE_BCCATCH +#define CAST MODE_AX | MODE_BX | MODE_CIMMZ | MODE_BCCAST + +#define RSRSRS MODE_AS | MODE_BS | MODE_CS +#define RIRS MODE_AI | MODE_BS | MODE_CUNUSED +#define I8RXRX MODE_AIMMZ | MODE_BX | MODE_CX + +#define RIRIRI MODE_AI | MODE_BI | MODE_CI +#define RIRII8 MODE_AI | MODE_BI | MODE_CIMMZ +#define RIRIKI MODE_AI | MODE_BI | MODE_CKI +#define RIKIRI MODE_AI | MODE_BKI | MODE_CI +#define RIKII8 MODE_AI | MODE_BKI | MODE_CIMMZ +#define RIRIIs MODE_AI | MODE_BI | MODE_CIMMS +#define RIRI MODE_AI | MODE_BI | MODE_CUNUSED +#define I8RIRI MODE_AIMMZ | MODE_BI | MODE_CI +#define I8RIKI MODE_AIMMZ | MODE_BI | MODE_CKI +#define I8KIRI MODE_AIMMZ | MODE_BKI | MODE_CI + +#define RFRFRF MODE_AF | MODE_BF | MODE_CF +#define RFRFKF MODE_AF | MODE_BF | MODE_CKF +#define RFKFRF MODE_AF | MODE_BKF | MODE_CF +#define I8RFRF MODE_AIMMZ | MODE_BF | MODE_CF +#define I8RFKF MODE_AIMMZ | MODE_BF | MODE_CKF +#define I8KFRF MODE_AIMMZ | MODE_BKF | MODE_CF +#define RFRFI8 MODE_AF | MODE_BF | MODE_CIMMZ + +#define RVRV MODE_AV | MODE_BV | MODE_CUNUSED +#define RVRVRV MODE_AV | MODE_BV | MODE_CV +#define RVRVKV MODE_AV | MODE_BV | MODE_CKV +#define RVKVRV MODE_AV | MODE_BKV | MODE_CV +#define RFRV MODE_AF | MODE_BV | MODE_CUNUSED +#define I8RVRV MODE_AIMMZ | MODE_BV | MODE_CV +#define I8RVKV MODE_AIMMZ | MODE_BV | MODE_CKV + +#define RPRPRI MODE_AP | MODE_BP | MODE_CI +#define RPRPKI MODE_AP | MODE_BP | MODE_CKI +#define RIRPRP MODE_AI | MODE_BP | MODE_CP +#define I8RPRP MODE_AIMMZ | MODE_BP | MODE_CP +#define I8RPKP MODE_AIMMZ | MODE_BP | MODE_CKP + +const VMOpInfo OpInfo[NUM_OPS] = +{ +#define xx(op, name, mode) { #name, mode } +#include "vmops.h" +}; + +#ifdef WORDS_BIGENDIAN +#define JMPOFS(x) ((*(VM_SWORD *)(x) << 8) >> 6) +#else +#define JMPOFS(x) ((*(VM_SWORD *)(x) >> 6) & ~3) +#endif + +static int print_reg(int col, int arg, int mode, int immshift, const VMScriptFunction *func); + +void VMDisasm(const VM_UBYTE *code, int codesize, const VMScriptFunction *func) +{ + const char *name; + int col; + int mode; + int a; + + for (int i = 0; i < codesize; i += 4) + { + name = OpInfo[code[i]].Name; + mode = OpInfo[code[i]].Mode; + a = code[i+1]; + + // String comparison encodes everything in a single instruction. + if (code[i] == OP_CMPS) + { + switch (a & CMP_METHOD_MASK) + { + case CMP_EQ: name = "eq"; break; + case CMP_LT: name = "lt"; break; + case CMP_LE: name = "le"; break; + } + mode = MODE_AIMMZ; + mode |= (a & CMP_BK) ? MODE_BKS : MODE_BS; + mode |= (a & CMP_CK) ? MODE_CKS : MODE_CS; + a &= CMP_CHECK | CMP_APPROX; + } + + printf("%08x: %02x%02x%02x%02x %-8s", i, code[i], code[i+1], code[i+2], code[i+3], name); + col = 0; + switch (code[i]) + { + case OP_JMP: + case OP_TRY: + col = printf("%08x", i + 4 + JMPOFS(&code[i])); + break; + + case OP_RET: + if (code[i+2] != REGT_NIL) + { + if ((code[i+2] & REGT_FINAL) && a == 0) + { + col = print_reg(0, *(VM_UHALF *)&code[i+2], MODE_PARAM, 16, func); + } + else + { + col = print_reg(0, a, (mode & MODE_ATYPE) >> MODE_ASHIFT, 24, func); + col += print_reg(col, *(VM_UHALF *)&code[i+2], MODE_PARAM, 16, func); + if (code[i+2] & REGT_FINAL) + { + col += printf(" [final]"); + } + } + } + break; + + default: + if ((mode & MODE_BCTYPE) == MODE_BCCAST) + { + switch (code[i+3]) + { + case CAST_I2F: + mode = MODE_AF | MODE_BI | MODE_CUNUSED; + break; + case CAST_I2S: + mode = MODE_AS | MODE_BI | MODE_CUNUSED; + break; + case CAST_F2I: + mode = MODE_AI | MODE_BF | MODE_CUNUSED; + break; + case CAST_F2S: + mode = MODE_AS | MODE_BF | MODE_CUNUSED; + break; + case CAST_P2S: + mode = MODE_AS | MODE_BP | MODE_CUNUSED; + break; + case CAST_S2I: + mode = MODE_AI | MODE_BS | MODE_CUNUSED; + break; + case CAST_S2F: + mode = MODE_AF | MODE_BS | MODE_CUNUSED; + break; + default: + mode = MODE_AX | MODE_BX | MODE_CIMMZ; + break; + } + } + col = print_reg(0, a, (mode & MODE_ATYPE) >> MODE_ASHIFT, 24, func); + if ((mode & MODE_BCTYPE) == MODE_BCTHROW) + { + mode = (code[i+1] == 0) ? (MODE_BP | MODE_CUNUSED) : (MODE_BKP | MODE_CUNUSED); + } + else if ((mode & MODE_BCTYPE) == MODE_BCCATCH) + { + switch (code[i+1]) + { + case 0: + mode = MODE_BUNUSED | MODE_CUNUSED; + break; + case 1: + mode = MODE_BUNUSED | MODE_CP; + break; + case 2: + mode = MODE_BP | MODE_CP; + break; + case 3: + mode = MODE_BKP | MODE_CP; + break; + default: + mode = MODE_BIMMZ | MODE_CIMMZ; + break; + } + } + if ((mode & (MODE_BTYPE | MODE_CTYPE)) == MODE_BCJOINT) + { + col += print_reg(col, *(VM_UHALF *)&code[i+2], (mode & MODE_BCTYPE) >> MODE_BCSHIFT, 16, func); + } + else + { + col += print_reg(col, code[i+2], (mode & MODE_BTYPE) >> MODE_BSHIFT, 24, func); + col += print_reg(col, code[i+3], (mode & MODE_CTYPE) >> MODE_CSHIFT, 24, func); + } + break; + } + if (col > 30) + { + col = 30; + } + printf("%*c", 30 - col, ';'); + if (code[i] == OP_JMP || code[i] == OP_TRY) + { + printf("%d\n", JMPOFS(&code[i]) >> 2); + } + else + { + printf("%d,%d,%d\n", code[i+1], code[i+2], code[i+3]); + } + } +} + +static int print_reg(int col, int arg, int mode, int immshift, const VMScriptFunction *func) +{ + if (mode == MODE_UNUSED) + { + return 0; + } + if (col > 0) + { + col = printf(","); + } + switch(mode) + { + case MODE_I: + return col+printf("d%d", arg); + case MODE_F: + return col+printf("f%d", arg); + case MODE_S: + return col+printf("s%d", arg); + case MODE_P: + return col+printf("a%d", arg); + case MODE_V: + return col+printf("v%d", arg); + + case MODE_KI: + if (func != NULL) + { + return col+printf("%d", func->KonstD[arg]); + } + return printf("kd%d", arg); + case MODE_KF: + if (func != NULL) + { + return col+printf("%f", func->KonstF[arg]); + } + return col+printf("kf%d", arg); + case MODE_KS: + if (func != NULL) + { + return col+printf("\"%s\"", func->KonstS[arg].GetChars()); + } + return col+printf("ks%d", arg); + case MODE_KP: + if (func != NULL) + { + return col+printf("%p", func->KonstA[arg]); + } + return col+printf("ka%d", arg); + case MODE_KV: + if (func != NULL) + { + return col+printf("(%f,%f,%f)", func->KonstF[arg], func->KonstF[arg+1], func->KonstF[arg+2]); + } + return col+printf("kv%d", arg); + + case MODE_IMMS: + return col+printf("%d", (arg << immshift) >> immshift); + + case MODE_IMMZ: + return col+printf("%d", arg); + + case MODE_PARAM: + { + union { VM_UHALF Together; struct { VM_UBYTE RegType, RegNum; }; } p; + p.Together = arg; + switch (p.RegType & (REGT_TYPE | REGT_KONST | REGT_MULTIREG)) + { + case REGT_INT: + return col+printf("d%d", p.RegNum); + case REGT_FLOAT: + return col+printf("f%d", p.RegNum); + case REGT_STRING: + return col+printf("s%d", p.RegNum); + case REGT_POINTER: + return col+printf("a%d", p.RegNum); + case REGT_FLOAT | REGT_MULTIREG: + return col+printf("v%d", p.RegNum); + case REGT_INT | REGT_KONST: + return col+print_reg(0, p.RegNum, MODE_KI, 0, func); + case REGT_FLOAT | REGT_KONST: + return col+print_reg(0, p.RegNum, MODE_KF, 0, func); + case REGT_STRING | REGT_KONST: + return col+print_reg(0, p.RegNum, MODE_KS, 0, func); + case REGT_POINTER | REGT_KONST: + return col+print_reg(0, p.RegNum, MODE_KP, 0, func); + case REGT_FLOAT | REGT_MULTIREG | REGT_KONST: + return col+print_reg(0, p.RegNum, MODE_KV, 0, func); + default: + return col+printf("param[t=%d,%c,%c,n=%d]", + p.RegType & REGT_TYPE, + p.RegType & REGT_KONST ? 'k' : 'r', + p.RegType & REGT_MULTIREG ? 'm' : 's', + p.RegNum); + } + } + + default: + return col+printf("$%d", arg); + } + return col; +} diff --git a/zscript/vmexec.cpp b/zscript/vmexec.cpp new file mode 100644 index 0000000000..9c32b4fc55 --- /dev/null +++ b/zscript/vmexec.cpp @@ -0,0 +1,185 @@ +#include +#include "vm.h" + +#define IMPLEMENT_VMEXEC + +#if !defined(COMPGOTO) && defined(__GNUC__) +#define COMPGOTO 1 +#endif + +#if COMPGOTO +#define OP(x) x +#define NEXTOP do { unsigned op = *pc; a = pc[1]; pc += 4; goto *ops[op]; } while(0) +#else +#define OP(x) case OP_##x +#define NEXTOP break +#endif + +#define luai_nummod(a,b) ((a) - floor((a)/(b))*(b)) + +#define A (*(pc - 3)) +#define B (*(pc - 2)) +#define C ( *(pc - 1)) +#define Cs (*(VM_SBYTE *)(pc - 1)) +#define BC (*(VM_UHALF *)(pc - 2)) +#define BCs (*(VM_SHALF *)(pc - 2)) + +#ifdef WORDS_BIGENDIAN +#define JMPOFS(x) ((*(VM_SWORD *)(x) << 8) >> 6) +#else +#define JMPOFS(x) ((*(VM_SWORD *)(x) >> 6) & ~3) +#endif + +#define KC (konst[C]) +#define RC (reg.i[C]) + +#define PA (reg.a[A]) +#define PB (reg.a[B]) + +#define ASSERTD(x) assert((unsigned)(x) < f->NumRegD) +#define ASSERTF(x) assert((unsigned)(x) < f->NumRegF) +#define ASSERTA(x) assert((unsigned)(x) < f->NumRegA) +#define ASSERTS(x) assert((unsigned)(x) < f->NumRegS) + +#define ASSERTKD(x) assert(sfunc != NULL && (unsigned)(x) < sfunc->NumKonstD) +#define ASSERTKF(x) assert(sfunc != NULL && (unsigned)(x) < sfunc->NumKonstF) +#define ASSERTKA(x) assert(sfunc != NULL && (unsigned)(x) < sfunc->NumKonstA) +#define ASSERTKS(x) assert(sfunc != NULL && (unsigned)(x) < sfunc->NumKonstS) + +#define THROW(x) + +#define CMPJMP(test) \ + if ((test) == (a & CMP_CHECK)) { \ + assert(*pc == OP_JMP); \ + pc += 4 + JMPOFS(pc); \ + } else { \ + pc += 4; \ + } + +enum +{ + X_READ_NIL, + X_WRITE_NIL, + X_TOO_MANY_TRIES, +}; + +#define GETADDR(a,o,x) \ + if (a == 0) { THROW(x); } \ + ptr = (VM_SBYTE *)a + x \ + +static const VM_UWORD ZapTable[16] = +{ + 0x00000000, 0x000000FF, 0x0000FF00, 0x0000FFFF, + 0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00FFFFFF, + 0xFF000000, 0xFF0000FF, 0xFF00FF00, 0xFF00FFFF, + 0xFFFF0000, 0xFFFF00FF, 0xFFFFFF00, 0xFFFFFFFF +}; + +#ifdef NDEBUG +#define WAS_NDEBUG 1 +#else +#define WAS_NDEBUG 0 +#endif + +#if WAS_NDEBUG +#undef NDEBUG +#endif +#undef assert +#include +struct VMExec_Checked +{ +#include "vmexec.h" +}; +#if WAS_NDEBUG +#define NDEBUG +#endif + +#if !WAS_NDEBUG +#define NDEBUG +#endif +#undef assert +#include +struct VMExec_Unchecked +{ +#include "vmexec.h" +}; +#if !WAS_NDEBUG +#undef NDEBUG +#endif +#undef assert +#include + +int (*VMExec)(VMFrameStack *stack, const VM_UBYTE *pc, VMReturn *ret, int numret) = +#ifdef NDEBUG +VMExec_Unchecked::Exec +#else +VMExec_Checked::Exec +#endif +; + +void VMSelectEngine(EVMEngine engine) +{ + switch (engine) + { + case VMEngine_Default: +#ifdef NDEBUG + VMExec = VMExec_Unchecked::Exec; +#else + VMExec = VMExec_Checked::Exec; +#endif + break; + case VMEngine_Unchecked: + VMExec = VMExec_Unchecked::Exec; + break; + case VMEngine_Checked: + VMExec = VMExec_Checked::Exec; + break; + } +} + +//=========================================================================== +// +// VMFillParams +// +// Takes parameters from the paramater stack and stores them in the callee's +// registers. +// +//=========================================================================== + +void VMFillParams(VMValue *params, VMFrame *callee, int numparam) +{ + unsigned int regd, regf, regs, rega; + VMScriptFunction *calleefunc = static_cast(callee->Func); + const VMRegisters calleereg(callee); + + assert(calleefunc != NULL && !calleefunc->Native); + assert(numparam == calleefunc->NumArgs); + assert(REGT_INT == 0 && REGT_FLOAT == 1 && REGT_STRING == 2 && REGT_POINTER == 3); + + regd = regf = regs = rega = 0; + for (int i = 0; i < numparam; ++i) + { + VMValue &p = params[i]; + if (p.Type < REGT_STRING) + { + if (p.Type == REGT_INT) + { + calleereg.d[regd++] = p.i; + } + else // p.Type == REGT_FLOAT + { + calleereg.f[regf++] = p.f; + } + } + else if (p.Type == REGT_STRING) + { + calleereg.s[regs++] = p.s(); + } + else + { + assert(p.Type == REGT_POINTER); + calleereg.a[rega] = p.a; + calleereg.atag[rega++] = p.atag; + } + } +} diff --git a/zscript/vmexec.h b/zscript/vmexec.h new file mode 100644 index 0000000000..c0b2685897 --- /dev/null +++ b/zscript/vmexec.h @@ -0,0 +1,1495 @@ +#ifndef IMPLEMENT_VMEXEC +#error vmexec.h must not be #included outside vmexec.cpp. Use vm.h instead. +#endif + + +static int Exec(VMFrameStack *stack, const VM_UBYTE *pc, VMReturn *ret, int numret) +{ +#if COMPGOTO + static const void * const ops[256] = + { +#define xx(op,sym,mode) &&op +#include "vmops.h" + }; +#endif + const VM_UBYTE *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; + void * const *konsta; + + if (f->Func != NULL && !f->Func->Native) + { + sfunc = static_cast(f->Func); + konstd = sfunc->KonstD; + konstf = sfunc->KonstF; + konsts = sfunc->KonstS; + konsta = sfunc->KonstA; + } + else + { + sfunc = NULL; + konstd = NULL; + konstf = NULL; + konsts = NULL; + konsta = 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[0], a = pc[1], pc += 4, op) +#else + 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]; + reg.atag[a] = ATAG_OBJECT; + NEXTOP; + OP(LFP): + ASSERTA(a); assert(sfunc != NULL); assert(sfunc->ExtraSpace > 0); + reg.a[a] = f->GetExtra(); + reg.atag[a] = ATAG_FRAMEPOINTER; + 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(LO): + ASSERTA(a); ASSERTA(B); ASSERTKD(C); + GETADDR(PB,KC,X_READ_NIL); + reg.a[a] = *(void **)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] = *(void **)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(LV): + ASSERTF(a+2); ASSERTA(B); ASSERTKD(C); + GETADDR(PB,KC,X_READ_NIL); + { + float *v = (float *)ptr; + reg.f[a] = v[0]; + reg.f[a+1] = v[1]; + reg.f[a+2] = v[2]; + } + NEXTOP; + OP(LV_R): + ASSERTF(a+2); ASSERTA(B); ASSERTD(C); + GETADDR(PB,RC,X_READ_NIL); + { + float *v = (float *)ptr; + reg.f[a] = v[0]; + reg.f[a+1] = v[1]; + reg.f[a+2] = v[2]; + } + NEXTOP; + OP(LX): + ASSERTF(a); ASSERTA(B); ASSERTKD(C); + GETADDR(PB,KC,X_READ_NIL); + reg.f[a] = *(VM_SWORD *)ptr / 65536.0; + NEXTOP; + OP(LX_R): + ASSERTF(a); ASSERTA(B); ASSERTD(C); + GETADDR(PB,RC,X_READ_NIL); + reg.f[a] = *(VM_SWORD *)ptr / 65536.0; + 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(SV): + ASSERTA(a); ASSERTF(B+2); ASSERTKD(C); + GETADDR(PA,KC,X_WRITE_NIL); + { + float *v = (float *)ptr; + v[0] = (float)reg.f[B]; + v[1] = (float)reg.f[B+1]; + v[2] = (float)reg.f[B+2]; + } + NEXTOP; + OP(SV_R): + ASSERTA(a); ASSERTF(B+2); ASSERTD(C); + GETADDR(PA,RC,X_WRITE_NIL); + { + float *v = (float *)ptr; + v[0] = (float)reg.f[B]; + v[1] = (float)reg.f[B+1]; + v[2] = (float)reg.f[B+2]; + } + NEXTOP; + OP(SX): + ASSERTA(a); ASSERTF(B); ASSERTKD(C); + GETADDR(PA,KC,X_WRITE_NIL); + *(VM_SWORD *)ptr = (VM_SWORD)(reg.f[B] * 65536.0); + NEXTOP; + OP(SX_R): + ASSERTA(a); ASSERTF(B); ASSERTD(C); + GETADDR(PA,RC,X_WRITE_NIL); + *(VM_SWORD *)ptr = (VM_SWORD)(reg.f[B] * 65536.0); + 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); + reg.a[a] = reg.a[B]; + reg.atag[a] = reg.atag[B]; + 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(DYNCAST_R): + // UNDONE + NEXTOP; + OP(DYNCAST_K): + // UNDONE + NEXTOP; + + OP(TEST): + ASSERTD(a); + if (reg.d[a] != BC) + { + pc += 4; + } + NEXTOP; + OP(JMP): + pc += JMPOFS(pc - 4); + NEXTOP; + OP(IJMP): + ASSERTD(a); + pc += (BCs + reg.d[a]) << 2; + assert(*pc == OP_JMP); + pc += (1 + *((VM_SHALF *)pc + 1)) << 2; + NEXTOP; + OP(PARAM): + assert(f->NumParam < sfunc->MaxParam); + { + VMValue *param = ®.param[f->NumParam++]; + b = B; + if (b == REGT_NIL) + { + ::new(param) VMValue(); + } + else + { + switch(b & (REGT_TYPE | REGT_KONST | REGT_ADDROF)) + { + 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(®.d[C], ATAG_DREGISTER); + 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(®.s[C], ATAG_SREGISTER); + 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(®.a[C], ATAG_AREGISTER); + break; + case REGT_POINTER | REGT_KONST: + assert(C < sfunc->NumKonstA); + ::new(param) VMValue(konsta[C]); + break; + case REGT_FLOAT: + if (b & REGT_MULTIREG) + { + 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; + } + else + { + assert(C < f->NumRegF); + ::new(param) VMValue(reg.f[C]); + } + break; + case REGT_FLOAT | REGT_ADDROF: + assert(C < f->NumRegF); + ::new(param) VMValue(®.f[C], ATAG_FREGISTER); + break; + case REGT_FLOAT | REGT_KONST: + if (b & REGT_MULTIREG) + { + assert(C < sfunc->NumKonstF - 2); + assert(f->NumParam < sfunc->MaxParam - 1); + ::new(param) VMValue(konstf[C]); + ::new(param+1) VMValue(konstf[C+1]); + ::new(param+2) VMValue(konstf[C+2]); + f->NumParam += 2; + } + else + { + assert(C < sfunc->NumKonstF); + ::new(param) VMValue(konstf[C]); + } + break; + default: + assert(0); + break; + } + } + } + NEXTOP; + OP(CALL_K): + ASSERTKA(a); + ptr = sfunc->KonstA[a]; + 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, C); + if (call->Native) + { + numret = static_cast(call)->NativeCall(stack, reg.param + f->NumParam - B, B, returns, C); + } + else + { + VMScriptFunction *script = static_cast(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); + for (b = B; b != 0; --b) + { + reg.param[--f->NumParam].~VMValue(); + } + pc += C * 4; // Skip RESULTs + } + NEXTOP; + OP(RET): + if (B == REGT_NIL) + { // No return values + return 0; + } + assert(a < numret); + SetReturn(reg, f, &ret[a], B, C); + if (B & REGT_FINAL) + { + return a + 1; + } + 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(TRY): + assert(try_depth < MAX_TRY_DEPTH); + if (try_depth >= MAX_TRY_DEPTH) + { + THROW(X_TOO_MANY_TRIES); + } + assert(*(pc + JMPOFS(pc - 4)) == OP_CATCH); + exception_frames[try_depth++] = pc + JMPOFS(pc - 4); + NEXTOP; + OP(UNTRY): + assert(a <= try_depth); + try_depth -= a; + NEXTOP; + OP(THROW): + if (a == 0) + { + ASSERTA(B); + throw((VMException *)reg.a[B]); + } + else + { + ASSERTKA(B); + throw((VMException *)konsta[B]); + } + 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(CONCAT): + ASSERTS(a); ASSERTS(B); ASSERTS(C); + { + FString *rB = ®.s[B]; + FString *rC = ®.s[C]; + FString concat(*rB); + for (++rB; rB <= rC; ++rB) + { + concat += *rB; + } + reg.s[a] = concat; + } + 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 = ®.s[B]; + } + if (a & CMP_CK) + { + ASSERTKS(C); + c = &konsts[C]; + } + else + { + ASSERTS(C); + c = ®.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 == OP_JMP); + pc += 4 + JMPOFS(pc); + } + else + { + pc += 4; + } + } + 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(0 <= C && 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(0 <= C && 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(0 <= C && 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); + reg.d[a] = reg.d[B] / reg.d[C]; + NEXTOP; + OP(DIV_RK): + ASSERTD(a); ASSERTD(B); ASSERTKD(C); + reg.d[a] = reg.d[B] / konstd[C]; + NEXTOP; + OP(DIV_KR): + ASSERTD(a); ASSERTKD(B); ASSERTD(C); + reg.d[a] = konstd[B] / reg.d[C]; + NEXTOP; + + OP(MOD_RR): + ASSERTD(a); ASSERTD(B); ASSERTD(C); + reg.d[a] = reg.d[B] % reg.d[C]; + NEXTOP; + OP(MOD_RK): + ASSERTD(a); ASSERTD(B); ASSERTKD(C); + reg.d[a] = reg.d[B] % konstd[C]; + NEXTOP; + OP(MOD_KR): + ASSERTD(a); ASSERTKD(B); ASSERTD(C); + reg.d[a] = konstd[B] % 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); ASSERTD(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); + reg.f[a] = reg.f[B] / reg.f[C]; + NEXTOP; + OP(DIVF_RK): + ASSERTF(a); ASSERTF(B); ASSERTKF(C); + reg.f[a] = reg.f[B] / konstf[C]; + NEXTOP; + OP(DIVF_KR): + ASSERTF(a); ASSERTKF(B); ASSERTF(C); + 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: + 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] = pow(reg.f[B], reg.f[C]); + NEXTOP; + OP(POWF_RK): + ASSERTF(a); ASSERTF(B); ASSERTKF(C); + reg.f[a] = pow(reg.f[B], konstf[C]); + NEXTOP; + OP(POWF_KR): + ASSERTF(a); ASSERTKF(B); ASSERTF(C); + reg.f[a] = 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(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(NEGV): + 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(ADDV_RR): + ASSERTF(a+2); ASSERTF(B+2); ASSERTF(C+2); + fcp = ®.f[C]; + Do_ADDV: + fbp = ®.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(ADDV_RK): + fcp = &konstf[C]; + goto Do_ADDV; + + OP(SUBV_RR): + ASSERTF(a+2); ASSERTF(B+2); ASSERTF(C+2); + fbp = ®.f[B]; + fcp = ®.f[C]; + Do_SUBV: + 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(SUBV_RK): + ASSERTF(a+2); ASSERTF(B+2); ASSERTKF(C+2); + fbp = ®.f[B]; + fcp = &konstf[C]; + goto Do_SUBV; + OP(SUBV_KR): + ASSERTF(A+2); ASSERTKF(B+2); ASSERTF(C+2); + fbp = &konstf[B]; + fcp = ®.f[C]; + goto Do_SUBV; + + OP(DOTV_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(DOTV_RK): + ASSERTF(a); ASSERTF(B+2); ASSERTKF(C+2); + reg.f[a] = reg.f[B] * konstf[C] + reg.f[B+1] * konstf[C+1] + reg.f[B+2] * konstf[C+2]; + NEXTOP; + + OP(CROSSV_RR): + ASSERTF(a+2); ASSERTF(B+2); ASSERTF(C+2); + fbp = ®.f[B]; + fcp = ®.f[C]; + Do_CROSSV: + { + 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(CROSSV_RK): + ASSERTF(a+2); ASSERTF(B+2); ASSERTKF(C+2); + fbp = ®.f[B]; + fcp = &konstf[C]; + goto Do_CROSSV; + OP(CROSSV_KR): + ASSERTF(a+2); ASSERTKF(B+2); ASSERTF(C+2); + fbp = ®.f[B]; + fcp = &konstf[C]; + goto Do_CROSSV; + + OP(MULVF_RR): + ASSERTF(a+2); ASSERTF(B+2); ASSERTF(C); + fc = reg.f[C]; + fbp = ®.f[B]; + Do_MULV: + reg.f[a] = fbp[0] * fc; + reg.f[a+1] = fbp[1] * fc; + reg.f[a+2] = fbp[2] * fc; + NEXTOP; + OP(MULVF_RK): + ASSERTF(a+2); ASSERTF(B+2); ASSERTKF(C); + fc = konstf[C]; + fbp = ®.f[B]; + goto Do_MULV; + OP(MULVF_KR): + ASSERTF(a+2); ASSERTKF(B+2); ASSERTF(C); + fc = reg.f[C]; + fbp = &konstf[B]; + goto Do_MULV; + + OP(LENV): + ASSERTF(a); ASSERTF(B+2); + reg.f[a] = 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(EQV_R): + ASSERTF(B+2); ASSERTF(C+2); + fcp = ®.f[C]; + Do_EQV: + 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(EQV_K): + ASSERTF(B+2); ASSERTKF(C+2); + fcp = &konstf[C]; + goto Do_EQV; + + 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] : 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]); + 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[0] == OP_CATCH); + while (pc[1] > 1) + { + // CATCH must be followed by JMP if it doesn't terminate a catch chain. + assert(pc[4] == OP_JMP); + + PClass *type; + int b = pc[2]; + + if (pc[1] == 2) + { + ASSERTA(b); + type = (PClass *)reg.a[b]; + } + else + { + assert(pc[1] == 3); + ASSERTKA(b); + type = (PClass *)konsta[b]; + } + ASSERTA(pc[3]); + if (type == extype) + { + // Found a handler. Store the exception in pC, skip the JMP, + // and begin executing its code. + reg.a[pc[3]] = exception; + reg.atag[pc[3]] = ATAG_OBJECT; + pc += 8; + goto begin; + } + // This catch didn't handle it. Try the next one. + pc += 4 + JMPOFS(pc + 4); + assert(pc[0] == OP_CATCH); + } + if (pc[1] == 1) + { + // Catch any type of VMException. This terminates the chain. + ASSERTA(pc[3]); + reg.a[pc[3]] = exception; + reg.atag[pc[3]] = ATAG_OBJECT; + pc += 4; + goto begin; + } + // This frame failed. Try the next one out. + } + // Nothing caught it. Rethrow and let somebody else deal with it. + 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_ACOS: return acos(v); + case FLOP_ASIN: return asin(v); + case FLOP_ATAN: return atan(v); + case FLOP_COS: return cos(v); + case FLOP_COSH: return cosh(v); + case FLOP_EXP: return exp(v); + case FLOP_LOG: return log(v); + case FLOP_LOG10: return log10(v); + case FLOP_SIN: return sin(v); + case FLOP_SINH: return sinh(v); + case FLOP_TAN: return tan(v); + case FLOP_TANH: return tanh(v); + case FLOP_SQRT: return sqrt(v); + case FLOP_CEIL: return ceil(v); + case FLOP_FLOOR: return floor(v); + } + assert(0); + return 0; +} + +static void DoCast(const VMRegisters ®, 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_I2S: + ASSERTS(a); ASSERTD(b); + reg.s[a].Format("%d", reg.d[b]); + break; + + case CAST_F2I: + ASSERTD(a); ASSERTF(b); + reg.d[a] = (int)reg.f[b]; + break; + case CAST_F2S: + ASSERTS(a); ASSERTD(b); + reg.s[a].Format("%.14g", reg.f[b]); + break; + + case CAST_P2S: + ASSERTS(a); ASSERTA(b); + reg.s[a].Format("%s<%p>", reg.atag[b] == ATAG_OBJECT ? "Object" : "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; + + default: + assert(0); + } +} + +//=========================================================================== +// +// FillReturns +// +// Fills in an array of pointers to locations to store return values in. +// +//=========================================================================== + +static void FillReturns(const VMRegisters ®, VMFrame *frame, VMReturn *returns, const VM_UBYTE *retval, int numret) +{ + int i, type, num; + 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 += 4) + { + assert(retval[0] == OP_RESULT); // opcode + ret->RegType = type = retval[2]; + ret->RegNum = num = retval[3]; + assert(!(type & REGT_KONST)); + type &= REGT_TYPE; + if (type < REGT_STRING) + { + if (type == REGT_INT) + { + assert(num < frame->NumRegD); + ret->Location = ®.d[num]; + } + else // type == REGT_FLOAT + { + assert(num < frame->NumRegF); + ret->Location = ®.f[num]; + } + } + else if (type == REGT_STRING) + { + assert(num < frame->NumRegS); + ret->Location = ®.s[num]; + } + else + { + assert(type == REGT_POINTER); + assert(num < frame->NumRegA); + ret->Location = ®.a[num]; + } + } +} + +//=========================================================================== +// +// SetReturn +// +// Used by script code to set a return value. +// +//=========================================================================== + +static void SetReturn(const VMRegisters ®, VMFrame *frame, VMReturn *ret, VM_UBYTE regtype, int regnum) +{ + const void *src; + VM_UBYTE atag; + VMScriptFunction *func = static_cast(frame->Func); + + assert(func != NULL && !func->Native); + assert((regtype & ~(REGT_KONST | REGT_FINAL)) == 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 = ®.d[regnum]; + } + ret->SetInt(*(int *)src); + break; + + case REGT_FLOAT: + if (regtype & REGT_KONST) + { + assert(regnum + ((regtype & REGT_KONST) ? 2u : 0u) < func->NumKonstF); + src = &func->KonstF[regnum]; + } + else + { + assert(regnum + ((regtype & REGT_KONST) ? 2u : 0u) < frame->NumRegF); + src = ®.f[regnum]; + } + if (regtype & REGT_MULTIREG) + { + ret->SetVector((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 = ®.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]); + atag = ATAG_OBJECT; + } + else + { + assert(regnum < frame->NumRegA); + ret->SetPointer(reg.a[regnum]); + atag = reg.atag[regnum]; + } + if (ret->RegNum >= 0) + { + VMFrame *parent = frame->ParentFrame; + assert(parent != NULL); + parent->GetRegATag()[ret->RegNum] = atag; + } + break; + } +} diff --git a/zscript/vmframe.cpp b/zscript/vmframe.cpp new file mode 100644 index 0000000000..208bc47258 --- /dev/null +++ b/zscript/vmframe.cpp @@ -0,0 +1,294 @@ +#include +#include "vm.h" + + +//=========================================================================== +// +// VMFrame :: InitRegS +// +// Initialize the string registers of a newly-allocated VMFrame. +// +//=========================================================================== + +void VMFrame::InitRegS() +{ + FString *regs = GetRegS(); + for (int i = 0; i < NumRegS; ++i) + { + ::new(®s[i]) FString; + } +} + +//=========================================================================== +// +// VMFrameStack - Constructor +// +//=========================================================================== + +VMFrameStack::VMFrameStack() +{ + Blocks = NULL; + UnusedBlocks = NULL; +} + +//=========================================================================== +// +// VMFrameStack - Destructor +// +//=========================================================================== + +VMFrameStack::~VMFrameStack() +{ + while (PopFrame() != NULL) + { } + if (Blocks != NULL) + { + BlockHeader *block, *next; + for (block = Blocks; block != NULL; block = next) + { + next = block->NextBlock; + delete[] (VM_UBYTE *)block; + } + } + if (UnusedBlocks != NULL) + { + BlockHeader *block, *next; + for (block = UnusedBlocks; block != NULL; block = next) + { + next = block->NextBlock; + delete[] (VM_UBYTE *)block; + } + } + Blocks = NULL; + UnusedBlocks = NULL; +} + +//=========================================================================== +// +// VMFrameStack :: AllocFrame +// +// Allocates a frame from the stack with the desired number of registers. +// +//=========================================================================== + +VMFrame *VMFrameStack::AllocFrame(int numregd, int numregf, int numregs, int numrega) +{ + assert((unsigned)numregd < 255); + assert((unsigned)numregf < 255); + assert((unsigned)numregs < 255); + assert((unsigned)numrega < 255); + // To keep the arguments to this function simpler, it assumes that every + // register might be used as a parameter for a single call. + int numparam = numregd + numregf + numregs + numrega; + int size = VMFrame::FrameSize(numregd, numregf, numregs, numrega, numparam, 0); + VMFrame *frame = Alloc(size); + frame->NumRegD = numregd; + frame->NumRegF = numregf; + frame->NumRegS = numregs; + frame->NumRegA = numrega; + frame->MaxParam = numparam; + frame->InitRegS(); + return frame; +} + +//=========================================================================== +// +// VMFrameStack :: AllocFrame +// +// Allocates a frame from the stack suitable for calling a particular +// function. +// +//=========================================================================== + +VMFrame *VMFrameStack::AllocFrame(VMScriptFunction *func) +{ + int size = VMFrame::FrameSize(func->NumRegD, func->NumRegF, func->NumRegS, func->NumRegA, + func->MaxParam, func->ExtraSpace); + VMFrame *frame = Alloc(size); + frame->Func = func; + frame->NumRegD = func->NumRegD; + frame->NumRegF = func->NumRegF; + frame->NumRegS = func->NumRegS; + frame->NumRegA = func->NumRegA; + frame->MaxParam = func->MaxParam; + frame->Func = func; + frame->InitRegS(); + return frame; +} + +//=========================================================================== +// +// VMFrameStack :: Alloc +// +// Allocates space for a frame. Its size will be rounded up to a multiple +// of 16 bytes. +// +//=========================================================================== + +VMFrame *VMFrameStack::Alloc(int size) +{ + BlockHeader *block; + VMFrame *frame, *parent; + + size = (size + 15) & ~15; + block = Blocks; + if (block != NULL) + { + parent = block->LastFrame; + } + else + { + parent = NULL; + } + if (block == NULL || ((VM_UBYTE *)block + block->BlockSize) < (block->FreeSpace + size)) + { // Not enough space. Allocate a new block. + int blocksize = ((sizeof(BlockHeader) + 15) & ~15) + size; + BlockHeader **blockp; + if (blocksize < BLOCK_SIZE) + { + blocksize = BLOCK_SIZE; + } + for (blockp = &UnusedBlocks, block = *blockp; block != NULL; block = block->NextBlock) + { + if (block->BlockSize >= blocksize) + { + break; + } + } + if (block != NULL) + { + *blockp = block->NextBlock; + } + else + { + block = (BlockHeader *)new VM_UBYTE[blocksize]; + block->BlockSize = blocksize; + } + block->FreeSpace = (VM_UBYTE *)block + ((sizeof(BlockHeader) + 15) & ~15); + block->LastFrame = NULL; + block->NextBlock = Blocks; + Blocks = block; + } + frame = (VMFrame *)block->FreeSpace; + memset(frame, 0, size); + frame->ParentFrame = parent; + block->FreeSpace += size; + block->LastFrame = frame; + return frame; +} + + +//=========================================================================== +// +// VMFrameStack :: PopFrame +// +// Pops the top frame off the stack, returning a pointer to the new top +// frame. +// +//=========================================================================== + +VMFrame *VMFrameStack::PopFrame() +{ + if (Blocks == NULL) + { + return NULL; + } + VMFrame *frame = Blocks->LastFrame; + if (frame == NULL) + { + return NULL; + } + // Free any string registers this frame had. + FString *regs = frame->GetRegS(); + for (int i = frame->NumRegS; i != 0; --i) + { + (regs++)->~FString(); + } + // Free any parameters this frame left behind. + VMValue *param = frame->GetParam(); + for (int i = frame->NumParam; i != 0; --i) + { + (param++)->~VMValue(); + } + VMFrame *parent = frame->ParentFrame; + if (parent == NULL) + { + // Popping the last frame off the stack. + if (Blocks != NULL) + { + assert(Blocks->NextBlock == NULL); + Blocks->LastFrame = NULL; + Blocks->FreeSpace = (VM_UBYTE *)Blocks + ((sizeof(BlockHeader) + 15) & ~15); + } + return NULL; + } + if ((VM_UBYTE *)parent < (VM_UBYTE *)Blocks || (VM_UBYTE *)parent >= (VM_UBYTE *)Blocks + Blocks->BlockSize) + { // Parent frame is in a different block, so move this one to the unused list. + BlockHeader *next = Blocks->NextBlock; + assert(next != NULL); + assert((VM_UBYTE *)parent >= (VM_UBYTE *)next && (VM_UBYTE *)parent < (VM_UBYTE *)next + next->BlockSize); + Blocks->NextBlock = UnusedBlocks; + UnusedBlocks = Blocks; + Blocks = next; + } + else + { + Blocks->LastFrame = parent; + Blocks->FreeSpace = (VM_UBYTE *)frame; + } + return parent; +} + +//=========================================================================== +// +// VMFrameStack :: Call +// +// Calls a function, either native or scripted. If an exception occurs while +// executing, the stack is cleaned up. If trap is non-NULL, it is set to the +// VMException that was caught and the return value is negative. Otherwise, +// any caught exceptions will be rethrown. Under normal termination, the +// return value is the number of results from the function. +// +//=========================================================================== + +int VMFrameStack::Call(VMFunction *func, VMValue *params, int numparams, VMReturn *results, int numresults, VMException **trap) +{ + bool allocated = false; + try + { + if (func->Native) + { + return static_cast(func)->NativeCall(this, params, numparams, results, numresults); + } + else + { + AllocFrame(static_cast(func)); + allocated = true; + VMFillParams(params, TopFrame(), numparams); + int numret = VMExec(this, static_cast(func)->Code, results, numresults); + PopFrame(); + return numret; + } + } + catch (VMException *exception) + { + if (allocated) + { + PopFrame(); + } + if (trap != NULL) + { + *trap = exception; + return -1; + } + throw; + } + catch (...) + { + if (allocated) + { + PopFrame(); + } + throw; + } +} diff --git a/zscript/vmops.h b/zscript/vmops.h new file mode 100644 index 0000000000..f7d821bb92 --- /dev/null +++ b/zscript/vmops.h @@ -0,0 +1,210 @@ +#ifndef xx +#define xx(op, name, mode) OP_##op +#endif + +// Load constants. +xx(LI, li, LI), // load immediate signed 16-bit constant +xx(LK, lk, LKI), // load integer constant +xx(LKF, lk, LKF), // load float constant +xx(LKS, lk, LKS), // load string constant +xx(LKP, lk, LKP), // load pointer constant +xx(LFP, lf, LFP), // load frame pointer + +// Load from memory. rA = *(rB + rkC) +xx(LB, lb, RIRPKI), // load byte +xx(LB_R, lb, RIRPRI), +xx(LH, lh, RIRPKI), // load halfword +xx(LH_R, lh, RIRPRI), +xx(LW, lw, RIRPKI), // load word +xx(LW_R, lw, RIRPRI), +xx(LBU, lbu, RIRPKI), // load byte unsigned +xx(LBU_R, lbu, RIRPRI), +xx(LHU, lhu, RIRPKI), // load halfword unsigned +xx(LHU_R, lhu, RIRPRI), +xx(LSP, lsp, RFRPKI), // load single-precision fp +xx(LSP_R, lsp, RFRPRI), +xx(LDP, ldp, RFRPKI), // load double-precision fp +xx(LDP_R, ldp, RFRPRI), +xx(LS, ls, RSRPKI), // load string +xx(LS_R, ls, RSRPRI), +xx(LO, lo, RPRPKI), // load object +xx(LO_R, lo, RPRPRI), +xx(LP, lp, RPRPKI), // load pointer +xx(LP_R, lp, RPRPRI), +xx(LV, lv, RVRPKI), // load vector +xx(LV_R, lv, RVRPRI), +xx(LX, lx, RFRPKI), // load fixed point +xx(LX_R, lx, RFRPRI), + +xx(LBIT, lbit, RIRPI8), // rA = !!(*rB & C) -- *rB is a byte + +// Store instructions. *(rA + rkC) = rB +xx(SB, sb, RPRIKI), // store byte +xx(SB_R, sb, RPRIRI), +xx(SH, sh, RPRIKI), // store halfword +xx(SH_R, sh, RPRIRI), +xx(SW, sw, RPRIKI), // store word +xx(SW_R, sw, RPRIRI), +xx(SSP, ssp, RPRFKI), // store single-precision fp +xx(SSP_R, ssp, RPRFRI), +xx(SDP, sdp, RPRFKI), // store double-precision fp +xx(SDP_R, sdp, RPRFRI), +xx(SS, ss, RPRSKI), // store string +xx(SS_R, ss, RPRSRI), +xx(SP, sp, RPRPKI), // store pointer +xx(SP_R, sp, RPRPRI), +xx(SV, sv, RPRVKI), // store vector +xx(SV_R, sv, RPRVRI), +xx(SX, sx, RPRFKI), // store fixed point +xx(SX_R, sx, RPRFRI), + +xx(SBIT, sbit, RPRII8), // *rA |= C if rB is true, *rA &= ~C otherwise + +// Move instructions. +xx(MOVE, mov, RIRI), // dA = dB +xx(MOVEF, mov, RFRF), // fA = fB +xx(MOVES, mov, RSRS), // sA = sB +xx(MOVEA, mov, RPRP), // aA = aB +xx(CAST, cast, CAST), // xA = xB, conversion specified by C +xx(DYNCAST_R, dyncast,RPRPRP), // aA = aB after casting to rkC (specifying a class) +xx(DYNCAST_K, dyncast,RPRPKP), + +// Control flow. +xx(TEST, test, RII16), // if (dA != BC) then pc++ +xx(JMP, jmp, I24), // pc += ABC -- The ABC fields contain a signed 24-bit offset. +xx(IJMP, ijmp, RII16), // pc += dA + BC -- BC is a signed offset. The target instruction must be a JMP. +xx(PARAM, param, __BCP), // push parameter encoded in BC for function call or result for return +xx(CALL, call, RPI8I8), // Call function pkA with parameter count B and expected result count C +xx(CALL_K, call, KPI8I8), +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 +xx(UNTRY, untry, I8), // Pop A entries off the exception stack +xx(THROW, throw, THROW), // A == 0: Throw exception object pB + // A != 0: Throw exception object pkB +xx(CATCH, catch, CATCH), // A == 0: continue search on next try + // A == 1: continue execution at instruction immediately following CATCH (catches any exception) + // A == 2: (pB == ) then pc++ ; next instruction must JMP to another CATCH + // A == 3: (pkB == ) then pc++ ; next instruction must JMP to another CATCH + // for A > 0, exception is stored in pC + +// String instructions. +xx(CONCAT, concat, RSRSRS), // sA = sB.. ... ..sC +xx(LENS, lens, RIRS), // dA = sB.Length +xx(CMPS, cmps, I8RXRX), // if ((skB op skC) != (A & 1)) then pc++ + +// Integer math. +xx(SLL_RR, sll, RIRIRI), // dA = dkB << diC +xx(SLL_RI, sll, RIRII8), +xx(SLL_KR, sll, RIKIRI), +xx(SRL_RR, srl, RIRIRI), // dA = dkB >> diC -- unsigned +xx(SRL_RI, srl, RIRII8), +xx(SRL_KR, srl, RIKIRI), +xx(SRA_RR, sra, RIRIRI), // dA = dkB >> diC -- signed +xx(SRA_RI, sra, RIRII8), +xx(SRA_KR, sra, RIKIRI), +xx(ADD_RR, add, RIRIRI), // dA = dB + dkC +xx(ADD_RK, add, RIRIKI), +xx(ADDI, add, RIRIIs), // dA = dB + C -- C is a signed 8-bit constant +xx(SUB_RR, sub, RIRIRI), // dA = dkB - dkC +xx(SUB_RK, sub, RIRIKI), +xx(SUB_KR, sub, RIKIRI), +xx(MUL_RR, mul, RIRIRI), // dA = dB * dkC +xx(MUL_RK, mul, RIRIKI), +xx(DIV_RR, div, RIRIRI), // dA = dkB / dkC +xx(DIV_RK, div, RIRIKI), +xx(DIV_KR, div, RIKIRI), +xx(MOD_RR, mod, RIRIRI), // dA = dkB % dkC +xx(MOD_RK, mod, RIRIKI), +xx(MOD_KR, mod, RIKIRI), +xx(AND_RR, and, RIRIRI), // dA = dB & dkC +xx(AND_RK, and, RIRIKI), +xx(OR_RR, or, RIRIRI), // dA = dB | dkC +xx(OR_RK, or, RIRIKI), +xx(XOR_RR, xor, RIRIRI), // dA = dB ^ dkC +xx(XOR_RK, xor, RIRIKI), +xx(MIN_RR, min, RIRIRI), // dA = min(dB,dkC) +xx(MIN_RK, min, RIRIKI), +xx(MAX_RR, max, RIRIRI), // dA = max(dB,dkC) +xx(MAX_RK, max, RIRIKI), +xx(ABS, abs, RIRI), // dA = abs(dB) +xx(NEG, neg, RIRI), // dA = -dB +xx(NOT, not, RIRI), // dA = !dB +xx(SEXT, sext, RIRII8), // dA = dB, sign extended by shifting left then right by C +xx(ZAP_R, zap, RIRIRI), // dA = dB, with bytes zeroed where bits in C/dC are one +xx(ZAP_I, zap, RIRII8), +xx(ZAPNOT_R, zapnot, RIRIRI), // dA = dB, with bytes zeroed where bits in C/dC are zero +xx(ZAPNOT_I, zapnot, RIRII8), +xx(EQ_R, eq, I8RIRI), // if ((dB == dkC) != A) then pc++ +xx(EQ_K, eq, I8RIKI), +xx(LT_RR, lt, I8RIRI), // if ((dkB < dkC) != A) then pc++ +xx(LT_RK, lt, I8RIKI), +xx(LT_KR, lt, I8KIRI), +xx(LE_RR, le, I8RIRI), // if ((dkB <= dkC) != A) then pc++ +xx(LE_RK, le, I8RIKI), +xx(LE_KR, le, I8KIRI), +xx(LTU_RR, ltu, I8RIRI), // if ((dkB < dkC) != A) then pc++ -- unsigned +xx(LTU_RK, ltu, I8RIKI), +xx(LTU_KR, ltu, I8KIRI), +xx(LEU_RR, leu, I8RIRI), // if ((dkB <= dkC) != A) then pc++ -- unsigned +xx(LEU_RK, leu, I8RIKI), +xx(LEU_KR, leu, I8KIRI), + +// Double-precision floating point math. +xx(ADDF_RR, add, RFRFRF), // fA = fB + fkC +xx(ADDF_RK, add, RFRFKF), +xx(SUBF_RR, sub, RFRFRF), // fA = fkB - fkC +xx(SUBF_RK, sub, RFRFKF), +xx(SUBF_KR, sub, RFKFRF), +xx(MULF_RR, mul, RFRFRF), // fA = fB * fkC +xx(MULF_RK, mul, RFRFKF), +xx(DIVF_RR, div, RFRFRF), // fA = fkB / fkC +xx(DIVF_RK, div, RFRFKF), +xx(DIVF_KR, div, RFKFRF), +xx(MODF_RR, mod, RFRFRF), // fA = fkB % fkC +xx(MODF_RK, mod, RFRFKF), +xx(MODF_KR, mod, RFKFRF), +xx(POWF_RR, pow, RFRFRF), // fA = fkB ** fkC +xx(POWF_RK, pow, RFRFKF), +xx(POWF_KR, pow, RFKFRF), +xx(MINF_RR, min, RFRFRF), // fA = min(fB),fkC) +xx(MINF_RK, min, RFRFKF), +xx(MAXF_RR, max, RFRFRF), // fA = max(fB),fkC) +xx(MAXF_RK, max, RFRFKF), +xx(FLOP, flop, RFRFI8), // fA = f(fB), where function is selected by C +xx(EQF_R, eq, I8RFRF), // if ((fB == fkC) != (A & 1)) then pc++ +xx(EQF_K, eq, I8RFKF), +xx(LTF_RR, lt, I8RFRF), // if ((fkB < fkC) != (A & 1)) then pc++ +xx(LTF_RK, lt, I8RFKF), +xx(LTF_KR, lt, I8KFRF), +xx(LEF_RR, le, I8RFRF), // if ((fkb <= fkC) != (A & 1)) then pc++ +xx(LEF_RK, le, I8RFKF), +xx(LEF_KR, le, I8KFRF), + +// Vector math. +xx(NEGV, negv, RVRV), // vA = -vB +xx(ADDV_RR, addv, RVRVRV), // vA = vB + vkC +xx(ADDV_RK, addv, RVRVKV), +xx(SUBV_RR, subv, RVRVRV), // vA = vkB - vkC +xx(SUBV_RK, subv, RVRVKV), +xx(SUBV_KR, subv, RVKVRV), +xx(DOTV_RR, dotv, RVRVRV), // va = vB dot vkC +xx(DOTV_RK, dotv, RVRVKV), +xx(CROSSV_RR, crossv, RVRVRV), // vA = vkB cross vkC +xx(CROSSV_RK, crossv, RVRVKV), +xx(CROSSV_KR, crossv, RVKVRV), +xx(MULVF_RR, mulv, RVRVRV), // vA = vkB * fkC +xx(MULVF_RK, mulv, RVRVKV), +xx(MULVF_KR, mulv, RVKVRV), +xx(LENV, lenv, RFRV), // fA = vB.Length +xx(EQV_R, eqv, I8RVRV), // if ((vB == vkC) != A) then pc++ (inexact if A & 32) +xx(EQV_K, eqv, I8RVKV), + +// Pointer math. +xx(ADDA_RR, add, RPRPRI), // pA = pB + dkC +xx(ADDA_RK, add, RPRPKI), +xx(SUBA, sub, RIRPRP), // dA = pB - pC +xx(EQA_R, eq, I8RPRP), // if ((pB == pkC) != A) then pc++ +xx(EQA_K, eq, I8RPKP), + +#undef xx