Split double MoveOperands that conflict with floats.

This commit is contained in:
wolfbeast 2018-02-05 16:36:15 +01:00 • committed by Roy Tam
commit 27bb9f94c3
3 changed files with 203 additions and 1 deletions

View file

@ -106,12 +106,111 @@ MoveResolver::findCycledMove(PendingMoveIterator* iter, PendingMoveIterator end,
return nullptr;
}
#ifdef JS_CODEGEN_ARM
static inline bool
MoveIsDouble(const MoveOperand& move)
{
if (!move.isFloatReg())
return false;
return move.floatReg().isDouble();
}
#endif
#ifdef JS_CODEGEN_ARM
static inline bool
MoveIsSingle(const MoveOperand& move)
{
if (!move.isFloatReg())
return false;
return move.floatReg().isSingle();
}
#endif
#ifdef JS_CODEGEN_ARM
bool
MoveResolver::isDoubleAliasedAsSingle(const MoveOperand& move)
{
if (!MoveIsDouble(move))
return false;
for (auto iter = pending_.begin(); iter != pending_.end(); ++iter) {
PendingMove* other = *iter;
if (other->from().aliases(move) && MoveIsSingle(other->from()))
return true;
if (other->to().aliases(move) && MoveIsSingle(other->to()))
return true;
}
return false;
}
#endif
#ifdef JS_CODEGEN_ARM
static MoveOperand
SplitIntoLowerHalf(const MoveOperand& move)
{
if (MoveIsDouble(move)) {
FloatRegister lowerSingle = move.floatReg().asSingle();
return MoveOperand(lowerSingle);
}
MOZ_ASSERT(move.isMemoryOrEffectiveAddress());
return move;
}
#endif
#ifdef JS_CODEGEN_ARM
static MoveOperand
SplitIntoUpperHalf(const MoveOperand& move)
{
if (MoveIsDouble(move)) {
FloatRegister lowerSingle = move.floatReg().asSingle();
FloatRegister upperSingle = VFPRegister(lowerSingle.code() + 1, VFPRegister::Single);
return MoveOperand(upperSingle);
}
MOZ_ASSERT(move.isMemoryOrEffectiveAddress());
return MoveOperand(move.base(), move.disp() + sizeof(float));
}
#endif
bool
MoveResolver::resolve()
{
resetState();
orderedMoves_.clear();
#ifdef JS_CODEGEN_ARM
// Some of ARM's double registers alias two of its single registers,
// but the algorithm below assumes that every register can participate
// in at most one cycle. To satisfy the algorithm, any double registers
// that may conflict are split into their single-register halves.
//
// This logic is only applicable because ARM only uses registers d0-d15,
// all of which alias s0-s31. Double registers d16-d31 are unused.
// Therefore there is never a double move that cannot be split.
// If this changes in the future, the algorithm will have to be fixed.
for (auto iter = pending_.begin(); iter != pending_.end(); ++iter) {
PendingMove* pm = *iter;
if (isDoubleAliasedAsSingle(pm->from()) || isDoubleAliasedAsSingle(pm->to())) {
PendingMove* lower = movePool_.allocate();
if (!lower)
return false;
// Insert the new node before the current position to not affect iteration.
MoveOperand fromLower = SplitIntoLowerHalf(pm->from());
MoveOperand toLower = SplitIntoLowerHalf(pm->to());
new (lower) PendingMove(fromLower, toLower, MoveOp::FLOAT32);
pending_.insertBefore(pm, lower);
// Overwrite pm in place for the upper move. Iteration proceeds as normal.
MoveOperand fromUpper = SplitIntoUpperHalf(pm->from());
MoveOperand toUpper = SplitIntoUpperHalf(pm->to());
pm->overwrite(fromUpper, toUpper, MoveOp::FLOAT32);
}
}
#endif
InlineList<PendingMove> stack;
// This is a depth-first-search without recursion, which tries to find

View file

@ -252,6 +252,13 @@ class MoveOp
bool aliases(const MoveOp& other) const {
return aliases(other.from()) || aliases(other.to());
}
#ifdef JS_CODEGEN_ARM
void overwrite(MoveOperand& from, MoveOperand& to, Type type) {
from_ = from;
to_ = to;
type_ = type;
}
#endif
};
class MoveResolver
@ -299,6 +306,10 @@ class MoveResolver
// Internal reset function. Does not clear lists.
void resetState();
#ifdef JS_CODEGEN_ARM
bool isDoubleAliasedAsSingle(const MoveOperand& move);
#endif
public:
MoveResolver();

View file

@ -6,6 +6,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#if defined(JS_SIMULATOR_ARM)
#include "jit/arm/Assembler-arm.h"
#include "jit/arm/MoveEmitter-arm.h"
#include "jit/arm/Simulator-arm.h"
@ -528,5 +529,96 @@ BEGIN_TEST(testJitMoveEmitterCycles_autogen3)
return true;
}
END_TEST(testJitMoveEmitterCycles_autogen3)
BEGIN_TEST(testJitMoveEmitterCycles_bug1299147_1)
{
using namespace js;
using namespace js::jit;
LifoAlloc lifo(LIFO_ALLOC_PRIMARY_CHUNK_SIZE);
TempAllocator alloc(&lifo);
JitContext jc(cx, &alloc);
cx->runtime()->getJitRuntime(cx);
MacroAssembler masm;
MoveEmitter mover(masm);
MoveResolver mr;
mr.setAllocator(alloc);
Simulator* sim = Simulator::Current();
// S2 -> S0
// S2 -> S6
// S3 -> S1
// S3 -> S7
// D0 -> D1
// D0 -> D2
TRY(mr.addMove(MoveOperand(s2), MoveOperand(s0), MoveOp::FLOAT32));
TRY(mr.addMove(MoveOperand(s2), MoveOperand(s6), MoveOp::FLOAT32));
sim->set_s_register_from_float(2, 2);
TRY(mr.addMove(MoveOperand(s3), MoveOperand(s1), MoveOp::FLOAT32));
TRY(mr.addMove(MoveOperand(s3), MoveOperand(s7), MoveOp::FLOAT32));
sim->set_s_register_from_float(3, 4);
TRY(mr.addMove(MoveOperand(d0), MoveOperand(d1), MoveOp::FLOAT32));
TRY(mr.addMove(MoveOperand(d0), MoveOperand(d2), MoveOp::FLOAT32));
sim->set_d_register_from_double(0, 1);
// don't explode!
TRY(mr.resolve());
mover.emit(mr);
mover.finish();
masm.abiret();
JitCode* code = linkAndAllocate(cx, &masm);
sim->call(code->raw(), 1, 1);
float f;
double d;
sim->get_double_from_d_register(1, &d);
CHECK(d == 1);
sim->get_double_from_d_register(2, &d);
CHECK(d == 1);
sim->get_float_from_s_register(0, &f);
CHECK(int(f) == 2);
sim->get_float_from_s_register(6, &f);
CHECK(int(f) == 2);
sim->get_float_from_s_register(1, &f);
CHECK(int(f) == 4);
sim->get_float_from_s_register(7, &f);
CHECK(int(f) == 4);
return true;
}
END_TEST(testJitMoveEmitterCycles_bug1299147_1)
BEGIN_TEST(testJitMoveEmitterCycles_bug1299147)
{
using namespace js;
using namespace js::jit;
LifoAlloc lifo(LIFO_ALLOC_PRIMARY_CHUNK_SIZE);
TempAllocator alloc(&lifo);
JitContext jc(cx, &alloc);
cx->runtime()->getJitRuntime(cx);
MacroAssembler masm;
MoveEmitter mover(masm);
MoveResolver mr;
mr.setAllocator(alloc);
Simulator* sim = Simulator::Current();
// S2 -> S5
// S2 -> S6
// D0 -> D1
TRY(mr.addMove(MoveOperand(s2), MoveOperand(s5), MoveOp::FLOAT32));
TRY(mr.addMove(MoveOperand(s2), MoveOperand(s6), MoveOp::FLOAT32));
sim->set_s_register_from_float(2, 2);
TRY(mr.addMove(MoveOperand(d0), MoveOperand(d1), MoveOp::FLOAT32));
sim->set_d_register_from_double(0, 1);
// don't explode!
TRY(mr.resolve());
mover.emit(mr);
mover.finish();
masm.abiret();
JitCode* code = linkAndAllocate(cx, &masm);
sim->call(code->raw(), 1, 1);
float f;
double d;
sim->get_double_from_d_register(1, &d);
CHECK(d == 1);
sim->get_float_from_s_register(5, &f);
CHECK(int(f) == 2);
sim->get_float_from_s_register(6, &f);
CHECK(int(f) == 2);
return true;
}
END_TEST(testJitMoveEmitterCycles_bug1299147)
#endif
#endif // JS_SIMULATOR_ARM