diff options
-rw-r--r-- | js/src/jit/MoveResolver.cpp | 99 | ||||
-rw-r--r-- | js/src/jit/MoveResolver.h | 11 | ||||
-rw-r--r-- | js/src/jsapi-tests/testJitMoveEmitterCycles.cpp | 94 |
3 files changed, 203 insertions, 1 deletions
diff --git a/js/src/jit/MoveResolver.cpp b/js/src/jit/MoveResolver.cpp index 5fd6c7bd5..383b45073 100644 --- a/js/src/jit/MoveResolver.cpp +++ b/js/src/jit/MoveResolver.cpp @@ -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 diff --git a/js/src/jit/MoveResolver.h b/js/src/jit/MoveResolver.h index fad2ba9e3..db045cfcf 100644 --- a/js/src/jit/MoveResolver.h +++ b/js/src/jit/MoveResolver.h @@ -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(); diff --git a/js/src/jsapi-tests/testJitMoveEmitterCycles.cpp b/js/src/jsapi-tests/testJitMoveEmitterCycles.cpp index 416587293..c1c2baddd 100644 --- a/js/src/jsapi-tests/testJitMoveEmitterCycles.cpp +++ b/js/src/jsapi-tests/testJitMoveEmitterCycles.cpp @@ -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 |