- moved a few things around to have them into better fitting places.

This commit is contained in:
Christoph Oelckers 2017-02-08 12:24:08 +01:00
commit 5a81a4ca16
20 changed files with 23 additions and 28 deletions

View file

@ -1,953 +0,0 @@
/*
** vmbuilder.cpp
**
**---------------------------------------------------------------------------
** Copyright -2016 Randy Heit
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include "vmbuilder.h"
#include "codegeneration/codegen.h"
#include "info.h"
#include "m_argv.h"
#include "thingdef.h"
#include "doomerrors.h"
struct VMRemap
{
BYTE altOp, kReg, kType;
};
#define xx(op, name, mode, alt, kreg, ktype) {OP_##alt, kreg, ktype }
VMRemap opRemap[NUM_OPS] = {
#include "vmops.h"
};
#undef xx
//==========================================================================
//
// VMFunctionBuilder - Constructor
//
//==========================================================================
VMFunctionBuilder::VMFunctionBuilder(int numimplicits)
{
MaxParam = 0;
ActiveParam = 0;
NumImplicits = numimplicits;
}
//==========================================================================
//
// VMFunctionBuilder - Destructor
//
//==========================================================================
VMFunctionBuilder::~VMFunctionBuilder()
{
}
//==========================================================================
//
// VMFunctionBuilder :: BeginStatement
//
// Records the start of a new statement.
//
//==========================================================================
void VMFunctionBuilder::BeginStatement(FxExpression *stmt)
{
// pop empty statement records.
while (LineNumbers.Size() > 0 && LineNumbers.Last().InstructionIndex == Code.Size()) LineNumbers.Pop();
// only add a new entry if the line number differs.
if (LineNumbers.Size() == 0 || stmt->ScriptPosition.ScriptLine != LineNumbers.Last().LineNumber)
{
FStatementInfo si = { (uint16_t)Code.Size(), (uint16_t)stmt->ScriptPosition.ScriptLine };
LineNumbers.Push(si);
}
StatementStack.Push(stmt);
}
void VMFunctionBuilder::EndStatement()
{
// pop empty statement records.
while (LineNumbers.Size() > 0 && LineNumbers.Last().InstructionIndex == Code.Size()) LineNumbers.Pop();
StatementStack.Pop();
// Re-enter the previous statement.
if (StatementStack.Size() > 0)
{
FStatementInfo si = { (uint16_t)Code.Size(), (uint16_t)StatementStack.Last()->ScriptPosition.ScriptLine };
LineNumbers.Push(si);
}
}
void VMFunctionBuilder::MakeFunction(VMScriptFunction *func)
{
func->Alloc(Code.Size(), IntConstantList.Size(), FloatConstantList.Size(), StringConstantList.Size(), AddressConstantList.Size(), LineNumbers.Size());
// Copy code block.
memcpy(func->Code, &Code[0], Code.Size() * sizeof(VMOP));
memcpy(func->LineInfo, &LineNumbers[0], LineNumbers.Size() * sizeof(LineNumbers[0]));
// Create constant tables.
if (IntConstantList.Size() > 0)
{
FillIntConstants(func->KonstD);
}
if (FloatConstantList.Size() > 0)
{
FillFloatConstants(func->KonstF);
}
if (AddressConstantList.Size() > 0)
{
FillAddressConstants(func->KonstA, func->KonstATags());
}
if (StringConstantList.Size() > 0)
{
FillStringConstants(func->KonstS);
}
// Assign required register space.
func->NumRegD = Registers[REGT_INT].MostUsed;
func->NumRegF = Registers[REGT_FLOAT].MostUsed;
func->NumRegA = Registers[REGT_POINTER].MostUsed;
func->NumRegS = Registers[REGT_STRING].MostUsed;
func->MaxParam = MaxParam;
// Technically, there's no reason why we can't end the function with
// entries on the parameter stack, but it means the caller probably
// did something wrong.
assert(ActiveParam == 0);
}
//==========================================================================
//
// VMFunctionBuilder :: FillIntConstants
//
//==========================================================================
void VMFunctionBuilder::FillIntConstants(int *konst)
{
memcpy(konst, &IntConstantList[0], sizeof(int) * IntConstantList.Size());
}
//==========================================================================
//
// VMFunctionBuilder :: FillFloatConstants
//
//==========================================================================
void VMFunctionBuilder::FillFloatConstants(double *konst)
{
memcpy(konst, &FloatConstantList[0], sizeof(double) * FloatConstantList.Size());
}
//==========================================================================
//
// VMFunctionBuilder :: FillAddressConstants
//
//==========================================================================
void VMFunctionBuilder::FillAddressConstants(FVoidObj *konst, VM_ATAG *tags)
{
memcpy(konst, &AddressConstantList[0], sizeof(void*) * AddressConstantList.Size());
memcpy(tags, &AtagConstantList[0], sizeof(VM_ATAG) * AtagConstantList.Size());
}
//==========================================================================
//
// VMFunctionBuilder :: FillStringConstants
//
//==========================================================================
void VMFunctionBuilder::FillStringConstants(FString *konst)
{
for (auto &s : StringConstantList)
{
*konst++ = s;
}
}
//==========================================================================
//
// VMFunctionBuilder :: GetConstantInt
//
// Returns a constant register initialized with the given value.
//
//==========================================================================
unsigned VMFunctionBuilder::GetConstantInt(int val)
{
unsigned int *locp = IntConstantMap.CheckKey(val);
if (locp != NULL)
{
return *locp;
}
else
{
unsigned loc = IntConstantList.Push(val);
IntConstantMap.Insert(val, loc);
return loc;
}
}
//==========================================================================
//
// VMFunctionBuilder :: GetConstantFloat
//
// Returns a constant register initialized with the given value.
//
//==========================================================================
unsigned VMFunctionBuilder::GetConstantFloat(double val)
{
unsigned *locp = FloatConstantMap.CheckKey(val);
if (locp != NULL)
{
return *locp;
}
else
{
unsigned loc = FloatConstantList.Push(val);
FloatConstantMap.Insert(val, loc);
return loc;
}
}
//==========================================================================
//
// VMFunctionBuilder :: GetConstantString
//
// Returns a constant register initialized with the given value.
//
//==========================================================================
unsigned VMFunctionBuilder::GetConstantString(FString val)
{
unsigned *locp = StringConstantMap.CheckKey(val);
if (locp != NULL)
{
return *locp;
}
else
{
int loc = StringConstantList.Push(val);
StringConstantMap.Insert(val, loc);
return loc;
}
}
//==========================================================================
//
// VMFunctionBuilder :: GetConstantAddress
//
// Returns a constant register initialized with the given value, or -1 if
// there were no more constants free.
//
//==========================================================================
unsigned VMFunctionBuilder::GetConstantAddress(void *ptr, VM_ATAG tag)
{
if (ptr == NULL)
{ // Make all NULL pointers generic. (Or should we allow typed NULLs?)
tag = ATAG_GENERIC;
}
AddrKonst *locp = AddressConstantMap.CheckKey(ptr);
if (locp != NULL)
{
// There should only be one tag associated with a memory location. Exceptions are made for null pointers that got allocated through constant arrays.
assert(ptr == nullptr || locp->Tag == tag);
return locp->KonstNum;
}
else
{
unsigned locc = AddressConstantList.Push(ptr);
AtagConstantList.Push(tag);
AddrKonst loc = { locc, tag };
AddressConstantMap.Insert(ptr, loc);
return loc.KonstNum;
}
}
//==========================================================================
//
// VMFunctionBuilder :: AllocConstants*
//
// Returns a range of constant register initialized with the given values.
//
//==========================================================================
unsigned VMFunctionBuilder::AllocConstantsInt(unsigned count, int *values)
{
unsigned addr = IntConstantList.Reserve(count);
memcpy(&IntConstantList[addr], values, count * sizeof(int));
for (unsigned i = 0; i < count; i++)
{
IntConstantMap.Insert(values[i], addr + i);
}
return addr;
}
unsigned VMFunctionBuilder::AllocConstantsFloat(unsigned count, double *values)
{
unsigned addr = FloatConstantList.Reserve(count);
memcpy(&FloatConstantList[addr], values, count * sizeof(double));
for (unsigned i = 0; i < count; i++)
{
FloatConstantMap.Insert(values[i], addr + i);
}
return addr;
}
unsigned VMFunctionBuilder::AllocConstantsAddress(unsigned count, void **ptrs, VM_ATAG tag)
{
unsigned addr = AddressConstantList.Reserve(count);
AtagConstantList.Reserve(count);
memcpy(&AddressConstantList[addr], ptrs, count * sizeof(void *));
for (unsigned i = 0; i < count; i++)
{
AtagConstantList[addr + i] = tag;
AddrKonst loc = { addr+i, tag };
AddressConstantMap.Insert(ptrs[i], loc);
}
return addr;
}
unsigned VMFunctionBuilder::AllocConstantsString(unsigned count, FString *ptrs)
{
unsigned addr = StringConstantList.Reserve(count);
for (unsigned i = 0; i < count; i++)
{
StringConstantList[addr + i] = ptrs[i];
StringConstantMap.Insert(ptrs[i], addr + i);
}
return addr;
}
//==========================================================================
//
// VMFunctionBuilder :: ParamChange
//
// Adds delta to ActiveParam and keeps track of MaxParam.
//
//==========================================================================
void VMFunctionBuilder::ParamChange(int delta)
{
assert(delta > 0 || -delta <= ActiveParam);
ActiveParam += delta;
if (ActiveParam > MaxParam)
{
MaxParam = ActiveParam;
}
}
//==========================================================================
//
// VMFunctionBuilder :: RegAvailability - Constructor
//
//==========================================================================
VMFunctionBuilder::RegAvailability::RegAvailability()
{
memset(Used, 0, sizeof(Used));
MostUsed = 0;
}
//==========================================================================
//
// VMFunctionBuilder :: RegAvailability :: Get
//
// Gets one or more unused registers. If getting multiple registers, they
// will all be consecutive. Returns -1 if there were not enough consecutive
// registers to satisfy the request.
//
// Preference is given to low-numbered registers in an attempt to keep
// the maximum register count low so as to preserve VM stack space when this
// function is executed.
//
//==========================================================================
int VMFunctionBuilder::RegAvailability::Get(int count)
{
VM_UWORD mask;
int i, firstbit;
// Getting fewer than one register makes no sense, and
// the algorithm used here can only obtain ranges of up to 32 bits.
if (count < 1 || count > 32)
{
return -1;
}
mask = count == 32 ? ~0u : (1 << count) - 1;
for (i = 0; i < 256 / 32; ++i)
{
// Find the first word with free registers
VM_UWORD bits = Used[i];
if (bits != ~0u)
{
// Are there enough consecutive bits to satisfy the request?
// Search by 16, then 8, then 1 bit at a time for the first
// free register.
if ((bits & 0xFFFF) == 0xFFFF)
{
firstbit = ((bits & 0xFF0000) == 0xFF0000) ? 24 : 16;
}
else
{
firstbit = ((bits & 0xFF) == 0xFF) ? 8 : 0;
}
for (; firstbit < 32; ++firstbit)
{
if (((bits >> firstbit) & mask) == 0)
{
if (firstbit + count <= 32)
{ // Needed bits all fit in one word, so we got it.
if (i * 32 + firstbit + count > MostUsed)
{
MostUsed = i * 32 + firstbit + count;
}
Used[i] |= mask << firstbit;
return i * 32 + firstbit;
}
// Needed bits span two words, so check the next word.
else if (i < 256/32 - 1)
{ // There is a next word.
if (((Used[i + 1]) & (mask >> (32 - firstbit))) == 0)
{ // The next word has the needed open space, too.
if (i * 32 + firstbit + count > MostUsed)
{
MostUsed = i * 32 + firstbit + count;
}
Used[i] |= mask << firstbit;
Used[i + 1] |= mask >> (32 - firstbit);
return i * 32 + firstbit;
}
else
{ // Skip to the next word, because we know we won't find
// what we need if we stay inside this one. All bits
// from firstbit to the end of the word are 0. If the
// next word does not start with the x amount of 0's, we
// need to satisfy the request, then it certainly won't
// have the x+1 0's we would need if we started at
// firstbit+1 in this one.
firstbit = 32;
}
}
else
{ // Out of words.
break;
}
}
}
}
}
// No room!
return -1;
}
//==========================================================================
//
// VMFunctionBuilder :: RegAvailibity :: Return
//
// Marks a range of registers as free again.
//
//==========================================================================
void VMFunctionBuilder::RegAvailability::Return(int reg, int count)
{
assert(count >= 1 && count <= 32);
assert(reg >= 0 && reg + count <= 256);
VM_UWORD mask, partialmask;
int firstword, firstbit;
mask = count == 32 ? ~0u : (1 << count) - 1;
firstword = reg / 32;
firstbit = reg & 31;
if (firstbit + count <= 32)
{ // Range is all in one word.
mask <<= firstbit;
// If we are trying to return registers that are already free,
// it probably means that the caller messed up somewhere.
assert((Used[firstword] & mask) == mask);
Used[firstword] &= ~mask;
}
else
{ // Range is in two words.
partialmask = mask << firstbit;
assert((Used[firstword] & partialmask) == partialmask);
Used[firstword] &= ~partialmask;
partialmask = mask >> (32 - firstbit);
assert((Used[firstword + 1] & partialmask) == partialmask);
Used[firstword + 1] &= ~partialmask;
}
}
//==========================================================================
//
// VMFunctionBuilder :: RegAvailability :: Reuse
//
// Marks an unused register as in-use. Returns false if the register is
// already in use or true if it was successfully reused.
//
//==========================================================================
bool VMFunctionBuilder::RegAvailability::Reuse(int reg)
{
assert(reg >= 0 && reg <= 255);
assert(reg < MostUsed && "Attempt to reuse a register that was never used");
VM_UWORD mask = 1 << (reg & 31);
int word = reg / 32;
if (Used[word] & mask)
{ // It's already in use!
return false;
}
Used[word] |= mask;
return true;
}
//==========================================================================
//
// VMFunctionBuilder :: GetAddress
//
//==========================================================================
size_t VMFunctionBuilder::GetAddress()
{
return Code.Size();
}
//==========================================================================
//
// VMFunctionBuilder :: Emit
//
// Just dumbly output an instruction. Returns instruction position, not
// byte position. (Because all instructions are exactly four bytes long.)
//
//==========================================================================
size_t VMFunctionBuilder::Emit(int opcode, int opa, int opb, int opc)
{
static BYTE opcodes[] = { OP_LK, OP_LKF, OP_LKS, OP_LKP };
assert(opcode >= 0 && opcode < NUM_OPS);
assert(opa >= 0);
assert(opb >= 0);
assert(opc >= 0);
// The following were just asserts, meaning this would silently create broken code if there was an overflow
// if this happened in a release build. Not good.
// These are critical errors that need to be reported to the user.
// In addition, the limit of 256 constants can easily be exceeded with arrays so this had to be extended to
// 65535 by adding some checks here that map byte-limited instructions to alternatives that can handle larger indices.
// (See vmops.h for the remapping info.)
// Note: OP_CMPS also needs treatment, but I do not expect constant overflow to become an issue with strings, so for now there is no handling.
if (opa > 255)
{
if (opRemap[opcode].kReg != 1 || opa > 32767)
{
I_Error("Register limit exceeded");
}
int regtype = opRemap[opcode].kType;
ExpEmit emit(this, regtype);
Emit(opcodes[regtype], emit.RegNum, opa);
opcode = opRemap[opcode].altOp;
opa = emit.RegNum;
emit.Free(this);
}
if (opb > 255)
{
if (opRemap[opcode].kReg != 2 || opb > 32767)
{
I_Error("Register limit exceeded");
}
int regtype = opRemap[opcode].kType;
ExpEmit emit(this, regtype);
Emit(opcodes[regtype], emit.RegNum, opb);
opcode = opRemap[opcode].altOp;
opb = emit.RegNum;
emit.Free(this);
}
if (opc > 255)
{
if (opcode == OP_PARAM && (opb & REGT_KONST) && opc <= 32767)
{
int regtype = opb & REGT_TYPE;
opb = regtype;
ExpEmit emit(this, regtype);
Emit(opcodes[regtype], emit.RegNum, opc);
opc = emit.RegNum;
emit.Free(this);
}
else
{
if (opRemap[opcode].kReg != 4 || opc > 32767)
{
I_Error("Register limit exceeded");
}
int regtype = opRemap[opcode].kType;
ExpEmit emit(this, regtype);
Emit(opcodes[regtype], emit.RegNum, opc);
opcode = opRemap[opcode].altOp;
opc = emit.RegNum;
emit.Free(this);
}
}
if (opcode == OP_PARAM)
{
int chg;
if (opb & REGT_MULTIREG2) chg = 2;
else if (opb&REGT_MULTIREG3) chg = 3;
else chg = 1;
ParamChange(chg);
}
else if (opcode == OP_CALL || opcode == OP_CALL_K || opcode == OP_TAIL || opcode == OP_TAIL_K)
{
ParamChange(-opb);
}
VMOP op;
op.op = opcode;
op.a = opa;
op.b = opb;
op.c = opc;
return Code.Push(op);
}
size_t VMFunctionBuilder::Emit(int opcode, int opa, VM_SHALF opbc)
{
assert(opcode >= 0 && opcode < NUM_OPS);
assert(opa >= 0 && opa <= 255);
//assert(opbc >= -32768 && opbc <= 32767); always true due to parameter's width
VMOP op;
op.op = opcode;
op.a = opa;
op.i16 = opbc;
return Code.Push(op);
}
size_t VMFunctionBuilder::Emit(int opcode, int opabc)
{
assert(opcode >= 0 && opcode < NUM_OPS);
assert(opabc >= -(1 << 23) && opabc <= (1 << 24) - 1);
if (opcode == OP_PARAMI)
{
ParamChange(1);
}
VMOP op;
op.op = opcode;
op.i24 = opabc;
return Code.Push(op);
}
//==========================================================================
//
// VMFunctionBuilder :: EmitParamInt
//
// Passes a constant integer parameter, using either PARAMI and an immediate
// value or PARAM and a constant register, as appropriate.
//
//==========================================================================
size_t VMFunctionBuilder::EmitParamInt(int value)
{
// Immediates for PARAMI must fit in 24 bits.
if (((value << 8) >> 8) == value)
{
return Emit(OP_PARAMI, value);
}
else
{
return Emit(OP_PARAM, 0, REGT_INT | REGT_KONST, GetConstantInt(value));
}
}
//==========================================================================
//
// VMFunctionBuilder :: EmitLoadInt
//
// Loads an integer constant into a register, using either an immediate
// value or a constant register, as appropriate.
//
//==========================================================================
size_t VMFunctionBuilder::EmitLoadInt(int regnum, int value)
{
assert(regnum >= 0 && regnum < Registers[REGT_INT].MostUsed);
if (value >= -32768 && value <= 32767)
{
return Emit(OP_LI, regnum, value);
}
else
{
return Emit(OP_LK, regnum, GetConstantInt(value));
}
}
//==========================================================================
//
// VMFunctionBuilder :: EmitRetInt
//
// Returns an integer, using either an immediate value or a constant
// register, as appropriate.
//
//==========================================================================
size_t VMFunctionBuilder::EmitRetInt(int retnum, bool final, int value)
{
assert(retnum >= 0 && retnum <= 127);
if (value >= -32768 && value <= 32767)
{
return Emit(OP_RETI, retnum | (final << 7), value);
}
else
{
return Emit(OP_RET, retnum | (final << 7), REGT_INT | REGT_KONST, GetConstantInt(value));
}
}
//==========================================================================
//
// VMFunctionBuilder :: Backpatch
//
// Store a JMP instruction at <loc> that points at <target>.
//
//==========================================================================
void VMFunctionBuilder::Backpatch(size_t loc, size_t target)
{
assert(loc < Code.Size());
int offset = int(target - loc - 1);
assert(((offset << 8) >> 8) == offset);
Code[loc].op = OP_JMP;
Code[loc].i24 = offset;
}
void VMFunctionBuilder::BackpatchList(TArray<size_t> &locs, size_t target)
{
for (auto loc : locs)
Backpatch(loc, target);
}
//==========================================================================
//
// VMFunctionBuilder :: BackpatchToHere
//
// Store a JMP instruction at <loc> that points to the current code gen
// location.
//
//==========================================================================
void VMFunctionBuilder::BackpatchToHere(size_t loc)
{
Backpatch(loc, Code.Size());
}
void VMFunctionBuilder::BackpatchListToHere(TArray<size_t> &locs)
{
for (auto loc : locs)
Backpatch(loc, Code.Size());
}
//==========================================================================
//
// FFunctionBuildList
//
// This list contains all functions yet to build.
// All adding functions return a VMFunction - either a complete one
// for native functions or an empty VMScriptFunction for scripted ones
// This VMScriptFunction object later gets filled in with the actual
// info, but we get the pointer right after registering the function
// with the builder.
//
//==========================================================================
FFunctionBuildList FunctionBuildList;
VMFunction *FFunctionBuildList::AddFunction(PNamespace *gnspc, PFunction *functype, FxExpression *code, const FString &name, bool fromdecorate, int stateindex, int statecount, int lumpnum)
{
auto func = code->GetDirectFunction();
if (func != nullptr)
{
delete code;
return func;
}
//Printf("Adding %s\n", name.GetChars());
Item it;
assert(gnspc != nullptr);
it.CurGlobals = gnspc;
it.Func = functype;
it.Code = code;
it.PrintableName = name;
it.Function = new VMScriptFunction;
it.Function->Name = functype->SymbolName;
it.Function->PrintableName = name;
it.Function->ImplicitArgs = functype->GetImplicitArgs();
it.Proto = nullptr;
it.FromDecorate = fromdecorate;
it.StateIndex = stateindex;
it.StateCount = statecount;
it.Lump = lumpnum;
assert(it.Func->Variants.Size() == 1);
it.Func->Variants[0].Implementation = it.Function;
// set prototype for named functions.
if (it.Func->SymbolName != NAME_None)
{
it.Function->Proto = it.Func->Variants[0].Proto;
}
mItems.Push(it);
return it.Function;
}
void FFunctionBuildList::Build()
{
int errorcount = 0;
int codesize = 0;
FILE *dump = nullptr;
if (Args->CheckParm("-dumpdisasm")) dump = fopen("disasm.txt", "w");
for (auto &item : mItems)
{
assert(item.Code != NULL);
// We don't know the return type in advance for anonymous functions.
FCompileContext ctx(item.CurGlobals, item.Func, item.Func->SymbolName == NAME_None ? nullptr : item.Func->Variants[0].Proto, item.FromDecorate, item.StateIndex, item.StateCount, item.Lump);
// Allocate registers for the function's arguments and create local variable nodes before starting to resolve it.
VMFunctionBuilder buildit(item.Func->GetImplicitArgs());
for (unsigned i = 0; i < item.Func->Variants[0].Proto->ArgumentTypes.Size(); i++)
{
auto type = item.Func->Variants[0].Proto->ArgumentTypes[i];
auto name = item.Func->Variants[0].ArgNames[i];
auto flags = item.Func->Variants[0].ArgFlags[i];
// this won't get resolved and won't get emitted. It is only needed so that the code generator can retrieve the necessary info about this argument to do its work.
auto local = new FxLocalVariableDeclaration(type, name, nullptr, flags, FScriptPosition());
if (!(flags & VARF_Out)) local->RegNum = buildit.Registers[type->GetRegType()].Get(type->GetRegCount());
else local->RegNum = buildit.Registers[REGT_POINTER].Get(1);
ctx.FunctionArgs.Push(local);
}
FScriptPosition::StrictErrors = !item.FromDecorate;
item.Code = item.Code->Resolve(ctx);
// If we need extra space, load the frame pointer into a register so that we do not have to call the wasteful LFP instruction more than once.
if (item.Function->ExtraSpace > 0)
{
buildit.FramePointer = ExpEmit(&buildit, REGT_POINTER);
buildit.FramePointer.Fixed = true;
buildit.Emit(OP_LFP, buildit.FramePointer.RegNum);
}
// Make sure resolving it didn't obliterate it.
if (item.Code != nullptr)
{
if (!item.Code->CheckReturn())
{
auto newcmpd = new FxCompoundStatement(item.Code->ScriptPosition);
newcmpd->Add(item.Code);
newcmpd->Add(new FxReturnStatement(nullptr, item.Code->ScriptPosition));
item.Code = newcmpd->Resolve(ctx);
}
item.Proto = ctx.ReturnProto;
if (item.Proto == nullptr)
{
item.Code->ScriptPosition.Message(MSG_ERROR, "Function %s without prototype", item.PrintableName.GetChars());
continue;
}
// Generate prototype for anonymous functions.
VMScriptFunction *sfunc = item.Function;
// create a new prototype from the now known return type and the argument list of the function's template prototype.
if (sfunc->Proto == nullptr)
{
sfunc->Proto = NewPrototype(item.Proto->ReturnTypes, item.Func->Variants[0].Proto->ArgumentTypes);
}
// Emit code
try
{
sfunc->SourceFileName = item.Code->ScriptPosition.FileName; // remember the file name for printing error messages if something goes wrong in the VM.
buildit.BeginStatement(item.Code);
item.Code->Emit(&buildit);
buildit.EndStatement();
buildit.MakeFunction(sfunc);
sfunc->NumArgs = 0;
// NumArgs for the VMFunction must be the amount of stack elements, which can differ from the amount of logical function arguments if vectors are in the list.
// For the VM a vector is 2 or 3 args, depending on size.
for (auto s : item.Func->Variants[0].Proto->ArgumentTypes)
{
sfunc->NumArgs += s->GetRegCount();
}
if (dump != nullptr)
{
DumpFunction(dump, sfunc, item.PrintableName.GetChars(), (int)item.PrintableName.Len());
codesize += sfunc->CodeSize;
}
sfunc->Unsafe = ctx.Unsafe;
}
catch (CRecoverableError &err)
{
// catch errors from the code generator and pring something meaningful.
item.Code->ScriptPosition.Message(MSG_ERROR, "%s in %s", err.GetMessage(), item.PrintableName.GetChars());
}
}
delete item.Code;
if (dump != nullptr)
{
fflush(dump);
}
}
if (dump != nullptr)
{
fprintf(dump, "\n*************************************************************************\n%i code bytes\n", codesize * 4);
fclose(dump);
}
FScriptPosition::StrictErrors = false;
mItems.Clear();
FxAlloc.FreeAllBlocks();
}

View file

@ -1,160 +0,0 @@
#ifndef VMUTIL_H
#define VMUTIL_H
#include "dobject.h"
class VMFunctionBuilder;
class FxExpression;
class FxLocalVariableDeclaration;
struct ExpEmit
{
ExpEmit() : RegNum(0), RegType(REGT_NIL), RegCount(1), Konst(false), Fixed(false), Final(false), Target(false) {}
ExpEmit(int reg, int type, bool konst = false, bool fixed = false) : RegNum(reg), RegType(type), RegCount(1), Konst(konst), Fixed(fixed), Final(false), Target(false) {}
ExpEmit(VMFunctionBuilder *build, int type, int count = 1);
void Free(VMFunctionBuilder *build);
void Reuse(VMFunctionBuilder *build);
uint16_t RegNum;
uint8_t RegType, RegCount;
// We are at 8 bytes for this struct, no matter what, so it's rather pointless to squeeze these flags into bitfields.
bool Konst, Fixed, Final, Target;
};
class VMFunctionBuilder
{
public:
// Keeps track of which registers are available by way of a bitmask table.
class RegAvailability
{
public:
RegAvailability();
int GetMostUsed() { return MostUsed; }
int Get(int count); // Returns the first register in the range
void Return(int reg, int count);
bool Reuse(int regnum);
private:
VM_UWORD Used[256/32]; // Bitmap of used registers (bit set means reg is used)
int MostUsed;
friend class VMFunctionBuilder;
};
VMFunctionBuilder(int numimplicits);
~VMFunctionBuilder();
void BeginStatement(FxExpression *stmt);
void EndStatement();
void MakeFunction(VMScriptFunction *func);
// Returns the constant register holding the value.
unsigned GetConstantInt(int val);
unsigned GetConstantFloat(double val);
unsigned GetConstantAddress(void *ptr, VM_ATAG tag);
unsigned GetConstantString(FString str);
unsigned AllocConstantsInt(unsigned int count, int *values);
unsigned AllocConstantsFloat(unsigned int count, double *values);
unsigned AllocConstantsAddress(unsigned int count, void **ptrs, VM_ATAG tag);
unsigned AllocConstantsString(unsigned int count, FString *ptrs);
// Returns the address of the next instruction to be emitted.
size_t GetAddress();
// Returns the address of the newly-emitted instruction.
size_t Emit(int opcode, int opa, int opb, int opc);
size_t Emit(int opcode, int opa, VM_SHALF opbc);
size_t Emit(int opcode, int opabc);
size_t EmitParamInt(int value);
size_t EmitLoadInt(int regnum, int value);
size_t EmitRetInt(int retnum, bool final, int value);
void Backpatch(size_t addr, size_t target);
void BackpatchToHere(size_t addr);
void BackpatchList(TArray<size_t> &addrs, size_t target);
void BackpatchListToHere(TArray<size_t> &addrs);
// Write out complete constant tables.
void FillIntConstants(int *konst);
void FillFloatConstants(double *konst);
void FillAddressConstants(FVoidObj *konst, VM_ATAG *tags);
void FillStringConstants(FString *strings);
// PARAM increases ActiveParam; CALL decreases it.
void ParamChange(int delta);
// Track available registers.
RegAvailability Registers[4];
// amount of implicit parameters so that proper code can be emitted for method calls
int NumImplicits;
// keep the frame pointer, if needed, in a register because the LFP opcode is hideously inefficient, requiring more than 20 instructions on x64.
ExpEmit FramePointer;
TArray<FxLocalVariableDeclaration *> ConstructedStructs;
private:
struct AddrKonst
{
unsigned KonstNum;
VM_ATAG Tag;
};
TArray<FStatementInfo> LineNumbers;
TArray<FxExpression *> StatementStack;
TArray<int> IntConstantList;
TArray<double> FloatConstantList;
TArray<void *> AddressConstantList;
TArray<VM_ATAG> AtagConstantList;
TArray<FString> StringConstantList;
// These map from the constant value to its position in the constant table.
TMap<int, unsigned> IntConstantMap;
TMap<double, unsigned> FloatConstantMap;
TMap<void *, AddrKonst> AddressConstantMap;
TMap<FString, unsigned> StringConstantMap;
int MaxParam;
int ActiveParam;
TArray<VMOP> Code;
};
void DumpFunction(FILE *dump, VMScriptFunction *sfunc, const char *label, int labellen);
//==========================================================================
//
//
//
//==========================================================================
class FxExpression;
class FFunctionBuildList
{
struct Item
{
PFunction *Func = nullptr;
FxExpression *Code = nullptr;
PPrototype *Proto = nullptr;
VMScriptFunction *Function = nullptr;
PNamespace *CurGlobals = nullptr;
FString PrintableName;
int StateIndex;
int StateCount;
int Lump;
bool FromDecorate;
};
TArray<Item> mItems;
public:
VMFunction *AddFunction(PNamespace *curglobals, PFunction *func, FxExpression *code, const FString &name, bool fromdecorate, int currentstate, int statecnt, int lumpnum);
void Build();
};
extern FFunctionBuildList FunctionBuildList;
#endif

View file

@ -1,653 +0,0 @@
/*
** vmdisasm.cpp
**
**---------------------------------------------------------------------------
** Copyright -2016 Randy Heit
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include "dobject.h"
#include "c_console.h"
#include "templates.h"
#define NOP MODE_AUNUSED | MODE_BUNUSED | MODE_CUNUSED
#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 RPKP MODE_AP | MODE_BKP | 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 I8I16 MODE_AIMMZ | MODE_BCIMMZ
#define __BCP MODE_AUNUSED | MODE_BCJOINT | MODE_BCPARAM
#define RPI8 MODE_AP | MODE_BIMMZ | MODE_CUNUSED
#define KPI8 MODE_AKP | MODE_BIMMZ | MODE_CUNUSED
#define RPI8I8 MODE_AP | MODE_BIMMZ | MODE_CIMMZ
#define RPRPI8 MODE_AP | MODE_BP | 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 CASTB MODE_AI | 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 RFRII8 MODE_AF | MODE_BI | MODE_CIMMZ
#define RPRII8 MODE_AP | MODE_BI | MODE_CIMMZ
#define RSRII8 MODE_AS | 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 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 RVRVRF MODE_AV | MODE_BV | MODE_CF
#define RVRVKF MODE_AV | MODE_BV | MODE_CKF
#define RVKVRF MODE_AV | MODE_BKV | MODE_CF
#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
#define CIRR MODE_ACMP | MODE_BI | MODE_CI
#define CIRK MODE_ACMP | MODE_BI | MODE_CKI
#define CIKR MODE_ACMP | MODE_BKI | MODE_CI
#define CFRR MODE_ACMP | MODE_BF | MODE_CF
#define CFRK MODE_ACMP | MODE_BF | MODE_CKF
#define CFKR MODE_ACMP | MODE_BKF | MODE_CF
#define CVRR MODE_ACMP | MODE_BV | MODE_CV
#define CVRK MODE_ACMP | MODE_BV | MODE_CKV
#define CPRR MODE_ACMP | MODE_BP | MODE_CP
#define CPRK MODE_ACMP | MODE_BP | MODE_CKP
const VMOpInfo OpInfo[NUM_OPS] =
{
#define xx(op, name, mode, alt, kreg, ktype) { #name, mode }
#include "vmops.h"
};
static const char *const FlopNames[] =
{
"abs",
"neg",
"exp",
"log",
"log10",
"sqrt",
"ceil",
"floor",
"acos rad",
"asin rad",
"atan rad",
"cos rad",
"sin rad",
"tan rad",
"acos deg",
"asin deg",
"atan deg",
"cos deg",
"sin deg",
"tan deg",
"cosh",
"sinh",
"tanh",
};
static int print_reg(FILE *out, int col, int arg, int mode, int immshift, const VMScriptFunction *func);
static int printf_wrapper(FILE *f, const char *fmt, ...)
{
va_list argptr;
int count;
va_start(argptr, fmt);
if (f == NULL)
{
count = VPrintf(PRINT_HIGH, fmt, argptr);
}
else
{
count = vfprintf(f, fmt, argptr);
}
va_end(argptr);
return count;
}
void VMDumpConstants(FILE *out, const VMScriptFunction *func)
{
char tmp[30];
int i, j, k, kk;
if (func->KonstD != NULL && func->NumKonstD != 0)
{
printf_wrapper(out, "\nConstant integers:\n");
kk = (func->NumKonstD + 3) / 4;
for (i = 0; i < kk; ++i)
{
for (j = 0, k = i; j < 4 && k < func->NumKonstD; j++, k += kk)
{
mysnprintf(tmp, countof(tmp), "%3d. %d", k, func->KonstD[k]);
printf_wrapper(out, "%-20s", tmp);
}
printf_wrapper(out, "\n");
}
}
if (func->KonstF != NULL && func->NumKonstF != 0)
{
printf_wrapper(out, "\nConstant floats:\n");
kk = (func->NumKonstF + 3) / 4;
for (i = 0; i < kk; ++i)
{
for (j = 0, k = i; j < 4 && k < func->NumKonstF; j++, k += kk)
{
mysnprintf(tmp, countof(tmp), "%3d. %.16f", k, func->KonstF[k]);
printf_wrapper(out, "%-20s", tmp);
}
printf_wrapper(out, "\n");
}
}
if (func->KonstA != NULL && func->NumKonstA != 0)
{
printf_wrapper(out, "\nConstant addresses:\n");
kk = (func->NumKonstA + 3) / 4;
for (i = 0; i < kk; ++i)
{
for (j = 0, k = i; j < 4 && k < func->NumKonstA; j++, k += kk)
{
mysnprintf(tmp, countof(tmp), "%3d. %p:%d", k, func->KonstA[k].v, func->KonstATags()[k]);
printf_wrapper(out, "%-22s", tmp);
}
printf_wrapper(out, "\n");
}
}
if (func->KonstS != NULL && func->NumKonstS != 0)
{
printf_wrapper(out, "\nConstant strings:\n");
for (i = 0; i < func->NumKonstS; ++i)
{
printf_wrapper(out, "%3d. %s\n", i, func->KonstS[i].GetChars());
}
}
}
void VMDisasm(FILE *out, const VMOP *code, int codesize, const VMScriptFunction *func)
{
VMFunction *callfunc;
const char *name;
int col;
int mode;
int a;
bool cmp;
char cmpname[8];
for (int i = 0; i < codesize; ++i)
{
name = OpInfo[code[i].op].Name;
mode = OpInfo[code[i].op].Mode;
a = code[i].a;
cmp = (mode & MODE_ATYPE) == MODE_ACMP;
// String comparison encodes everything in a single instruction.
if (code[i].op == OP_CMPS)
{
switch (a & CMP_METHOD_MASK)
{
case CMP_EQ: name = "beq"; break;
case CMP_LT: name = "blt"; break;
case CMP_LE: name = "ble"; 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;
cmp = true;
}
if (code[i].op == OP_PARAM && code[i].b & REGT_ADDROF)
{
name = "parama";
}
if (cmp)
{ // Comparison instruction. Modify name for inverted test.
if (!(a & CMP_CHECK))
{
strcpy(cmpname, name);
if (name[1] == 'e')
{ // eq -> ne
cmpname[1] = 'n', cmpname[2] = 'e';
}
else if (name[2] == 't')
{ // lt -> ge
cmpname[1] = 'g', cmpname[2] = 'e';
}
else
{ // le -> gt
cmpname[1] = 'g', cmpname[2] = 't';
}
name = cmpname;
}
}
printf_wrapper(out, "%08x: %02x%02x%02x%02x %-8s", i << 2, code[i].op, code[i].a, code[i].b, code[i].c, name);
col = 0;
switch (code[i].op)
{
case OP_JMP:
case OP_TRY:
col = printf_wrapper(out, "%08x", (i + 1 + code[i].i24) << 2);
break;
case OP_PARAMI:
col = printf_wrapper(out, "%d", code[i].i24);
break;
case OP_CALL_K:
case OP_TAIL_K:
{
callfunc = (VMFunction *)func->KonstA[code[i].a].o;
col = printf_wrapper(out, "[%p],%d", callfunc, code[i].b);
if (code[i].op == OP_CALL_K)
{
col += printf_wrapper(out, ",%d", code[i].c);
}
break;
}
case OP_RET:
if (code[i].b != REGT_NIL)
{
if (a == RET_FINAL)
{
col = print_reg(out, 0, code[i].i16u, MODE_PARAM, 16, func);
}
else
{
col = print_reg(out, 0, a & ~RET_FINAL, (mode & MODE_ATYPE) >> MODE_ASHIFT, 24, func);
col += print_reg(out, col, code[i].i16u, MODE_PARAM, 16, func);
if (a & RET_FINAL)
{
col += printf_wrapper(out, " [final]");
}
}
}
break;
case OP_RETI:
if (a == RET_FINAL)
{
col = printf_wrapper(out, "%d", code[i].i16);
}
else
{
col = print_reg(out, 0, a & ~RET_FINAL, (mode & MODE_ATYPE) >> MODE_ASHIFT, 24, func);
col += print_reg(out, col, code[i].i16, MODE_IMMS, 16, func);
if (a & RET_FINAL)
{
col += printf_wrapper(out, " [final]");
}
}
break;
case OP_FLOP:
col = printf_wrapper(out, "f%d,f%d,%d", code[i].a, code[i].b, code[i].c);
if (code[i].c < countof(FlopNames))
{
col += printf_wrapper(out, " [%s]", FlopNames[code[i].c]);
}
break;
default:
if ((mode & MODE_BCTYPE) == MODE_BCCAST)
{
switch (code[i].c)
{
case CASTB_I:
mode = MODE_AI | MODE_BI | MODE_CUNUSED;
break;
case CASTB_A:
mode = MODE_AI | MODE_BP | MODE_CUNUSED;
break;
case CAST_I2F:
case CAST_U2F:
mode = MODE_AF | MODE_BI | MODE_CUNUSED;
break;
case CAST_Co2S:
case CAST_So2S:
case CAST_N2S:
case CAST_I2S:
case CAST_U2S:
mode = MODE_AS | MODE_BI | MODE_CUNUSED;
break;
case CAST_F2I:
case CAST_F2U:
case CASTB_F:
mode = MODE_AI | MODE_BF | MODE_CUNUSED;
break;
case CAST_F2S:
case CAST_V22S:
case CAST_V32S:
mode = MODE_AS | MODE_BF | MODE_CUNUSED;
break;
case CAST_P2S:
mode = MODE_AS | MODE_BP | MODE_CUNUSED;
break;
case CAST_S2Co:
case CAST_S2So:
case CAST_S2N:
case CAST_S2I:
case CASTB_S:
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(out, 0, a, (mode & MODE_ATYPE) >> MODE_ASHIFT, 24, func);
if ((mode & MODE_BCTYPE) == MODE_BCTHROW)
{
if (code[i].a == 0)
{
mode = (MODE_BP | MODE_CUNUSED);
}
else if (code[i].a == 1)
{
mode = (MODE_BKP | MODE_CUNUSED);
}
else
{
mode = (MODE_BCJOINT | MODE_BCIMMS);
}
}
else if ((mode & MODE_BCTYPE) == MODE_BCCATCH)
{
switch (code[i].a)
{
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(out, col, code[i].i16u, (mode & MODE_BCTYPE) >> MODE_BCSHIFT, 16, func);
}
else
{
col += print_reg(out, col, code[i].b, (mode & MODE_BTYPE) >> MODE_BSHIFT, 24, func);
col += print_reg(out, col, code[i].c, (mode & MODE_CTYPE) >> MODE_CSHIFT, 24, func);
}
break;
}
if (cmp && i + 1 < codesize)
{
if (code[i+1].op != OP_JMP)
{ // comparison instructions must be followed by jump
col += printf_wrapper(out, " => *!*!*!*\n");
}
else
{
col += printf_wrapper(out, " => %08x", (i + 2 + code[i+1].i24) << 2);
}
}
if (col > 30)
{
col = 30;
}
printf_wrapper(out, "%*c", 30 - col, ';');
if (!cmp && (code[i].op == OP_JMP || code[i].op == OP_TRY || code[i].op == OP_PARAMI))
{
printf_wrapper(out, "%d\n", code[i].i24);
}
else
{
printf_wrapper(out, "%d,%d,%d", code[i].a, code[i].b, code[i].c);
if (cmp && i + 1 < codesize && code[i+1].op == OP_JMP)
{
printf_wrapper(out, ",%d\n", code[++i].i24);
}
else if (code[i].op == OP_CALL_K || code[i].op == OP_TAIL_K)
{
printf_wrapper(out, " [%s]\n", callfunc->PrintableName.GetChars());
}
else
{
printf_wrapper(out, "\n");
}
}
}
}
static int print_reg(FILE *out, int col, int arg, int mode, int immshift, const VMScriptFunction *func)
{
if (mode == MODE_UNUSED || mode == MODE_CMP)
{
return 0;
}
if (col > 0)
{
col = printf_wrapper(out, ",");
}
switch(mode)
{
case MODE_I:
return col+printf_wrapper(out, "d%d", arg);
case MODE_F:
return col+printf_wrapper(out, "f%d", arg);
case MODE_S:
return col+printf_wrapper(out, "s%d", arg);
case MODE_P:
return col+printf_wrapper(out, "a%d", arg);
case MODE_V:
return col+printf_wrapper(out, "v%d", arg);
case MODE_KI:
if (func != NULL)
{
return col+printf_wrapper(out, "%d", func->KonstD[arg]);
}
return printf_wrapper(out, "kd%d", arg);
case MODE_KF:
if (func != NULL)
{
return col+printf_wrapper(out, "%#g", func->KonstF[arg]);
}
return col+printf_wrapper(out, "kf%d", arg);
case MODE_KS:
if (func != NULL)
{
return col+printf_wrapper(out, "\"%.27s\"", func->KonstS[arg].GetChars());
}
return col+printf_wrapper(out, "ks%d", arg);
case MODE_KP:
if (func != NULL)
{
return col+printf_wrapper(out, "%p", func->KonstA[arg]);
}
return col+printf_wrapper(out, "ka%d", arg);
case MODE_KV:
if (func != NULL)
{
return col+printf_wrapper(out, "(%f,%f,%f)", func->KonstF[arg], func->KonstF[arg+1], func->KonstF[arg+2]);
}
return col+printf_wrapper(out, "kv%d", arg);
case MODE_IMMS:
return col+printf_wrapper(out, "%d", (arg << immshift) >> immshift);
case MODE_IMMZ:
return col+printf_wrapper(out, "%d", arg);
case MODE_PARAM:
{
int regtype, regnum;
#ifdef __BIG_ENDIAN__
regtype = (arg >> 8) & 255;
regnum = arg & 255;
#else
regtype = arg & 255;
regnum = (arg >> 8) & 255;
#endif
switch (regtype & (REGT_TYPE | REGT_KONST | REGT_MULTIREG))
{
case REGT_INT:
return col+printf_wrapper(out, "d%d", regnum);
case REGT_FLOAT:
return col+printf_wrapper(out, "f%d", regnum);
case REGT_STRING:
return col+printf_wrapper(out, "s%d", regnum);
case REGT_POINTER:
return col+printf_wrapper(out, "a%d", regnum);
case REGT_FLOAT | REGT_MULTIREG2:
return col+printf_wrapper(out, "v%d.2", regnum);
case REGT_FLOAT | REGT_MULTIREG3:
return col+printf_wrapper(out, "v%d.3", regnum);
case REGT_INT | REGT_KONST:
return col+print_reg(out, 0, regnum, MODE_KI, 0, func);
case REGT_FLOAT | REGT_KONST:
return col+print_reg(out, 0, regnum, MODE_KF, 0, func);
case REGT_STRING | REGT_KONST:
return col+print_reg(out, 0, regnum, MODE_KS, 0, func);
case REGT_POINTER | REGT_KONST:
return col+print_reg(out, 0, regnum, MODE_KP, 0, func);
case REGT_FLOAT | REGT_MULTIREG | REGT_KONST:
return col+print_reg(out, 0, regnum, MODE_KV, 0, func);
default:
if (regtype == REGT_NIL)
{
return col+printf_wrapper(out, "nil");
}
return col+printf_wrapper(out, "param[t=%d,%c,%c,n=%d]",
regtype & REGT_TYPE,
regtype & REGT_KONST ? 'k' : 'r',
regtype & REGT_MULTIREG ? 'm' : 's',
regnum);
}
}
default:
return col+printf_wrapper(out, "$%d", arg);
}
return col;
}
//==========================================================================
//
// Do some postprocessing after everything has been defined
//
//==========================================================================
void DumpFunction(FILE *dump, VMScriptFunction *sfunc, const char *label, int labellen)
{
const char *marks = "=======================================================";
fprintf(dump, "\n%.*s %s %.*s", MAX(3, 38 - labellen / 2), marks, label, MAX(3, 38 - labellen / 2), marks);
fprintf(dump, "\nInteger regs: %-3d Float regs: %-3d Address regs: %-3d String regs: %-3d\nStack size: %d\n",
sfunc->NumRegD, sfunc->NumRegF, sfunc->NumRegA, sfunc->NumRegS, sfunc->MaxParam);
VMDumpConstants(dump, sfunc);
fprintf(dump, "\nDisassembly @ %p:\n", sfunc->Code);
VMDisasm(dump, sfunc->Code, sfunc->CodeSize, sfunc);
}