blob: 93e7244f054e4ae368e29f10dd16cb9325659a43 [file] [log] [blame]
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/compiler/backend/instruction.h"
#include "src/codegen/register-configuration.h"
#include "test/unittests/test-utils.h"
#include "testing/gtest-support.h"
namespace v8 {
namespace internal {
namespace compiler {
namespace instruction_unittest {
namespace {
const MachineRepresentation kWord = MachineRepresentation::kWord32;
const MachineRepresentation kFloat = MachineRepresentation::kFloat32;
const MachineRepresentation kDouble = MachineRepresentation::kFloat64;
bool Interfere(LocationOperand::LocationKind kind, MachineRepresentation rep1,
int index1, MachineRepresentation rep2, int index2) {
return AllocatedOperand(kind, rep1, index1)
.InterferesWith(AllocatedOperand(kind, rep2, index2));
}
bool Contains(const ZoneVector<MoveOperands*>* moves,
const InstructionOperand& to, const InstructionOperand& from) {
for (auto move : *moves) {
if (move->destination().Equals(to) && move->source().Equals(from)) {
return true;
}
}
return false;
}
} // namespace
class InstructionTest : public TestWithZone {
public:
InstructionTest() = default;
~InstructionTest() override = default;
ParallelMove* CreateParallelMove(
const std::vector<InstructionOperand>& operand_pairs) {
ParallelMove* parallel_move = new (zone()) ParallelMove(zone());
for (size_t i = 0; i < operand_pairs.size(); i += 2)
parallel_move->AddMove(operand_pairs[i + 1], operand_pairs[i]);
return parallel_move;
}
};
TEST_F(InstructionTest, OperandInterference) {
// All general registers and slots interfere only with themselves.
for (int i = 0; i < RegisterConfiguration::kMaxGeneralRegisters; ++i) {
EXPECT_TRUE(Interfere(LocationOperand::REGISTER, kWord, i, kWord, i));
EXPECT_TRUE(Interfere(LocationOperand::STACK_SLOT, kWord, i, kWord, i));
for (int j = i + 1; j < RegisterConfiguration::kMaxGeneralRegisters; ++j) {
EXPECT_FALSE(Interfere(LocationOperand::REGISTER, kWord, i, kWord, j));
EXPECT_FALSE(Interfere(LocationOperand::STACK_SLOT, kWord, i, kWord, j));
}
}
// All FP registers interfere with themselves.
for (int i = 0; i < RegisterConfiguration::kMaxFPRegisters; ++i) {
EXPECT_TRUE(Interfere(LocationOperand::REGISTER, kFloat, i, kFloat, i));
EXPECT_TRUE(Interfere(LocationOperand::STACK_SLOT, kFloat, i, kFloat, i));
EXPECT_TRUE(Interfere(LocationOperand::REGISTER, kDouble, i, kDouble, i));
EXPECT_TRUE(Interfere(LocationOperand::STACK_SLOT, kDouble, i, kDouble, i));
}
if (kSimpleFPAliasing) {
// Simple FP aliasing: interfering registers of different reps have the same
// index.
for (int i = 0; i < RegisterConfiguration::kMaxFPRegisters; ++i) {
EXPECT_TRUE(Interfere(LocationOperand::REGISTER, kFloat, i, kDouble, i));
EXPECT_TRUE(Interfere(LocationOperand::REGISTER, kDouble, i, kFloat, i));
for (int j = i + 1; j < RegisterConfiguration::kMaxFPRegisters; ++j) {
EXPECT_FALSE(Interfere(LocationOperand::REGISTER, kWord, i, kWord, j));
EXPECT_FALSE(
Interfere(LocationOperand::STACK_SLOT, kWord, i, kWord, j));
}
}
} else {
// Complex FP aliasing: sub-registers intefere with containing registers.
// Test sub-register indices which may not exist on the platform. This is
// necessary since the GapResolver may split large moves into smaller ones.
for (int i = 0; i < RegisterConfiguration::kMaxFPRegisters; ++i) {
EXPECT_TRUE(
Interfere(LocationOperand::REGISTER, kFloat, i * 2, kDouble, i));
EXPECT_TRUE(
Interfere(LocationOperand::REGISTER, kFloat, i * 2 + 1, kDouble, i));
EXPECT_TRUE(
Interfere(LocationOperand::REGISTER, kDouble, i, kFloat, i * 2));
EXPECT_TRUE(
Interfere(LocationOperand::REGISTER, kDouble, i, kFloat, i * 2 + 1));
for (int j = i + 1; j < RegisterConfiguration::kMaxFPRegisters; ++j) {
EXPECT_FALSE(
Interfere(LocationOperand::REGISTER, kFloat, i * 2, kDouble, j));
EXPECT_FALSE(Interfere(LocationOperand::REGISTER, kFloat, i * 2 + 1,
kDouble, j));
EXPECT_FALSE(
Interfere(LocationOperand::REGISTER, kDouble, i, kFloat, j * 2));
EXPECT_FALSE(Interfere(LocationOperand::REGISTER, kDouble, i, kFloat,
j * 2 + 1));
}
}
}
}
TEST_F(InstructionTest, PrepareInsertAfter) {
InstructionOperand r0 = AllocatedOperand(LocationOperand::REGISTER,
MachineRepresentation::kWord32, 0);
InstructionOperand r1 = AllocatedOperand(LocationOperand::REGISTER,
MachineRepresentation::kWord32, 1);
InstructionOperand r2 = AllocatedOperand(LocationOperand::REGISTER,
MachineRepresentation::kWord32, 2);
InstructionOperand d0 = AllocatedOperand(LocationOperand::REGISTER,
MachineRepresentation::kFloat64, 0);
InstructionOperand d1 = AllocatedOperand(LocationOperand::REGISTER,
MachineRepresentation::kFloat64, 1);
InstructionOperand d2 = AllocatedOperand(LocationOperand::REGISTER,
MachineRepresentation::kFloat64, 2);
{
// Moves inserted after should pick up assignments to their sources.
// Moves inserted after should cause interfering moves to be eliminated.
ZoneVector<MoveOperands*> to_eliminate(zone());
std::vector<InstructionOperand> moves = {
r1, r0, // r1 <- r0
r2, r0, // r2 <- r0
d1, d0, // d1 <- d0
d2, d0 // d2 <- d0
};
ParallelMove* pm = CreateParallelMove(moves);
MoveOperands m1(r1, r2); // r2 <- r1
pm->PrepareInsertAfter(&m1, &to_eliminate);
CHECK(m1.source().Equals(r0));
CHECK(Contains(&to_eliminate, r2, r0));
MoveOperands m2(d1, d2); // d2 <- d1
pm->PrepareInsertAfter(&m2, &to_eliminate);
CHECK(m2.source().Equals(d0));
CHECK(Contains(&to_eliminate, d2, d0));
}
if (!kSimpleFPAliasing) {
// Moves inserted after should cause all interfering moves to be eliminated.
auto s0 = AllocatedOperand(LocationOperand::REGISTER,
MachineRepresentation::kFloat32, 0);
auto s1 = AllocatedOperand(LocationOperand::REGISTER,
MachineRepresentation::kFloat32, 1);
auto s2 = AllocatedOperand(LocationOperand::REGISTER,
MachineRepresentation::kFloat32, 2);
{
ZoneVector<MoveOperands*> to_eliminate(zone());
std::vector<InstructionOperand> moves = {
s0, s2, // s0 <- s2
s1, s2 // s1 <- s2
};
ParallelMove* pm = CreateParallelMove(moves);
MoveOperands m1(d1, d0); // d0 <- d1
pm->PrepareInsertAfter(&m1, &to_eliminate);
CHECK(Contains(&to_eliminate, s0, s2));
CHECK(Contains(&to_eliminate, s1, s2));
}
}
}
} // namespace instruction_unittest
} // namespace compiler
} // namespace internal
} // namespace v8