2014-07-30 13:54:45 +00:00
|
|
|
// 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"
|
|
|
|
|
2015-10-30 09:16:26 +00:00
|
|
|
namespace v8 {
|
|
|
|
namespace internal {
|
|
|
|
namespace compiler {
|
2014-07-30 13:54:45 +00:00
|
|
|
|
|
|
|
// 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:
|
2015-04-15 12:36:36 +00:00
|
|
|
void ExecuteInParallel(const ParallelMove* moves) {
|
2014-07-30 13:54:45 +00:00
|
|
|
InterpreterState copy(*this);
|
2015-04-15 12:36:36 +00:00
|
|
|
for (const auto m : *moves) {
|
|
|
|
if (!m->IsRedundant()) write(m->destination(), copy.read(m->source()));
|
2014-07-30 13:54:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool operator==(const InterpreterState& other) const {
|
|
|
|
return values_ == other.values_;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool operator!=(const InterpreterState& other) const {
|
|
|
|
return values_ != other.values_;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2015-04-09 10:40:41 +00:00
|
|
|
struct Key {
|
|
|
|
bool is_constant;
|
2016-07-20 19:05:48 +00:00
|
|
|
MachineRepresentation rep;
|
[turbofan] Create ExplicitOperands to specify operands without virtual registers
Up until now, if one wanted to specify an explicit stack location or register as an operand for an instruction, it had to also be
explicitly associated with a virtual register as a so-called
FixedRegister or FixedStackSlot.
For the implementation of tail calls, the plan is to use the gap
resolver needs to shuffle stack locations from the caller to the
tail-called callee. In order to do this, it must be possible to
explicitly address operand locations on the stack that are not
associated with virtual registers.
This CL introduces ExplictOperands, which can specify a specific
register or stack location that is not associated with virtual
register. This will allow tail calls to specify the target
locations for the necessary stack moves in the gap for the tail
call without the core register allocation having to know about
the target of the stack moves at all.
In the process this CL:
* creates a new Operand kind, ExplicitOperand, with which
instructions can specify register and stack slots without an
associated virtual register.
* creates a LocationOperand class from which AllocatedOperand and
ExplicitOperand are derived and provides a common interface to
get Register, DoubleRegister and spill slot information.
* removes RegisterOperand, DoubleRegisterOperand,
StackSlotOperand and DoubleStackSlotOperand, they are subsumed
by LocationOperand.
* addresses a cleanup TODO in AllocatedOperand to reduce the
redundancy of AllocatedOperand::Kind by using machine_type() to
determine if an operand corresponds to a general purpose or
double register.
BUG=v8:4076
LOG=n
Review URL: https://codereview.chromium.org/1389373002
Cr-Commit-Position: refs/heads/master@{#31603}
2015-10-27 13:26:35 +00:00
|
|
|
LocationOperand::LocationKind kind;
|
2015-04-09 10:40:41 +00:00
|
|
|
int index;
|
|
|
|
|
|
|
|
bool operator<(const Key& other) const {
|
|
|
|
if (this->is_constant != other.is_constant) {
|
|
|
|
return this->is_constant;
|
|
|
|
}
|
2016-07-20 19:05:48 +00:00
|
|
|
if (this->rep != other.rep) {
|
|
|
|
return static_cast<int>(this->rep) < static_cast<int>(other.rep);
|
[turbofan] Create ExplicitOperands to specify operands without virtual registers
Up until now, if one wanted to specify an explicit stack location or register as an operand for an instruction, it had to also be
explicitly associated with a virtual register as a so-called
FixedRegister or FixedStackSlot.
For the implementation of tail calls, the plan is to use the gap
resolver needs to shuffle stack locations from the caller to the
tail-called callee. In order to do this, it must be possible to
explicitly address operand locations on the stack that are not
associated with virtual registers.
This CL introduces ExplictOperands, which can specify a specific
register or stack location that is not associated with virtual
register. This will allow tail calls to specify the target
locations for the necessary stack moves in the gap for the tail
call without the core register allocation having to know about
the target of the stack moves at all.
In the process this CL:
* creates a new Operand kind, ExplicitOperand, with which
instructions can specify register and stack slots without an
associated virtual register.
* creates a LocationOperand class from which AllocatedOperand and
ExplicitOperand are derived and provides a common interface to
get Register, DoubleRegister and spill slot information.
* removes RegisterOperand, DoubleRegisterOperand,
StackSlotOperand and DoubleStackSlotOperand, they are subsumed
by LocationOperand.
* addresses a cleanup TODO in AllocatedOperand to reduce the
redundancy of AllocatedOperand::Kind by using machine_type() to
determine if an operand corresponds to a general purpose or
double register.
BUG=v8:4076
LOG=n
Review URL: https://codereview.chromium.org/1389373002
Cr-Commit-Position: refs/heads/master@{#31603}
2015-10-27 13:26:35 +00:00
|
|
|
}
|
2015-04-09 10:40:41 +00:00
|
|
|
if (this->kind != other.kind) {
|
|
|
|
return this->kind < other.kind;
|
|
|
|
}
|
|
|
|
return this->index < other.index;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool operator==(const Key& other) const {
|
2016-07-20 19:05:48 +00:00
|
|
|
return this->is_constant == other.is_constant && this->rep == other.rep &&
|
2015-04-09 10:40:41 +00:00
|
|
|
this->kind == other.kind && this->index == other.index;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2014-07-30 13:54:45 +00:00
|
|
|
// Internally, the state is a normalized permutation of (kind,index) pairs.
|
|
|
|
typedef Key Value;
|
|
|
|
typedef std::map<Key, Value> OperandMap;
|
|
|
|
|
2015-04-15 12:36:36 +00:00
|
|
|
Value read(const InstructionOperand& op) const {
|
2014-07-30 13:54:45 +00:00
|
|
|
OperandMap::const_iterator it = values_.find(KeyFor(op));
|
|
|
|
return (it == values_.end()) ? ValueFor(op) : it->second;
|
|
|
|
}
|
|
|
|
|
2015-04-15 12:36:36 +00:00
|
|
|
void write(const InstructionOperand& op, Value v) {
|
2014-07-30 13:54:45 +00:00
|
|
|
if (v == ValueFor(op)) {
|
|
|
|
values_.erase(KeyFor(op));
|
|
|
|
} else {
|
|
|
|
values_[KeyFor(op)] = v;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-15 12:36:36 +00:00
|
|
|
static Key KeyFor(const InstructionOperand& op) {
|
|
|
|
bool is_constant = op.IsConstant();
|
2016-07-20 19:05:48 +00:00
|
|
|
MachineRepresentation rep =
|
|
|
|
v8::internal::compiler::InstructionSequence::DefaultRepresentation();
|
[turbofan] Create ExplicitOperands to specify operands without virtual registers
Up until now, if one wanted to specify an explicit stack location or register as an operand for an instruction, it had to also be
explicitly associated with a virtual register as a so-called
FixedRegister or FixedStackSlot.
For the implementation of tail calls, the plan is to use the gap
resolver needs to shuffle stack locations from the caller to the
tail-called callee. In order to do this, it must be possible to
explicitly address operand locations on the stack that are not
associated with virtual registers.
This CL introduces ExplictOperands, which can specify a specific
register or stack location that is not associated with virtual
register. This will allow tail calls to specify the target
locations for the necessary stack moves in the gap for the tail
call without the core register allocation having to know about
the target of the stack moves at all.
In the process this CL:
* creates a new Operand kind, ExplicitOperand, with which
instructions can specify register and stack slots without an
associated virtual register.
* creates a LocationOperand class from which AllocatedOperand and
ExplicitOperand are derived and provides a common interface to
get Register, DoubleRegister and spill slot information.
* removes RegisterOperand, DoubleRegisterOperand,
StackSlotOperand and DoubleStackSlotOperand, they are subsumed
by LocationOperand.
* addresses a cleanup TODO in AllocatedOperand to reduce the
redundancy of AllocatedOperand::Kind by using machine_type() to
determine if an operand corresponds to a general purpose or
double register.
BUG=v8:4076
LOG=n
Review URL: https://codereview.chromium.org/1389373002
Cr-Commit-Position: refs/heads/master@{#31603}
2015-10-27 13:26:35 +00:00
|
|
|
LocationOperand::LocationKind kind;
|
2015-04-09 10:40:41 +00:00
|
|
|
int index;
|
|
|
|
if (!is_constant) {
|
2016-07-20 19:05:48 +00:00
|
|
|
const LocationOperand& loc_op = LocationOperand::cast(op);
|
|
|
|
if (loc_op.IsAnyRegister()) {
|
|
|
|
if (loc_op.IsFPRegister()) {
|
|
|
|
rep = kSimpleFPAliasing ? MachineRepresentation::kFloat64
|
|
|
|
: loc_op.representation();
|
|
|
|
}
|
|
|
|
index = loc_op.register_code();
|
2015-10-02 16:55:12 +00:00
|
|
|
} else {
|
2016-07-20 19:05:48 +00:00
|
|
|
index = loc_op.index();
|
2015-10-02 16:55:12 +00:00
|
|
|
}
|
2016-07-20 19:05:48 +00:00
|
|
|
kind = loc_op.location_kind();
|
2015-04-09 10:40:41 +00:00
|
|
|
} else {
|
2015-04-15 12:36:36 +00:00
|
|
|
index = ConstantOperand::cast(op).virtual_register();
|
[turbofan] Create ExplicitOperands to specify operands without virtual registers
Up until now, if one wanted to specify an explicit stack location or register as an operand for an instruction, it had to also be
explicitly associated with a virtual register as a so-called
FixedRegister or FixedStackSlot.
For the implementation of tail calls, the plan is to use the gap
resolver needs to shuffle stack locations from the caller to the
tail-called callee. In order to do this, it must be possible to
explicitly address operand locations on the stack that are not
associated with virtual registers.
This CL introduces ExplictOperands, which can specify a specific
register or stack location that is not associated with virtual
register. This will allow tail calls to specify the target
locations for the necessary stack moves in the gap for the tail
call without the core register allocation having to know about
the target of the stack moves at all.
In the process this CL:
* creates a new Operand kind, ExplicitOperand, with which
instructions can specify register and stack slots without an
associated virtual register.
* creates a LocationOperand class from which AllocatedOperand and
ExplicitOperand are derived and provides a common interface to
get Register, DoubleRegister and spill slot information.
* removes RegisterOperand, DoubleRegisterOperand,
StackSlotOperand and DoubleStackSlotOperand, they are subsumed
by LocationOperand.
* addresses a cleanup TODO in AllocatedOperand to reduce the
redundancy of AllocatedOperand::Kind by using machine_type() to
determine if an operand corresponds to a general purpose or
double register.
BUG=v8:4076
LOG=n
Review URL: https://codereview.chromium.org/1389373002
Cr-Commit-Position: refs/heads/master@{#31603}
2015-10-27 13:26:35 +00:00
|
|
|
kind = LocationOperand::REGISTER;
|
2015-04-09 10:40:41 +00:00
|
|
|
}
|
2016-07-20 19:05:48 +00:00
|
|
|
Key key = {is_constant, rep, kind, index};
|
2015-04-09 10:40:41 +00:00
|
|
|
return key;
|
2014-07-30 13:54:45 +00:00
|
|
|
}
|
|
|
|
|
2015-04-15 12:36:36 +00:00
|
|
|
static Value ValueFor(const InstructionOperand& op) { return KeyFor(op); }
|
2015-04-09 09:15:28 +00:00
|
|
|
|
|
|
|
static InstructionOperand FromKey(Key key) {
|
2015-04-09 10:40:41 +00:00
|
|
|
if (key.is_constant) {
|
|
|
|
return ConstantOperand(key.index);
|
2015-04-09 09:15:28 +00:00
|
|
|
}
|
2016-07-20 19:05:48 +00:00
|
|
|
return AllocatedOperand(key.kind, key.rep, key.index);
|
2014-07-30 13:54:45 +00:00
|
|
|
}
|
|
|
|
|
2014-09-30 10:29:32 +00:00
|
|
|
friend std::ostream& operator<<(std::ostream& os,
|
|
|
|
const InterpreterState& is) {
|
2014-07-30 13:54:45 +00:00
|
|
|
for (OperandMap::const_iterator it = is.values_.begin();
|
|
|
|
it != is.values_.end(); ++it) {
|
|
|
|
if (it != is.values_.begin()) os << " ";
|
2016-07-20 19:05:48 +00:00
|
|
|
InstructionOperand source = FromKey(it->second);
|
|
|
|
InstructionOperand destination = FromKey(it->first);
|
2015-04-15 12:36:36 +00:00
|
|
|
MoveOperands mo(source, destination);
|
2016-06-27 15:29:51 +00:00
|
|
|
PrintableMoveOperands pmo = {RegisterConfiguration::Turbofan(), &mo};
|
2014-11-04 09:21:12 +00:00
|
|
|
os << pmo;
|
2014-07-30 13:54:45 +00:00
|
|
|
}
|
|
|
|
return os;
|
|
|
|
}
|
|
|
|
|
|
|
|
OperandMap values_;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// An abstract interpreter for moves, swaps and parallel moves.
|
|
|
|
class MoveInterpreter : public GapResolver::Assembler {
|
|
|
|
public:
|
2015-04-15 12:36:36 +00:00
|
|
|
explicit MoveInterpreter(Zone* zone) : zone_(zone) {}
|
|
|
|
|
2015-11-04 13:08:27 +00:00
|
|
|
void AssembleMove(InstructionOperand* source,
|
|
|
|
InstructionOperand* destination) override {
|
2015-04-15 12:36:36 +00:00
|
|
|
ParallelMove* moves = new (zone_) ParallelMove(zone_);
|
|
|
|
moves->AddMove(*source, *destination);
|
2014-07-30 13:54:45 +00:00
|
|
|
state_.ExecuteInParallel(moves);
|
|
|
|
}
|
|
|
|
|
2015-11-04 13:08:27 +00:00
|
|
|
void AssembleSwap(InstructionOperand* source,
|
|
|
|
InstructionOperand* destination) override {
|
2015-04-15 12:36:36 +00:00
|
|
|
ParallelMove* moves = new (zone_) ParallelMove(zone_);
|
|
|
|
moves->AddMove(*source, *destination);
|
|
|
|
moves->AddMove(*destination, *source);
|
2014-07-30 13:54:45 +00:00
|
|
|
state_.ExecuteInParallel(moves);
|
|
|
|
}
|
|
|
|
|
2015-04-15 12:36:36 +00:00
|
|
|
void AssembleParallelMove(const ParallelMove* moves) {
|
2014-07-30 13:54:45 +00:00
|
|
|
state_.ExecuteInParallel(moves);
|
|
|
|
}
|
|
|
|
|
|
|
|
InterpreterState state() const { return state_; }
|
|
|
|
|
|
|
|
private:
|
2015-04-15 12:36:36 +00:00
|
|
|
Zone* const zone_;
|
2014-07-30 13:54:45 +00:00
|
|
|
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());
|
2015-04-29 19:36:16 +00:00
|
|
|
std::set<InstructionOperand, CompareOperandModuloType> seen;
|
2014-07-30 13:54:45 +00:00
|
|
|
for (int i = 0; i < size; ++i) {
|
2016-07-20 19:05:48 +00:00
|
|
|
MachineRepresentation rep = RandomRepresentation();
|
2016-06-14 12:26:59 +00:00
|
|
|
MoveOperands mo(CreateRandomOperand(true, rep),
|
|
|
|
CreateRandomOperand(false, rep));
|
2014-07-30 13:54:45 +00:00
|
|
|
if (!mo.IsRedundant() && seen.find(mo.destination()) == seen.end()) {
|
2015-04-15 12:36:36 +00:00
|
|
|
parallel_move->AddMove(mo.source(), mo.destination());
|
2014-07-30 13:54:45 +00:00
|
|
|
seen.insert(mo.destination());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return parallel_move;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2015-12-10 09:03:30 +00:00
|
|
|
MachineRepresentation RandomRepresentation() {
|
2016-06-14 12:26:59 +00:00
|
|
|
int index = rng_->NextInt(5);
|
2015-04-29 19:36:16 +00:00
|
|
|
switch (index) {
|
|
|
|
case 0:
|
2015-12-10 09:03:30 +00:00
|
|
|
return MachineRepresentation::kWord32;
|
2015-04-29 19:36:16 +00:00
|
|
|
case 1:
|
2015-12-10 09:03:30 +00:00
|
|
|
return MachineRepresentation::kWord64;
|
2015-04-29 19:36:16 +00:00
|
|
|
case 2:
|
2016-07-20 19:05:48 +00:00
|
|
|
// TODO(bbudge) Re-enable float operands when GapResolver correctly
|
|
|
|
// handles FP aliasing.
|
|
|
|
return kSimpleFPAliasing ? MachineRepresentation::kFloat32
|
|
|
|
: MachineRepresentation::kFloat64;
|
2016-06-14 12:26:59 +00:00
|
|
|
case 3:
|
|
|
|
return MachineRepresentation::kFloat64;
|
|
|
|
case 4:
|
2015-12-10 09:03:30 +00:00
|
|
|
return MachineRepresentation::kTagged;
|
2015-04-29 19:36:16 +00:00
|
|
|
}
|
|
|
|
UNREACHABLE();
|
2015-12-10 09:03:30 +00:00
|
|
|
return MachineRepresentation::kNone;
|
2015-04-29 19:36:16 +00:00
|
|
|
}
|
|
|
|
|
2016-06-14 12:26:59 +00:00
|
|
|
InstructionOperand CreateRandomOperand(bool is_source,
|
|
|
|
MachineRepresentation rep) {
|
2016-06-27 15:29:51 +00:00
|
|
|
auto conf = RegisterConfiguration::Turbofan();
|
2016-06-24 08:23:52 +00:00
|
|
|
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);
|
|
|
|
};
|
[turbofan] Create ExplicitOperands to specify operands without virtual registers
Up until now, if one wanted to specify an explicit stack location or register as an operand for an instruction, it had to also be
explicitly associated with a virtual register as a so-called
FixedRegister or FixedStackSlot.
For the implementation of tail calls, the plan is to use the gap
resolver needs to shuffle stack locations from the caller to the
tail-called callee. In order to do this, it must be possible to
explicitly address operand locations on the stack that are not
associated with virtual registers.
This CL introduces ExplictOperands, which can specify a specific
register or stack location that is not associated with virtual
register. This will allow tail calls to specify the target
locations for the necessary stack moves in the gap for the tail
call without the core register allocation having to know about
the target of the stack moves at all.
In the process this CL:
* creates a new Operand kind, ExplicitOperand, with which
instructions can specify register and stack slots without an
associated virtual register.
* creates a LocationOperand class from which AllocatedOperand and
ExplicitOperand are derived and provides a common interface to
get Register, DoubleRegister and spill slot information.
* removes RegisterOperand, DoubleRegisterOperand,
StackSlotOperand and DoubleStackSlotOperand, they are subsumed
by LocationOperand.
* addresses a cleanup TODO in AllocatedOperand to reduce the
redundancy of AllocatedOperand::Kind by using machine_type() to
determine if an operand corresponds to a general purpose or
double register.
BUG=v8:4076
LOG=n
Review URL: https://codereview.chromium.org/1389373002
Cr-Commit-Position: refs/heads/master@{#31603}
2015-10-27 13:26:35 +00:00
|
|
|
int index = rng_->NextInt(7);
|
2015-04-09 09:15:28 +00:00
|
|
|
// destination can't be Constant.
|
2016-06-14 12:26:59 +00:00
|
|
|
switch (rng_->NextInt(is_source ? 5 : 4)) {
|
2014-07-30 13:54:45 +00:00
|
|
|
case 0:
|
2016-06-14 12:26:59 +00:00
|
|
|
return AllocatedOperand(LocationOperand::STACK_SLOT, rep, index);
|
2014-07-30 13:54:45 +00:00
|
|
|
case 1:
|
2016-06-14 12:26:59 +00:00
|
|
|
return AllocatedOperand(LocationOperand::REGISTER, rep, index);
|
2014-07-30 13:54:45 +00:00
|
|
|
case 2:
|
2016-06-24 08:23:52 +00:00
|
|
|
return ExplicitOperand(LocationOperand::REGISTER, rep,
|
|
|
|
GetRegisterCode(rep, 1));
|
2016-06-14 12:26:59 +00:00
|
|
|
case 3:
|
2016-06-24 08:23:52 +00:00
|
|
|
return ExplicitOperand(LocationOperand::STACK_SLOT, rep,
|
|
|
|
GetRegisterCode(rep, index));
|
2016-06-14 12:26:59 +00:00
|
|
|
case 4:
|
2015-04-15 12:36:36 +00:00
|
|
|
return ConstantOperand(index);
|
2014-07-30 13:54:45 +00:00
|
|
|
}
|
|
|
|
UNREACHABLE();
|
2015-04-15 12:36:36 +00:00
|
|
|
return InstructionOperand();
|
2014-07-30 13:54:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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.
|
2015-04-15 12:36:36 +00:00
|
|
|
MoveInterpreter mi1(pmc.main_zone());
|
2014-07-30 13:54:45 +00:00
|
|
|
mi1.AssembleParallelMove(pm);
|
|
|
|
|
2015-04-15 12:36:36 +00:00
|
|
|
MoveInterpreter mi2(pmc.main_zone());
|
2014-07-30 13:54:45 +00:00
|
|
|
GapResolver resolver(&mi2);
|
|
|
|
resolver.Resolve(pm);
|
|
|
|
|
2016-07-20 19:05:48 +00:00
|
|
|
CHECK_EQ(mi1.state(), mi2.state());
|
2014-07-30 13:54:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-10-30 09:16:26 +00:00
|
|
|
|
|
|
|
} // namespace compiler
|
|
|
|
} // namespace internal
|
|
|
|
} // namespace v8
|