- cleaned out a large part of unused methods from VMValue.

- keep string registers which are being used as function parameters allocated until after the function call returns. This is for allowing to pass strings by reference which would avoid some costly constructor/destructor loops in the call instruction.
This commit is contained in:
Christoph Oelckers 2017-03-19 21:25:30 +01:00
parent 0a11e38967
commit 403c5693a9
4 changed files with 29 additions and 194 deletions

View file

@ -503,7 +503,7 @@ static int EncodeRegType(ExpEmit reg)
//
//==========================================================================
static int EmitParameter(VMFunctionBuilder *build, FxExpression *operand, const FScriptPosition &pos)
static int EmitParameter(VMFunctionBuilder *build, FxExpression *operand, const FScriptPosition &pos, TArray<ExpEmit> *tempstrings = nullptr)
{
ExpEmit where = operand->Emit(build);
@ -516,7 +516,14 @@ static int EmitParameter(VMFunctionBuilder *build, FxExpression *operand, const
else
{
build->Emit(OP_PARAM, 0, EncodeRegType(where), where.RegNum);
where.Free(build);
if (tempstrings != nullptr && where.RegType == REGT_STRING && !where.Fixed && !where.Konst)
{
tempstrings->Push(where); // keep temp strings until after the function call.
}
else
{
where.Free(build);
}
return where.RegCount;
}
}
@ -6581,18 +6588,6 @@ FxClassDefaults::~FxClassDefaults()
}
//==========================================================================
//
//
//
//==========================================================================
PPrototype *FxClassDefaults::ReturnProto()
{
EmitTail = true;
return FxExpression::ReturnProto();
}
//==========================================================================
//
//
@ -8572,7 +8567,6 @@ FxVMFunctionCall::FxVMFunctionCall(FxExpression *self, PFunction *func, FArgumen
Self = self;
Function = func;
ArgList = std::move(args);
EmitTail = false;
NoVirtual = novirtual;
CallingFunction = nullptr;
}
@ -8595,6 +8589,8 @@ FxVMFunctionCall::~FxVMFunctionCall()
PPrototype *FxVMFunctionCall::ReturnProto()
{
if (hasStringArgs)
return FxExpression::ReturnProto();
EmitTail = true;
return Function->Variants[0].Proto;
}
@ -8810,6 +8806,10 @@ FxExpression *FxVMFunctionCall::Resolve(FCompileContext& ctx)
}
failed |= (x == nullptr);
ArgList[i] = x;
if (!failed && x->ValueType == TypeString)
{
hasStringArgs = true;
}
}
int numargs = ArgList.Size() + implicit;
if ((unsigned)numargs < argtypes.Size() && argtypes[numargs] != nullptr)
@ -8861,6 +8861,8 @@ FxExpression *FxVMFunctionCall::Resolve(FCompileContext& ctx)
ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build)
{
TArray<ExpEmit> tempstrings;
assert(build->Registers[REGT_POINTER].GetMostUsed() >= build->NumImplicits);
int count = 0;
@ -8873,6 +8875,7 @@ ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build)
{
ArgList.DeleteAndClear();
ArgList.ShrinkToFit();
for (auto & exp : tempstrings) exp.Free(build);
return reg;
}
}
@ -8939,7 +8942,7 @@ ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build)
// Emit code to pass explicit parameters
for (unsigned i = 0; i < ArgList.Size(); ++i)
{
count += EmitParameter(build, ArgList[i], ScriptPosition);
count += EmitParameter(build, ArgList[i], ScriptPosition, &tempstrings);
}
ArgList.DeleteAndClear();
ArgList.ShrinkToFit();
@ -8954,6 +8957,7 @@ ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build)
build->Emit(OP_TAIL_K, funcaddr, count, 0);
ExpEmit call;
call.Final = true;
for (auto & exp : tempstrings) exp.Free(build);
return call;
}
else if (vmfunc->Proto->ReturnTypes.Size() > 0)
@ -8964,6 +8968,7 @@ ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build)
else
{ // Call, expecting no results
build->Emit(OP_CALL_K, funcaddr, count, 0);
for (auto & exp : tempstrings) exp.Free(build);
return ExpEmit();
}
}
@ -8978,6 +8983,7 @@ ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build)
build->Emit(OP_TAIL, funcreg.RegNum, count, 0);
ExpEmit call;
call.Final = true;
for (auto & exp : tempstrings) exp.Free(build);
return call;
}
else if (vmfunc->Proto->ReturnTypes.Size() > 0)
@ -8988,6 +8994,7 @@ ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build)
else
{ // Call, expecting no results
build->Emit(OP_CALL, funcreg.RegNum, count, 0);
for (auto & exp : tempstrings) exp.Free(build);
return ExpEmit();
}
}
@ -8997,6 +9004,7 @@ handlereturns:
// Regular call, will not write to ReturnRegs
ExpEmit reg(build, vmfunc->Proto->ReturnTypes[0]->GetRegType(), vmfunc->Proto->ReturnTypes[0]->GetRegCount());
build->Emit(OP_RESULT, 0, EncodeRegType(reg), reg.RegNum);
for (auto & exp : tempstrings) exp.Free(build);
return reg;
}
else
@ -9009,8 +9017,9 @@ handlereturns:
build->Emit(OP_RESULT, 0, EncodeRegType(reg), reg.RegNum);
ReturnRegs.Push(reg);
}
return ExpEmit();
}
for (auto & exp : tempstrings) exp.Free(build);
return ExpEmit();
}
//==========================================================================

View file

@ -407,7 +407,6 @@ class FxClassDefaults : public FxExpression
public:
FxClassDefaults(FxExpression *, const FScriptPosition &);
~FxClassDefaults();
PPrototype *ReturnProto();
FxExpression *Resolve(FCompileContext&);
ExpEmit Emit(VMFunctionBuilder *build);
};
@ -1704,8 +1703,9 @@ class FxVMFunctionCall : public FxExpression
{
friend class FxMultiAssign;
bool EmitTail;
bool EmitTail = false;
bool NoVirtual;
bool hasStringArgs = false;
FxExpression *Self;
PFunction *Function;
FArgumentList ArgList;

View file

@ -570,6 +570,7 @@ size_t VMFunctionBuilder::Emit(int opcode, int opa, int opb, int opc)
assert(opb >= 0);
assert(opc >= 0);
assert(opcode != OP_NOP);
// 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.

View file

@ -481,181 +481,6 @@ struct VMValue
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;
}
void SetPointer(void *v, VM_ATAG atag=ATAG_GENERIC)
{
Kill();
a = v;
this->atag = atag;
Type = REGT_POINTER;
}
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)