- Make PClass derive from DObject so that it can participate in garbage collection.

- Import VM.

SVN r1841 (scripting)
This commit is contained in:
Randy Heit 2009-09-16 01:39:44 +00:00
parent 45842e28bd
commit fefc306a54
12 changed files with 3399 additions and 19 deletions

View file

@ -149,10 +149,10 @@ static TArray<const PClass *> WeaponNames;
// List of states that are hacked to use a codepointer
struct MBFParamState
{
FState * state;
FState *state;
int pointer;
};
static TArray<MBFParamState *> MBFParamStates;
static TArray<MBFParamState> 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();

View file

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

View file

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

View file

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

View file

@ -382,7 +382,7 @@ protected:
DFrameBuffer () {}
private:
DWORD LastMS, LastSec, FrameCount, LastCount, LastTic;
uint32 LastMS, LastSec, FrameCount, LastCount, LastTic;
};

View file

@ -6446,6 +6446,34 @@
>
</File>
</Filter>
<Filter
Name="ZScript"
>
<File
RelativePath=".\zscript\vm.h"
>
</File>
<File
RelativePath=".\zscript\vmdisasm.cpp"
>
</File>
<File
RelativePath=".\zscript\vmexec.cpp"
>
</File>
<File
RelativePath=".\zscript\vmexec.h"
>
</File>
<File
RelativePath=".\zscript\vmframe.cpp"
>
</File>
<File
RelativePath=".\zscript\vmops.h"
>
</File>
</Filter>
</Files>
<Globals>
</Globals>

791
zscript/vm.h Normal file
View file

@ -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 *&param) 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

343
zscript/vmdisasm.cpp Normal file
View file

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

185
zscript/vmexec.cpp Normal file
View file

@ -0,0 +1,185 @@
#include <math.h>
#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 <assert.h>
struct VMExec_Checked
{
#include "vmexec.h"
};
#if WAS_NDEBUG
#define NDEBUG
#endif
#if !WAS_NDEBUG
#define NDEBUG
#endif
#undef assert
#include <assert.h>
struct VMExec_Unchecked
{
#include "vmexec.h"
};
#if !WAS_NDEBUG
#undef NDEBUG
#endif
#undef assert
#include <assert.h>
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<VMScriptFunction *>(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;
}
}
}

1495
zscript/vmexec.h Normal file

File diff suppressed because it is too large Load diff

294
zscript/vmframe.cpp Normal file
View file

@ -0,0 +1,294 @@
#include <new>
#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(&regs[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<VMNativeFunction *>(func)->NativeCall(this, params, numparams, results, numresults);
}
else
{
AllocFrame(static_cast<VMScriptFunction *>(func));
allocated = true;
VMFillParams(params, TopFrame(), numparams);
int numret = VMExec(this, static_cast<VMScriptFunction *>(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;
}
}

210
zscript/vmops.h Normal file
View file

@ -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 == <type of exception thrown>) then pc++ ; next instruction must JMP to another CATCH
// A == 3: (pkB == <type of exception thrown>) 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