/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- * vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "jit/RegisterSets.h" #include "jsapi-tests/tests.h" using namespace js; using namespace js::jit; static bool CoPrime(size_t a, size_t b) { if (b <= 1) { return a == 1 || b == 1; } return CoPrime(b, a % b); } // This macros are use to iterave over all registers in a large number of // non-looping sequences, which does not rely on the getFirst / getLast // functions. #define BEGIN_INDEX_WALK(RegTotal) \ static const size_t Total = RegTotal; \ for (size_t walk = 1; walk < RegTotal; walk += 2) { \ if (!CoPrime(RegTotal, walk)) continue; \ for (size_t start = 0; start < RegTotal; start++) { \ size_t index = start; #define END_INDEX_WALK \ } \ } #define BEGIN_All_WALK(RegTotal) \ static const size_t Total = RegTotal; \ size_t walk = 1; \ size_t start = 0; \ size_t index = start; #define FOR_ALL_REGISTERS(Register, reg) \ do { \ Register reg = Register::FromCode(index); #define END_FOR_ALL_REGISTERS \ index = (index + walk) % Total; \ } \ while (index != start) BEGIN_TEST(testJitRegisterSet_GPR) { BEGIN_INDEX_WALK(Registers::Total) LiveGeneralRegisterSet liveRegs; AllocatableGeneralRegisterSet pool(GeneralRegisterSet::All()); CHECK(liveRegs.empty()); CHECK(pool.set() == GeneralRegisterSet::All()); FOR_ALL_REGISTERS(Register, reg) { CHECK(!pool.has(reg) || !liveRegs.has(reg)); if (pool.has(reg)) { CHECK(!liveRegs.has(reg)); pool.take(reg); liveRegs.add(reg); CHECK(liveRegs.has(reg)); CHECK(!pool.has(reg)); } CHECK(!pool.has(reg) || !liveRegs.has(reg)); } END_FOR_ALL_REGISTERS; CHECK(pool.empty()); FOR_ALL_REGISTERS(Register, reg) { CHECK(!pool.has(reg) || !liveRegs.has(reg)); if (liveRegs.has(reg)) { CHECK(!pool.has(reg)); liveRegs.take(reg); pool.add(reg); CHECK(pool.has(reg)); CHECK(!liveRegs.has(reg)); } CHECK(!pool.has(reg) || !liveRegs.has(reg)); } END_FOR_ALL_REGISTERS; CHECK(liveRegs.empty()); CHECK(pool.set() == GeneralRegisterSet::All()); END_INDEX_WALK return true; } END_TEST(testJitRegisterSet_GPR) BEGIN_TEST(testJitRegisterSet_FPU) { BEGIN_INDEX_WALK(FloatRegisters::Total) LiveFloatRegisterSet liveRegs; AllocatableFloatRegisterSet pool(FloatRegisterSet::All()); CHECK(liveRegs.empty()); CHECK(pool.set() == FloatRegisterSet::All()); FOR_ALL_REGISTERS(FloatRegister, reg) { CHECK(!pool.has(reg) || !liveRegs.has(reg)); if (pool.has(reg)) { CHECK(!liveRegs.has(reg)); pool.take(reg); liveRegs.add(reg); CHECK(liveRegs.has(reg)); CHECK(!pool.has(reg)); } CHECK(!pool.has(reg) || !liveRegs.has(reg)); } END_FOR_ALL_REGISTERS; CHECK(pool.empty()); FOR_ALL_REGISTERS(FloatRegister, reg) { CHECK(!pool.has(reg) || !liveRegs.has(reg)); if (liveRegs.has(reg)) { CHECK(!pool.has(reg)); liveRegs.take(reg); pool.add(reg); CHECK(pool.has(reg)); CHECK(!liveRegs.has(reg)); } CHECK(!pool.has(reg) || !liveRegs.has(reg)); } END_FOR_ALL_REGISTERS; CHECK(liveRegs.empty()); CHECK(pool.set() == FloatRegisterSet::All()); END_INDEX_WALK return true; } END_TEST(testJitRegisterSet_FPU) void pullAllFpus(AllocatableFloatRegisterSet& set, uint32_t& max_bits, uint32_t bits) { FloatRegisterSet allocSet(set.bits()); FloatRegisterSet available_f32( allocSet.allAllocatable()); FloatRegisterSet available_f64( allocSet.allAllocatable()); FloatRegisterSet available_v128( allocSet.allAllocatable()); for (FloatRegisterIterator it(available_f32); it.more(); ++it) { FloatRegister tmp = *it; set.take(tmp); pullAllFpus(set, max_bits, bits + 32); set.add(tmp); } for (FloatRegisterIterator it(available_f64); it.more(); ++it) { FloatRegister tmp = *it; set.take(tmp); pullAllFpus(set, max_bits, bits + 64); set.add(tmp); } for (FloatRegisterIterator it(available_v128); it.more(); ++it) { FloatRegister tmp = *it; set.take(tmp); pullAllFpus(set, max_bits, bits + 128); set.add(tmp); } if (bits >= max_bits) { max_bits = bits; } } BEGIN_TEST(testJitRegisterSet_FPU_Aliases) { BEGIN_All_WALK(FloatRegisters::Total); FOR_ALL_REGISTERS(FloatRegister, reg) { AllocatableFloatRegisterSet pool; pool.add(reg); uint32_t alias_bits = 0; for (uint32_t i = 0; i < reg.numAlignedAliased(); i++) { FloatRegister alias = reg.alignedAliased(i); if (alias.isSingle()) { if (alias_bits <= 32) { alias_bits = 32; } } else if (alias.isDouble()) { if (alias_bits <= 64) { alias_bits = 64; } } else if (alias.isSimd128()) { if (alias_bits <= 128) { alias_bits = 128; } } } uint32_t max_bits = 0; pullAllFpus(pool, max_bits, 0); // By adding one register, we expect that we should not be able to pull // more than any of its aligned aliases. This rule should hold for both // x64 and ARM. CHECK(max_bits <= alias_bits); // We added one register, we expect to be able to pull it back. CHECK(max_bits > 0); } END_FOR_ALL_REGISTERS; return true; } END_TEST(testJitRegisterSet_FPU_Aliases)