blob: 53e288df3e19a705694394d02625b269327a04c1 [file] [log] [blame]
// 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.
#ifndef V8_COMPILER_INSTRUCTION_SELECTOR_IMPL_H_
#define V8_COMPILER_INSTRUCTION_SELECTOR_IMPL_H_
#include "src/compiler/generic-node-inl.h"
#include "src/compiler/instruction.h"
#include "src/compiler/instruction-selector.h"
#include "src/compiler/linkage.h"
#include "src/macro-assembler.h"
namespace v8 {
namespace internal {
namespace compiler {
// A helper class for the instruction selector that simplifies construction of
// Operands. This class implements a base for architecture-specific helpers.
class OperandGenerator {
public:
explicit OperandGenerator(InstructionSelector* selector)
: selector_(selector) {}
InstructionOperand* DefineAsRegister(Node* node) {
return Define(node, new (zone())
UnallocatedOperand(UnallocatedOperand::MUST_HAVE_REGISTER));
}
InstructionOperand* DefineSameAsFirst(Node* result) {
return Define(result, new (zone())
UnallocatedOperand(UnallocatedOperand::SAME_AS_FIRST_INPUT));
}
InstructionOperand* DefineAsFixed(Node* node, Register reg) {
return Define(node, new (zone())
UnallocatedOperand(UnallocatedOperand::FIXED_REGISTER,
Register::ToAllocationIndex(reg)));
}
InstructionOperand* DefineAsFixed(Node* node, DoubleRegister reg) {
return Define(node, new (zone())
UnallocatedOperand(UnallocatedOperand::FIXED_DOUBLE_REGISTER,
DoubleRegister::ToAllocationIndex(reg)));
}
InstructionOperand* DefineAsConstant(Node* node) {
selector()->MarkAsDefined(node);
int virtual_register = selector_->GetVirtualRegister(node);
sequence()->AddConstant(virtual_register, ToConstant(node));
return ConstantOperand::Create(virtual_register, zone());
}
InstructionOperand* DefineAsLocation(Node* node, LinkageLocation location,
MachineType type) {
return Define(node, ToUnallocatedOperand(location, type));
}
InstructionOperand* Use(Node* node) {
return Use(
node, new (zone()) UnallocatedOperand(
UnallocatedOperand::NONE, UnallocatedOperand::USED_AT_START));
}
InstructionOperand* UseRegister(Node* node) {
return Use(node, new (zone())
UnallocatedOperand(UnallocatedOperand::MUST_HAVE_REGISTER,
UnallocatedOperand::USED_AT_START));
}
// Use register or operand for the node. If a register is chosen, it won't
// alias any temporary or output registers.
InstructionOperand* UseUnique(Node* node) {
return Use(node, new (zone()) UnallocatedOperand(UnallocatedOperand::NONE));
}
// Use a unique register for the node that does not alias any temporary or
// output registers.
InstructionOperand* UseUniqueRegister(Node* node) {
return Use(node, new (zone())
UnallocatedOperand(UnallocatedOperand::MUST_HAVE_REGISTER));
}
InstructionOperand* UseFixed(Node* node, Register reg) {
return Use(node, new (zone())
UnallocatedOperand(UnallocatedOperand::FIXED_REGISTER,
Register::ToAllocationIndex(reg)));
}
InstructionOperand* UseFixed(Node* node, DoubleRegister reg) {
return Use(node, new (zone())
UnallocatedOperand(UnallocatedOperand::FIXED_DOUBLE_REGISTER,
DoubleRegister::ToAllocationIndex(reg)));
}
InstructionOperand* UseImmediate(Node* node) {
int index = sequence()->AddImmediate(ToConstant(node));
return ImmediateOperand::Create(index, zone());
}
InstructionOperand* UseLocation(Node* node, LinkageLocation location,
MachineType type) {
return Use(node, ToUnallocatedOperand(location, type));
}
InstructionOperand* TempRegister() {
UnallocatedOperand* op =
new (zone()) UnallocatedOperand(UnallocatedOperand::MUST_HAVE_REGISTER,
UnallocatedOperand::USED_AT_START);
op->set_virtual_register(sequence()->NextVirtualRegister());
return op;
}
InstructionOperand* TempDoubleRegister() {
UnallocatedOperand* op =
new (zone()) UnallocatedOperand(UnallocatedOperand::MUST_HAVE_REGISTER,
UnallocatedOperand::USED_AT_START);
op->set_virtual_register(sequence()->NextVirtualRegister());
sequence()->MarkAsDouble(op->virtual_register());
return op;
}
InstructionOperand* TempRegister(Register reg) {
return new (zone()) UnallocatedOperand(UnallocatedOperand::FIXED_REGISTER,
Register::ToAllocationIndex(reg));
}
InstructionOperand* TempImmediate(int32_t imm) {
int index = sequence()->AddImmediate(Constant(imm));
return ImmediateOperand::Create(index, zone());
}
InstructionOperand* TempLocation(LinkageLocation location, MachineType type) {
UnallocatedOperand* op = ToUnallocatedOperand(location, type);
op->set_virtual_register(sequence()->NextVirtualRegister());
return op;
}
InstructionOperand* Label(BasicBlock* block) {
// TODO(bmeurer): We misuse ImmediateOperand here.
return TempImmediate(block->rpo_number());
}
protected:
InstructionSelector* selector() const { return selector_; }
InstructionSequence* sequence() const { return selector()->sequence(); }
Isolate* isolate() const { return zone()->isolate(); }
Zone* zone() const { return selector()->instruction_zone(); }
private:
static Constant ToConstant(const Node* node) {
switch (node->opcode()) {
case IrOpcode::kInt32Constant:
return Constant(OpParameter<int32_t>(node));
case IrOpcode::kInt64Constant:
return Constant(OpParameter<int64_t>(node));
case IrOpcode::kFloat32Constant:
return Constant(OpParameter<float>(node));
case IrOpcode::kFloat64Constant:
case IrOpcode::kNumberConstant:
return Constant(OpParameter<double>(node));
case IrOpcode::kExternalConstant:
return Constant(OpParameter<ExternalReference>(node));
case IrOpcode::kHeapConstant:
return Constant(OpParameter<Unique<HeapObject> >(node).handle());
default:
break;
}
UNREACHABLE();
return Constant(static_cast<int32_t>(0));
}
UnallocatedOperand* Define(Node* node, UnallocatedOperand* operand) {
DCHECK_NOT_NULL(node);
DCHECK_NOT_NULL(operand);
operand->set_virtual_register(selector_->GetVirtualRegister(node));
selector()->MarkAsDefined(node);
return operand;
}
UnallocatedOperand* Use(Node* node, UnallocatedOperand* operand) {
DCHECK_NOT_NULL(node);
DCHECK_NOT_NULL(operand);
operand->set_virtual_register(selector_->GetVirtualRegister(node));
selector()->MarkAsUsed(node);
return operand;
}
UnallocatedOperand* ToUnallocatedOperand(LinkageLocation location,
MachineType type) {
if (location.location_ == LinkageLocation::ANY_REGISTER) {
return new (zone())
UnallocatedOperand(UnallocatedOperand::MUST_HAVE_REGISTER);
}
if (location.location_ < 0) {
return new (zone()) UnallocatedOperand(UnallocatedOperand::FIXED_SLOT,
location.location_);
}
if (RepresentationOf(type) == kRepFloat64) {
return new (zone()) UnallocatedOperand(
UnallocatedOperand::FIXED_DOUBLE_REGISTER, location.location_);
}
return new (zone()) UnallocatedOperand(UnallocatedOperand::FIXED_REGISTER,
location.location_);
}
InstructionSelector* selector_;
};
// The flags continuation is a way to combine a branch or a materialization
// of a boolean value with an instruction that sets the flags register.
// The whole instruction is treated as a unit by the register allocator, and
// thus no spills or moves can be introduced between the flags-setting
// instruction and the branch or set it should be combined with.
class FlagsContinuation FINAL {
public:
FlagsContinuation() : mode_(kFlags_none) {}
// Creates a new flags continuation from the given condition and true/false
// blocks.
FlagsContinuation(FlagsCondition condition, BasicBlock* true_block,
BasicBlock* false_block)
: mode_(kFlags_branch),
condition_(condition),
true_block_(true_block),
false_block_(false_block) {
DCHECK_NOT_NULL(true_block);
DCHECK_NOT_NULL(false_block);
}
// Creates a new flags continuation from the given condition and result node.
FlagsContinuation(FlagsCondition condition, Node* result)
: mode_(kFlags_set), condition_(condition), result_(result) {
DCHECK_NOT_NULL(result);
}
bool IsNone() const { return mode_ == kFlags_none; }
bool IsBranch() const { return mode_ == kFlags_branch; }
bool IsSet() const { return mode_ == kFlags_set; }
FlagsCondition condition() const {
DCHECK(!IsNone());
return condition_;
}
Node* result() const {
DCHECK(IsSet());
return result_;
}
BasicBlock* true_block() const {
DCHECK(IsBranch());
return true_block_;
}
BasicBlock* false_block() const {
DCHECK(IsBranch());
return false_block_;
}
void Negate() {
DCHECK(!IsNone());
condition_ = static_cast<FlagsCondition>(condition_ ^ 1);
}
void Commute() {
DCHECK(!IsNone());
switch (condition_) {
case kEqual:
case kNotEqual:
case kOverflow:
case kNotOverflow:
return;
case kSignedLessThan:
condition_ = kSignedGreaterThan;
return;
case kSignedGreaterThanOrEqual:
condition_ = kSignedLessThanOrEqual;
return;
case kSignedLessThanOrEqual:
condition_ = kSignedGreaterThanOrEqual;
return;
case kSignedGreaterThan:
condition_ = kSignedLessThan;
return;
case kUnsignedLessThan:
condition_ = kUnsignedGreaterThan;
return;
case kUnsignedGreaterThanOrEqual:
condition_ = kUnsignedLessThanOrEqual;
return;
case kUnsignedLessThanOrEqual:
condition_ = kUnsignedGreaterThanOrEqual;
return;
case kUnsignedGreaterThan:
condition_ = kUnsignedLessThan;
return;
case kUnorderedEqual:
case kUnorderedNotEqual:
return;
case kUnorderedLessThan:
condition_ = kUnorderedGreaterThan;
return;
case kUnorderedGreaterThanOrEqual:
condition_ = kUnorderedLessThanOrEqual;
return;
case kUnorderedLessThanOrEqual:
condition_ = kUnorderedGreaterThanOrEqual;
return;
case kUnorderedGreaterThan:
condition_ = kUnorderedLessThan;
return;
}
UNREACHABLE();
}
void OverwriteAndNegateIfEqual(FlagsCondition condition) {
bool negate = condition_ == kEqual;
condition_ = condition;
if (negate) Negate();
}
void SwapBlocks() { std::swap(true_block_, false_block_); }
// Encodes this flags continuation into the given opcode.
InstructionCode Encode(InstructionCode opcode) {
opcode |= FlagsModeField::encode(mode_);
if (mode_ != kFlags_none) {
opcode |= FlagsConditionField::encode(condition_);
}
return opcode;
}
private:
FlagsMode mode_;
FlagsCondition condition_;
Node* result_; // Only valid if mode_ == kFlags_set.
BasicBlock* true_block_; // Only valid if mode_ == kFlags_branch.
BasicBlock* false_block_; // Only valid if mode_ == kFlags_branch.
};
// An internal helper class for generating the operands to calls.
// TODO(bmeurer): Get rid of the CallBuffer business and make
// InstructionSelector::VisitCall platform independent instead.
struct CallBuffer {
CallBuffer(Zone* zone, CallDescriptor* descriptor,
FrameStateDescriptor* frame_state);
CallDescriptor* descriptor;
FrameStateDescriptor* frame_state_descriptor;
NodeVector output_nodes;
InstructionOperandVector outputs;
InstructionOperandVector instruction_args;
NodeVector pushed_nodes;
size_t input_count() const { return descriptor->InputCount(); }
size_t frame_state_count() const { return descriptor->FrameStateCount(); }
size_t frame_state_value_count() const {
return (frame_state_descriptor == NULL)
? 0
: (frame_state_descriptor->GetTotalSize() +
1); // Include deopt id.
}
};
} // namespace compiler
} // namespace internal
} // namespace v8
#endif // V8_COMPILER_INSTRUCTION_SELECTOR_IMPL_H_