- branch optimization.

This commit is contained in:
Christoph Oelckers 2016-12-02 00:51:29 +01:00
parent 17d9a152e7
commit 77192fa9dd
4 changed files with 166 additions and 97 deletions

View File

@ -288,6 +288,45 @@ ExpEmit FxExpression::Emit (VMFunctionBuilder *build)
} }
//==========================================================================
//
//
//
//==========================================================================
void FxExpression::EmitCompare(VMFunctionBuilder *build, bool invert, TArray<size_t> &patchspots_yes, TArray<size_t> &patchspots_no)
{
ExpEmit op = Emit(build);
ExpEmit i;
assert(op.RegType != REGT_NIL && op.RegCount == 1 && !op.Konst);
switch (op.RegType)
{
case REGT_INT:
build->Emit(OP_EQ_K, !invert, op.RegNum, build->GetConstantInt(0));
break;
case REGT_FLOAT:
build->Emit(OP_EQF_K, !invert, op.RegNum, build->GetConstantFloat(0));
break;
case REGT_POINTER:
build->Emit(OP_EQA_K, !invert, op.RegNum, build->GetConstantAddress(0, ATAG_GENERIC));
break;
case REGT_STRING:
i = ExpEmit(build, REGT_INT);
build->Emit(OP_LENS, i.RegNum, op.RegNum);
build->Emit(OP_EQ_K, !invert, i.RegNum, build->GetConstantInt(0));
i.Free(build);
break;
default:
break;
}
patchspots_no.Push(build->Emit(OP_JMP, 0));
op.Free(build);
}
//========================================================================== //==========================================================================
// //
// //
@ -757,7 +796,6 @@ ExpEmit FxBoolCast::Emit(VMFunctionBuilder *build)
{ {
ExpEmit to(build, REGT_INT); ExpEmit to(build, REGT_INT);
from.Free(build); from.Free(build);
// Preload result with 0.
build->Emit(OP_CASTB, to.RegNum, from.RegNum, from.RegType == REGT_INT ? CASTB_I : from.RegType == REGT_FLOAT ? CASTB_F : CASTB_A); build->Emit(OP_CASTB, to.RegNum, from.RegNum, from.RegType == REGT_INT ? CASTB_I : from.RegType == REGT_FLOAT ? CASTB_F : CASTB_A);
return to; return to;
} }
@ -773,6 +811,17 @@ ExpEmit FxBoolCast::Emit(VMFunctionBuilder *build)
// //
//========================================================================== //==========================================================================
void FxBoolCast::EmitCompare(VMFunctionBuilder *build, bool invert, TArray<size_t> &patchspots_yes, TArray<size_t> &patchspots_no)
{
basex->EmitCompare(build, invert, patchspots_yes, patchspots_no);
}
//==========================================================================
//
//
//
//==========================================================================
FxIntCast::FxIntCast(FxExpression *x, bool nowarn, bool explicitly) FxIntCast::FxIntCast(FxExpression *x, bool nowarn, bool explicitly)
: FxExpression(EFX_IntCast, x->ScriptPosition) : FxExpression(EFX_IntCast, x->ScriptPosition)
{ {
@ -1872,6 +1921,17 @@ ExpEmit FxUnaryNotBoolean::Emit(VMFunctionBuilder *build)
// //
//========================================================================== //==========================================================================
void FxUnaryNotBoolean::EmitCompare(VMFunctionBuilder *build, bool invert, TArray<size_t> &patchspots_yes, TArray<size_t> &patchspots_no)
{
Operand->EmitCompare(build, !invert, patchspots_yes, patchspots_no);
}
//==========================================================================
//
//
//
//==========================================================================
FxSizeAlign::FxSizeAlign(FxExpression *operand, int which) FxSizeAlign::FxSizeAlign(FxExpression *operand, int which)
: FxExpression(EFX_SizeAlign, operand->ScriptPosition) : FxExpression(EFX_SizeAlign, operand->ScriptPosition)
{ {
@ -3124,7 +3184,7 @@ FxExpression *FxCompareRel::Resolve(FCompileContext& ctx)
// //
//========================================================================== //==========================================================================
ExpEmit FxCompareRel::Emit(VMFunctionBuilder *build) ExpEmit FxCompareRel::EmitCommon(VMFunctionBuilder *build, bool forcompare, bool invert)
{ {
ExpEmit op1 = left->Emit(build); ExpEmit op1 = left->Emit(build);
ExpEmit op2 = right->Emit(build); ExpEmit op2 = right->Emit(build);
@ -3154,11 +3214,15 @@ ExpEmit FxCompareRel::Emit(VMFunctionBuilder *build)
{ {
op2.Free(build); op2.Free(build);
} }
if (invert) a ^= CMP_CHECK;
build->Emit(OP_LI, to.RegNum, 0, 0); if (!forcompare) build->Emit(OP_LI, to.RegNum, 0, 0);
build->Emit(OP_CMPS, a, op1.RegNum, op2.RegNum); build->Emit(OP_CMPS, a, op1.RegNum, op2.RegNum);
build->Emit(OP_JMP, 1); if (!forcompare)
build->Emit(OP_LI, to.RegNum, 1); {
build->Emit(OP_JMP, 1);
build->Emit(OP_LI, to.RegNum, 1);
}
return to; return to;
} }
else else
@ -3197,16 +3261,32 @@ ExpEmit FxCompareRel::Emit(VMFunctionBuilder *build)
{ {
op1.Free(build); op1.Free(build);
} }
if (invert) check ^= 1;
// See FxBoolCast for comments, since it's the same thing. // See FxBoolCast for comments, since it's the same thing.
build->Emit(OP_LI, to.RegNum, 0, 0); if (!forcompare) build->Emit(OP_LI, to.RegNum, 0, 0);
build->Emit(instr, check, op1.RegNum, op2.RegNum); build->Emit(instr, check, op1.RegNum, op2.RegNum);
build->Emit(OP_JMP, 1); if (!forcompare)
build->Emit(OP_LI, to.RegNum, 1); {
build->Emit(OP_JMP, 1);
build->Emit(OP_LI, to.RegNum, 1);
}
return to; return to;
} }
} }
ExpEmit FxCompareRel::Emit(VMFunctionBuilder *build)
{
return EmitCommon(build, false, false);
}
void FxCompareRel::EmitCompare(VMFunctionBuilder *build, bool invert, TArray<size_t> &patchspots_yes, TArray<size_t> &patchspots_no)
{
ExpEmit emit = EmitCommon(build, true, invert);
emit.Free(build);
patchspots_no.Push(build->Emit(OP_JMP, 0));
}
//========================================================================== //==========================================================================
// //
// //
@ -3404,7 +3484,7 @@ error:
// //
//========================================================================== //==========================================================================
ExpEmit FxCompareEq::Emit(VMFunctionBuilder *build) ExpEmit FxCompareEq::EmitCommon(VMFunctionBuilder *build, bool forcompare, bool invert)
{ {
ExpEmit op1 = left->Emit(build); ExpEmit op1 = left->Emit(build);
ExpEmit op2 = right->Emit(build); ExpEmit op2 = right->Emit(build);
@ -3418,13 +3498,17 @@ ExpEmit FxCompareEq::Emit(VMFunctionBuilder *build)
int a = Operator == TK_Eq ? CMP_EQ : int a = Operator == TK_Eq ? CMP_EQ :
Operator == TK_Neq ? CMP_EQ | CMP_CHECK : CMP_EQ | CMP_APPROX; Operator == TK_Neq ? CMP_EQ | CMP_CHECK : CMP_EQ | CMP_APPROX;
if (op1.Konst) a|= CMP_BK; if (op1.Konst) a |= CMP_BK;
if (op2.Konst) a |= CMP_CK; if (op2.Konst) a |= CMP_CK;
if (invert) a ^= CMP_CHECK;
build->Emit(OP_LI, to.RegNum, 0, 0); if (!forcompare) build->Emit(OP_LI, to.RegNum, 0, 0);
build->Emit(OP_CMPS, a, op1.RegNum, op2.RegNum); build->Emit(OP_CMPS, a, op1.RegNum, op2.RegNum);
build->Emit(OP_JMP, 1); if (!forcompare)
build->Emit(OP_LI, to.RegNum, 1); {
build->Emit(OP_JMP, 1);
build->Emit(OP_LI, to.RegNum, 1);
}
op1.Free(build); op1.Free(build);
op2.Free(build); op2.Free(build);
return to; return to;
@ -3457,14 +3541,29 @@ ExpEmit FxCompareEq::Emit(VMFunctionBuilder *build)
} }
// See FxUnaryNotBoolean for comments, since it's the same thing. // See FxUnaryNotBoolean for comments, since it's the same thing.
build->Emit(OP_LI, to.RegNum, 0, 0); if (!forcompare) build->Emit(OP_LI, to.RegNum, 0, 0);
build->Emit(instr, Operator == TK_ApproxEq ? CMP_APPROX : ((Operator != TK_Eq) ? CMP_CHECK : 0), op1.RegNum, op2.RegNum); build->Emit(instr, int(invert) ^ (Operator == TK_ApproxEq ? CMP_APPROX : ((Operator != TK_Eq) ? CMP_CHECK : 0)), op1.RegNum, op2.RegNum);
build->Emit(OP_JMP, 1); if (!forcompare)
build->Emit(OP_LI, to.RegNum, 1); {
build->Emit(OP_JMP, 1);
build->Emit(OP_LI, to.RegNum, 1);
}
return to; return to;
} }
} }
ExpEmit FxCompareEq::Emit(VMFunctionBuilder *build)
{
return EmitCommon(build, false, false);
}
void FxCompareEq::EmitCompare(VMFunctionBuilder *build, bool invert, TArray<size_t> &patchspots_yes, TArray<size_t> &patchspots_no)
{
ExpEmit emit = EmitCommon(build, true, invert);
emit.Free(build);
patchspots_no.Push(build->Emit(OP_JMP, 0));
}
//========================================================================== //==========================================================================
// //
// //
@ -4447,21 +4546,17 @@ FxExpression *FxConditional::Resolve(FCompileContext& ctx)
ExpEmit FxConditional::Emit(VMFunctionBuilder *build) ExpEmit FxConditional::Emit(VMFunctionBuilder *build)
{ {
size_t truejump, falsejump; size_t truejump;
ExpEmit out; ExpEmit out, falseout;
// The true and false expressions ought to be assigned to the // The true and false expressions ought to be assigned to the
// same temporary instead of being copied to it. Oh well; good enough // same temporary instead of being copied to it. Oh well; good enough
// for now. // for now.
ExpEmit cond = condition->Emit(build); TArray<size_t> yes, no;
assert(cond.RegType == REGT_INT && !cond.Konst); condition->EmitCompare(build, false, yes, no);
// Test condition. build->BackpatchListToHere(yes);
build->Emit(OP_EQ_K, 1, cond.RegNum, build->GetConstantInt(0));
falsejump = build->Emit(OP_JMP, 0);
cond.Free(build);
// Evaluate true expression.
if (truex->isConstant() && truex->ValueType->GetRegType() == REGT_INT) if (truex->isConstant() && truex->ValueType->GetRegType() == REGT_INT)
{ {
out = ExpEmit(build, REGT_INT); out = ExpEmit(build, REGT_INT);
@ -4501,7 +4596,7 @@ ExpEmit FxConditional::Emit(VMFunctionBuilder *build)
truejump = build->Emit(OP_JMP, 0); truejump = build->Emit(OP_JMP, 0);
// Evaluate false expression. // Evaluate false expression.
build->BackpatchToHere(falsejump); build->BackpatchListToHere(no);
if (falsex->isConstant() && falsex->ValueType->GetRegType() == REGT_INT) if (falsex->isConstant() && falsex->ValueType->GetRegType() == REGT_INT)
{ {
build->EmitLoadInt(out.RegNum, static_cast<FxConstant *>(falsex)->GetValue().GetInt()); build->EmitLoadInt(out.RegNum, static_cast<FxConstant *>(falsex)->GetValue().GetInt());
@ -8858,68 +8953,28 @@ FxExpression *FxIfStatement::Resolve(FCompileContext &ctx)
ExpEmit FxIfStatement::Emit(VMFunctionBuilder *build) ExpEmit FxIfStatement::Emit(VMFunctionBuilder *build)
{ {
ExpEmit v; ExpEmit v;
size_t jumpspot; size_t jumpspot = ~0u;
FxExpression *path1, *path2;
int condcheck;
// This is pretty much copied from FxConditional, except we don't TArray<size_t> yes, no;
// keep any results. Condition->EmitCompare(build, false, yes, no);
ExpEmit cond = Condition->Emit(build);
assert(cond.RegType != REGT_STRING && !cond.Konst);
if (WhenTrue != nullptr) if (WhenTrue != nullptr)
{ {
path1 = WhenTrue; build->BackpatchListToHere(yes);
path2 = WhenFalse; WhenTrue->Emit(build);
condcheck = 1; }
if (WhenFalse != nullptr)
{
if (!WhenTrue->CheckReturn()) jumpspot = build->Emit(OP_JMP, 0); // no need to emit a jump if the block returns.
build->BackpatchListToHere(no);
WhenFalse->Emit(build);
if (jumpspot != ~0u) build->BackpatchToHere(jumpspot);
if (WhenTrue == nullptr) build->BackpatchListToHere(yes);
} }
else else
{ {
// When there is only a false path, reverse the condition so we can build->BackpatchListToHere(no);
// treat it as a true path.
assert(WhenFalse != nullptr);
path1 = WhenFalse;
path2 = nullptr;
condcheck = 0;
} }
// Test condition.
switch (cond.RegType)
{
default:
case REGT_INT:
build->Emit(OP_EQ_K, condcheck, cond.RegNum, build->GetConstantInt(0));
break;
case REGT_FLOAT:
build->Emit(OP_EQF_K, condcheck, cond.RegNum, build->GetConstantFloat(0));
break;
case REGT_POINTER:
build->Emit(OP_EQA_K, condcheck, cond.RegNum, build->GetConstantAddress(nullptr, ATAG_GENERIC));
break;
}
jumpspot = build->Emit(OP_JMP, 0);
cond.Free(build);
// Evaluate first path
v = path1->Emit(build);
v.Free(build);
if (path2 != nullptr)
{
size_t path1jump;
// if the branch ends with a return we do not need a terminating jmp.
if (!path1->CheckReturn()) path1jump = build->Emit(OP_JMP, 0);
else path1jump = 0xffffffff;
// Evaluate second path
build->BackpatchToHere(jumpspot);
v = path2->Emit(build);
v.Free(build);
jumpspot = path1jump;
}
if (jumpspot != 0xffffffff) build->BackpatchToHere(jumpspot);
return ExpEmit(); return ExpEmit();
} }
@ -9027,19 +9082,17 @@ ExpEmit FxWhileLoop::Emit(VMFunctionBuilder *build)
assert(Condition->ValueType == TypeBool); assert(Condition->ValueType == TypeBool);
size_t loopstart, loopend; size_t loopstart, loopend;
size_t jumpspot; TArray<size_t> yes, no;
// Evaluate the condition and execute/break out of the loop. // Evaluate the condition and execute/break out of the loop.
loopstart = build->GetAddress(); loopstart = build->GetAddress();
if (!Condition->isConstant()) if (!Condition->isConstant())
{ {
ExpEmit cond = Condition->Emit(build); Condition->EmitCompare(build, false, yes, no);
build->Emit(OP_TEST, cond.RegNum, 0);
jumpspot = build->Emit(OP_JMP, 0);
cond.Free(build);
} }
else assert(static_cast<FxConstant *>(Condition)->GetValue().GetBool() == true); else assert(static_cast<FxConstant *>(Condition)->GetValue().GetBool() == true);
build->BackpatchListToHere(yes);
// Execute the loop's content. // Execute the loop's content.
if (Code != nullptr) if (Code != nullptr)
{ {
@ -9049,13 +9102,8 @@ ExpEmit FxWhileLoop::Emit(VMFunctionBuilder *build)
// Loop back. // Loop back.
build->Backpatch(build->Emit(OP_JMP, 0), loopstart); build->Backpatch(build->Emit(OP_JMP, 0), loopstart);
build->BackpatchListToHere(no);
loopend = build->GetAddress(); loopend = build->GetAddress();
if (!Condition->isConstant())
{
build->Backpatch(jumpspot, loopend);
}
Backpatch(build, loopstart, loopend); Backpatch(build, loopstart, loopend);
return ExpEmit(); return ExpEmit();
} }
@ -9132,17 +9180,16 @@ ExpEmit FxDoWhileLoop::Emit(VMFunctionBuilder *build)
loopstart = build->GetAddress(); loopstart = build->GetAddress();
if (!Condition->isConstant()) if (!Condition->isConstant())
{ {
ExpEmit cond = Condition->Emit(build); TArray<size_t> yes, no;
build->Emit(OP_TEST, cond.RegNum, 1); Condition->EmitCompare(build, true, yes, no);
cond.Free(build); build->BackpatchList(no, codestart);
build->Backpatch(build->Emit(OP_JMP, 0), codestart); build->BackpatchListToHere(yes);
} }
else if (static_cast<FxConstant *>(Condition)->GetValue().GetBool() == true) else if (static_cast<FxConstant *>(Condition)->GetValue().GetBool() == true)
{ // Always looping { // Always looping
build->Backpatch(build->Emit(OP_JMP, 0), codestart); build->Backpatch(build->Emit(OP_JMP, 0), codestart);
} }
loopend = build->GetAddress(); loopend = build->GetAddress();
Backpatch(build, loopstart, loopend); Backpatch(build, loopstart, loopend);
return ExpEmit(); return ExpEmit();

View File

@ -328,6 +328,7 @@ public:
bool IsObject() const { return ValueType->IsKindOf(RUNTIME_CLASS(PPointer)) && !ValueType->IsKindOf(RUNTIME_CLASS(PClassPointer)) && ValueType != TypeNullPtr && static_cast<PPointer*>(ValueType)->PointedType->IsKindOf(RUNTIME_CLASS(PClass)); } bool IsObject() const { return ValueType->IsKindOf(RUNTIME_CLASS(PPointer)) && !ValueType->IsKindOf(RUNTIME_CLASS(PClassPointer)) && ValueType != TypeNullPtr && static_cast<PPointer*>(ValueType)->PointedType->IsKindOf(RUNTIME_CLASS(PClass)); }
virtual ExpEmit Emit(VMFunctionBuilder *build); virtual ExpEmit Emit(VMFunctionBuilder *build);
virtual void EmitCompare(VMFunctionBuilder *build, bool invert, TArray<size_t> &patchspots_yes, TArray<size_t> &patchspots_no);
FScriptPosition ScriptPosition; FScriptPosition ScriptPosition;
PType *ValueType = nullptr; PType *ValueType = nullptr;
@ -565,6 +566,7 @@ public:
FxExpression *Resolve(FCompileContext&); FxExpression *Resolve(FCompileContext&);
ExpEmit Emit(VMFunctionBuilder *build); ExpEmit Emit(VMFunctionBuilder *build);
void EmitCompare(VMFunctionBuilder *build, bool invert, TArray<size_t> &patchspots_yes, TArray<size_t> &patchspots_no);
}; };
class FxIntCast : public FxExpression class FxIntCast : public FxExpression
@ -734,6 +736,7 @@ public:
~FxUnaryNotBoolean(); ~FxUnaryNotBoolean();
FxExpression *Resolve(FCompileContext&); FxExpression *Resolve(FCompileContext&);
ExpEmit Emit(VMFunctionBuilder *build); ExpEmit Emit(VMFunctionBuilder *build);
void EmitCompare(VMFunctionBuilder *build, bool invert, TArray<size_t> &patchspots_yes, TArray<size_t> &patchspots_no);
}; };
//========================================================================== //==========================================================================
@ -934,7 +937,9 @@ public:
FxCompareRel(int, FxExpression*, FxExpression*); FxCompareRel(int, FxExpression*, FxExpression*);
FxExpression *Resolve(FCompileContext&); FxExpression *Resolve(FCompileContext&);
ExpEmit EmitCommon(VMFunctionBuilder *build, bool forcompare, bool invert);
ExpEmit Emit(VMFunctionBuilder *build); ExpEmit Emit(VMFunctionBuilder *build);
void EmitCompare(VMFunctionBuilder *build, bool invert, TArray<size_t> &patchspots_yes, TArray<size_t> &patchspots_no);
}; };
//========================================================================== //==========================================================================
@ -949,7 +954,9 @@ public:
FxCompareEq(int, FxExpression*, FxExpression*); FxCompareEq(int, FxExpression*, FxExpression*);
FxExpression *Resolve(FCompileContext&); FxExpression *Resolve(FCompileContext&);
ExpEmit EmitCommon(VMFunctionBuilder *build, bool forcompare, bool invert);
ExpEmit Emit(VMFunctionBuilder *build); ExpEmit Emit(VMFunctionBuilder *build);
void EmitCompare(VMFunctionBuilder *build, bool invert, TArray<size_t> &patchspots_yes, TArray<size_t> &patchspots_no);
}; };
//========================================================================== //==========================================================================

View File

@ -734,6 +734,13 @@ void VMFunctionBuilder::Backpatch(size_t loc, size_t target)
Code[loc].i24 = offset; Code[loc].i24 = offset;
} }
void VMFunctionBuilder::BackpatchList(TArray<size_t> &locs, size_t target)
{
for (auto loc : locs)
Backpatch(loc, target);
}
//========================================================================== //==========================================================================
// //
// VMFunctionBuilder :: BackpatchToHere // VMFunctionBuilder :: BackpatchToHere
@ -748,6 +755,12 @@ void VMFunctionBuilder::BackpatchToHere(size_t loc)
Backpatch(loc, Code.Size()); Backpatch(loc, Code.Size());
} }
void VMFunctionBuilder::BackpatchListToHere(TArray<size_t> &locs)
{
for (auto loc : locs)
Backpatch(loc, Code.Size());
}
//========================================================================== //==========================================================================
// //
// FFunctionBuildList // FFunctionBuildList

View File

@ -69,6 +69,8 @@ public:
void Backpatch(size_t addr, size_t target); void Backpatch(size_t addr, size_t target);
void BackpatchToHere(size_t addr); 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. // Write out complete constant tables.
void FillIntConstants(int *konst); void FillIntConstants(int *konst);