v8/test/cctest/compiler/test-gap-resolver.cc
bbudge 257336d26a [RegisterConfiguration] Streamline access to arch defaults, simplify Registers.
Replaces ArchDefault method with Crankshaft and Turbofan getters.
Eliminates IsAllocated method on Register, FloatRegister, DoubleRegister.
Eliminates ToString method too.
Changes call sites to access appropriate arch default RegisterConfiguration.

LOG=N
BUG=

Review-Url: https://codereview.chromium.org/2092413002
Cr-Commit-Position: refs/heads/master@{#37297}
2016-06-27 15:31:31 +00:00

263 lines
7.8 KiB
C++

// Copyright 2014 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/gap-resolver.h"
#include "src/base/utils/random-number-generator.h"
#include "test/cctest/cctest.h"
namespace v8 {
namespace internal {
namespace compiler {
// The state of our move interpreter is the mapping of operands to values. Note
// that the actual values don't really matter, all we care about is equality.
class InterpreterState {
public:
void ExecuteInParallel(const ParallelMove* moves) {
InterpreterState copy(*this);
for (const auto m : *moves) {
if (!m->IsRedundant()) write(m->destination(), copy.read(m->source()));
}
}
bool operator==(const InterpreterState& other) const {
return values_ == other.values_;
}
bool operator!=(const InterpreterState& other) const {
return values_ != other.values_;
}
private:
struct Key {
bool is_constant;
bool is_float;
LocationOperand::LocationKind kind;
int index;
bool operator<(const Key& other) const {
if (this->is_constant != other.is_constant) {
return this->is_constant;
}
if (this->is_float != other.is_float) {
return this->is_float;
}
if (this->kind != other.kind) {
return this->kind < other.kind;
}
return this->index < other.index;
}
bool operator==(const Key& other) const {
return this->is_constant == other.is_constant &&
this->kind == other.kind && this->index == other.index;
}
};
// Internally, the state is a normalized permutation of (kind,index) pairs.
typedef Key Value;
typedef std::map<Key, Value> OperandMap;
Value read(const InstructionOperand& op) const {
OperandMap::const_iterator it = values_.find(KeyFor(op));
return (it == values_.end()) ? ValueFor(op) : it->second;
}
void write(const InstructionOperand& op, Value v) {
if (v == ValueFor(op)) {
values_.erase(KeyFor(op));
} else {
values_[KeyFor(op)] = v;
}
}
static Key KeyFor(const InstructionOperand& op) {
bool is_constant = op.IsConstant();
bool is_float = false;
LocationOperand::LocationKind kind;
int index;
if (!is_constant) {
if (op.IsRegister()) {
index = LocationOperand::cast(op).GetRegister().code();
} else if (op.IsFPRegister()) {
index = LocationOperand::cast(op).GetDoubleRegister().code();
} else {
index = LocationOperand::cast(op).index();
}
is_float = IsFloatingPoint(LocationOperand::cast(op).representation());
kind = LocationOperand::cast(op).location_kind();
} else {
index = ConstantOperand::cast(op).virtual_register();
kind = LocationOperand::REGISTER;
}
Key key = {is_constant, is_float, kind, index};
return key;
}
static Value ValueFor(const InstructionOperand& op) { return KeyFor(op); }
static InstructionOperand FromKey(Key key) {
if (key.is_constant) {
return ConstantOperand(key.index);
}
return AllocatedOperand(
key.kind,
v8::internal::compiler::InstructionSequence::DefaultRepresentation(),
key.index);
}
friend std::ostream& operator<<(std::ostream& os,
const InterpreterState& is) {
for (OperandMap::const_iterator it = is.values_.begin();
it != is.values_.end(); ++it) {
if (it != is.values_.begin()) os << " ";
InstructionOperand source = FromKey(it->first);
InstructionOperand destination = FromKey(it->second);
MoveOperands mo(source, destination);
PrintableMoveOperands pmo = {RegisterConfiguration::Turbofan(), &mo};
os << pmo;
}
return os;
}
OperandMap values_;
};
// An abstract interpreter for moves, swaps and parallel moves.
class MoveInterpreter : public GapResolver::Assembler {
public:
explicit MoveInterpreter(Zone* zone) : zone_(zone) {}
void AssembleMove(InstructionOperand* source,
InstructionOperand* destination) override {
ParallelMove* moves = new (zone_) ParallelMove(zone_);
moves->AddMove(*source, *destination);
state_.ExecuteInParallel(moves);
}
void AssembleSwap(InstructionOperand* source,
InstructionOperand* destination) override {
ParallelMove* moves = new (zone_) ParallelMove(zone_);
moves->AddMove(*source, *destination);
moves->AddMove(*destination, *source);
state_.ExecuteInParallel(moves);
}
void AssembleParallelMove(const ParallelMove* moves) {
state_.ExecuteInParallel(moves);
}
InterpreterState state() const { return state_; }
private:
Zone* const zone_;
InterpreterState state_;
};
class ParallelMoveCreator : public HandleAndZoneScope {
public:
ParallelMoveCreator() : rng_(CcTest::random_number_generator()) {}
ParallelMove* Create(int size) {
ParallelMove* parallel_move = new (main_zone()) ParallelMove(main_zone());
std::set<InstructionOperand, CompareOperandModuloType> seen;
MachineRepresentation rep = RandomRepresentation();
for (int i = 0; i < size; ++i) {
MoveOperands mo(CreateRandomOperand(true, rep),
CreateRandomOperand(false, rep));
if (!mo.IsRedundant() && seen.find(mo.destination()) == seen.end()) {
parallel_move->AddMove(mo.source(), mo.destination());
seen.insert(mo.destination());
}
}
return parallel_move;
}
private:
MachineRepresentation RandomRepresentation() {
int index = rng_->NextInt(5);
switch (index) {
case 0:
return MachineRepresentation::kWord32;
case 1:
return MachineRepresentation::kWord64;
case 2:
return MachineRepresentation::kFloat32;
case 3:
return MachineRepresentation::kFloat64;
case 4:
return MachineRepresentation::kTagged;
}
UNREACHABLE();
return MachineRepresentation::kNone;
}
InstructionOperand CreateRandomOperand(bool is_source,
MachineRepresentation rep) {
auto conf = RegisterConfiguration::Turbofan();
auto GetRegisterCode = [&conf](MachineRepresentation rep, int index) {
switch (rep) {
case MachineRepresentation::kFloat32:
case MachineRepresentation::kFloat64:
return conf->RegisterConfiguration::GetAllocatableDoubleCode(index);
break;
default:
return conf->RegisterConfiguration::GetAllocatableGeneralCode(index);
break;
}
UNREACHABLE();
return static_cast<int>(Register::kCode_no_reg);
};
int index = rng_->NextInt(7);
// destination can't be Constant.
switch (rng_->NextInt(is_source ? 5 : 4)) {
case 0:
return AllocatedOperand(LocationOperand::STACK_SLOT, rep, index);
case 1:
return AllocatedOperand(LocationOperand::REGISTER, rep, index);
case 2:
return ExplicitOperand(LocationOperand::REGISTER, rep,
GetRegisterCode(rep, 1));
case 3:
return ExplicitOperand(LocationOperand::STACK_SLOT, rep,
GetRegisterCode(rep, index));
case 4:
return ConstantOperand(index);
}
UNREACHABLE();
return InstructionOperand();
}
private:
v8::base::RandomNumberGenerator* rng_;
};
TEST(FuzzResolver) {
ParallelMoveCreator pmc;
for (int size = 0; size < 20; ++size) {
for (int repeat = 0; repeat < 50; ++repeat) {
ParallelMove* pm = pmc.Create(size);
// Note: The gap resolver modifies the ParallelMove, so interpret first.
MoveInterpreter mi1(pmc.main_zone());
mi1.AssembleParallelMove(pm);
MoveInterpreter mi2(pmc.main_zone());
GapResolver resolver(&mi2);
resolver.Resolve(pm);
CHECK(mi1.state() == mi2.state());
}
}
}
} // namespace compiler
} // namespace internal
} // namespace v8