// 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/instruction-selector.h" #include "src/compiler/graph.h" #include "src/compiler/instruction-selector-impl.h" #include "src/compiler/node-matchers.h" #include "src/compiler/node-properties-inl.h" #include "src/compiler/pipeline.h" namespace v8 { namespace internal { namespace compiler { InstructionSelector::InstructionSelector(Zone* local_zone, Graph* graph, Linkage* linkage, InstructionSequence* sequence, Schedule* schedule, SourcePositionTable* source_positions, Features features) : zone_(local_zone), linkage_(linkage), sequence_(sequence), source_positions_(source_positions), features_(features), schedule_(schedule), node_map_(graph->NodeCount(), kNodeUnmapped, zone()), current_block_(NULL), instructions_(zone()), defined_(graph->NodeCount(), false, zone()), used_(graph->NodeCount(), false, zone()) {} void InstructionSelector::SelectInstructions() { // Mark the inputs of all phis in loop headers as used. BasicBlockVector* blocks = schedule()->rpo_order(); for (BasicBlockVectorIter i = blocks->begin(); i != blocks->end(); ++i) { BasicBlock* block = *i; if (!block->IsLoopHeader()) continue; DCHECK_NE(0, static_cast(block->PredecessorCount())); DCHECK_NE(1, static_cast(block->PredecessorCount())); for (BasicBlock::const_iterator j = block->begin(); j != block->end(); ++j) { Node* phi = *j; if (phi->opcode() != IrOpcode::kPhi) continue; // Mark all inputs as used. Node::Inputs inputs = phi->inputs(); for (InputIter k = inputs.begin(); k != inputs.end(); ++k) { MarkAsUsed(*k); } } } // Visit each basic block in post order. for (BasicBlockVectorRIter i = blocks->rbegin(); i != blocks->rend(); ++i) { VisitBlock(*i); } // Schedule the selected instructions. for (BasicBlockVectorIter i = blocks->begin(); i != blocks->end(); ++i) { BasicBlock* block = *i; InstructionBlock* instruction_block = sequence()->InstructionBlockAt(block->GetRpoNumber()); size_t end = instruction_block->code_end(); size_t start = instruction_block->code_start(); sequence()->StartBlock(block); while (start-- > end) { sequence()->AddInstruction(instructions_[start]); } sequence()->EndBlock(block); } } Instruction* InstructionSelector::Emit(InstructionCode opcode, InstructionOperand* output, size_t temp_count, InstructionOperand** temps) { size_t output_count = output == NULL ? 0 : 1; return Emit(opcode, output_count, &output, 0, NULL, temp_count, temps); } Instruction* InstructionSelector::Emit(InstructionCode opcode, InstructionOperand* output, InstructionOperand* a, size_t temp_count, InstructionOperand** temps) { size_t output_count = output == NULL ? 0 : 1; return Emit(opcode, output_count, &output, 1, &a, temp_count, temps); } Instruction* InstructionSelector::Emit(InstructionCode opcode, InstructionOperand* output, InstructionOperand* a, InstructionOperand* b, size_t temp_count, InstructionOperand** temps) { size_t output_count = output == NULL ? 0 : 1; InstructionOperand* inputs[] = {a, b}; size_t input_count = arraysize(inputs); return Emit(opcode, output_count, &output, input_count, inputs, temp_count, temps); } Instruction* InstructionSelector::Emit(InstructionCode opcode, InstructionOperand* output, InstructionOperand* a, InstructionOperand* b, InstructionOperand* c, size_t temp_count, InstructionOperand** temps) { size_t output_count = output == NULL ? 0 : 1; InstructionOperand* inputs[] = {a, b, c}; size_t input_count = arraysize(inputs); return Emit(opcode, output_count, &output, input_count, inputs, temp_count, temps); } Instruction* InstructionSelector::Emit( InstructionCode opcode, InstructionOperand* output, InstructionOperand* a, InstructionOperand* b, InstructionOperand* c, InstructionOperand* d, size_t temp_count, InstructionOperand** temps) { size_t output_count = output == NULL ? 0 : 1; InstructionOperand* inputs[] = {a, b, c, d}; size_t input_count = arraysize(inputs); return Emit(opcode, output_count, &output, input_count, inputs, temp_count, temps); } Instruction* InstructionSelector::Emit( InstructionCode opcode, size_t output_count, InstructionOperand** outputs, size_t input_count, InstructionOperand** inputs, size_t temp_count, InstructionOperand** temps) { Instruction* instr = Instruction::New(instruction_zone(), opcode, output_count, outputs, input_count, inputs, temp_count, temps); return Emit(instr); } Instruction* InstructionSelector::Emit(Instruction* instr) { instructions_.push_back(instr); return instr; } bool InstructionSelector::IsNextInAssemblyOrder(const BasicBlock* block) const { return current_block_->GetAoNumber().IsNext(block->GetAoNumber()); } bool InstructionSelector::CanCover(Node* user, Node* node) const { return node->OwnedBy(user) && schedule()->block(node) == schedule()->block(user); } int InstructionSelector::GetVirtualRegister(const Node* node) { if (node_map_[node->id()] == kNodeUnmapped) { node_map_[node->id()] = sequence()->NextVirtualRegister(); } return node_map_[node->id()]; } int InstructionSelector::GetMappedVirtualRegister(const Node* node) const { return node_map_[node->id()]; } bool InstructionSelector::IsDefined(Node* node) const { DCHECK_NOT_NULL(node); NodeId id = node->id(); DCHECK(id >= 0); DCHECK(id < static_cast(defined_.size())); return defined_[id]; } void InstructionSelector::MarkAsDefined(Node* node) { DCHECK_NOT_NULL(node); NodeId id = node->id(); DCHECK(id >= 0); DCHECK(id < static_cast(defined_.size())); defined_[id] = true; } bool InstructionSelector::IsUsed(Node* node) const { if (!node->op()->HasProperty(Operator::kEliminatable)) return true; NodeId id = node->id(); DCHECK(id >= 0); DCHECK(id < static_cast(used_.size())); return used_[id]; } void InstructionSelector::MarkAsUsed(Node* node) { DCHECK_NOT_NULL(node); NodeId id = node->id(); DCHECK(id >= 0); DCHECK(id < static_cast(used_.size())); used_[id] = true; } bool InstructionSelector::IsDouble(const Node* node) const { DCHECK_NOT_NULL(node); int virtual_register = GetMappedVirtualRegister(node); if (virtual_register == kNodeUnmapped) return false; return sequence()->IsDouble(virtual_register); } void InstructionSelector::MarkAsDouble(Node* node) { DCHECK_NOT_NULL(node); DCHECK(!IsReference(node)); sequence()->MarkAsDouble(GetVirtualRegister(node)); } bool InstructionSelector::IsReference(const Node* node) const { DCHECK_NOT_NULL(node); int virtual_register = GetMappedVirtualRegister(node); if (virtual_register == kNodeUnmapped) return false; return sequence()->IsReference(virtual_register); } void InstructionSelector::MarkAsReference(Node* node) { DCHECK_NOT_NULL(node); DCHECK(!IsDouble(node)); sequence()->MarkAsReference(GetVirtualRegister(node)); } void InstructionSelector::MarkAsRepresentation(MachineType rep, InstructionOperand* op) { UnallocatedOperand* unalloc = UnallocatedOperand::cast(op); switch (RepresentationOf(rep)) { case kRepFloat32: case kRepFloat64: sequence()->MarkAsDouble(unalloc->virtual_register()); break; case kRepTagged: sequence()->MarkAsReference(unalloc->virtual_register()); break; default: break; } } void InstructionSelector::MarkAsRepresentation(MachineType rep, Node* node) { DCHECK_NOT_NULL(node); switch (RepresentationOf(rep)) { case kRepFloat32: case kRepFloat64: MarkAsDouble(node); break; case kRepTagged: MarkAsReference(node); break; default: break; } } // TODO(bmeurer): Get rid of the CallBuffer business and make // InstructionSelector::VisitCall platform independent instead. CallBuffer::CallBuffer(Zone* zone, CallDescriptor* d, FrameStateDescriptor* frame_desc) : descriptor(d), frame_state_descriptor(frame_desc), output_nodes(zone), outputs(zone), instruction_args(zone), pushed_nodes(zone) { output_nodes.reserve(d->ReturnCount()); outputs.reserve(d->ReturnCount()); pushed_nodes.reserve(input_count()); instruction_args.reserve(input_count() + frame_state_value_count()); } // TODO(bmeurer): Get rid of the CallBuffer business and make // InstructionSelector::VisitCall platform independent instead. void InstructionSelector::InitializeCallBuffer(Node* call, CallBuffer* buffer, bool call_code_immediate, bool call_address_immediate) { OperandGenerator g(this); DCHECK_EQ(call->op()->OutputCount(), static_cast(buffer->descriptor->ReturnCount())); DCHECK_EQ( call->op()->ValueInputCount(), static_cast(buffer->input_count() + buffer->frame_state_count())); if (buffer->descriptor->ReturnCount() > 0) { // Collect the projections that represent multiple outputs from this call. if (buffer->descriptor->ReturnCount() == 1) { buffer->output_nodes.push_back(call); } else { buffer->output_nodes.resize(buffer->descriptor->ReturnCount(), NULL); call->CollectProjections(&buffer->output_nodes); } // Filter out the outputs that aren't live because no projection uses them. size_t outputs_needed_by_framestate = buffer->frame_state_descriptor == NULL ? 0 : buffer->frame_state_descriptor->state_combine() .ConsumedOutputCount(); for (size_t i = 0; i < buffer->output_nodes.size(); i++) { bool output_is_live = buffer->output_nodes[i] != NULL || i < outputs_needed_by_framestate; if (output_is_live) { MachineType type = buffer->descriptor->GetReturnType(static_cast(i)); LinkageLocation location = buffer->descriptor->GetReturnLocation(static_cast(i)); Node* output = buffer->output_nodes[i]; InstructionOperand* op = output == NULL ? g.TempLocation(location, type) : g.DefineAsLocation(output, location, type); MarkAsRepresentation(type, op); buffer->outputs.push_back(op); } } } // The first argument is always the callee code. Node* callee = call->InputAt(0); switch (buffer->descriptor->kind()) { case CallDescriptor::kCallCodeObject: buffer->instruction_args.push_back( (call_code_immediate && callee->opcode() == IrOpcode::kHeapConstant) ? g.UseImmediate(callee) : g.UseRegister(callee)); break; case CallDescriptor::kCallAddress: buffer->instruction_args.push_back( (call_address_immediate && (callee->opcode() == IrOpcode::kInt32Constant || callee->opcode() == IrOpcode::kInt64Constant)) ? g.UseImmediate(callee) : g.UseRegister(callee)); break; case CallDescriptor::kCallJSFunction: buffer->instruction_args.push_back( g.UseLocation(callee, buffer->descriptor->GetInputLocation(0), buffer->descriptor->GetInputType(0))); break; } DCHECK_EQ(1, static_cast(buffer->instruction_args.size())); // If the call needs a frame state, we insert the state information as // follows (n is the number of value inputs to the frame state): // arg 1 : deoptimization id. // arg 2 - arg (n + 1) : value inputs to the frame state. if (buffer->frame_state_descriptor != NULL) { InstructionSequence::StateId state_id = sequence()->AddFrameStateDescriptor(buffer->frame_state_descriptor); buffer->instruction_args.push_back(g.TempImmediate(state_id.ToInt())); Node* frame_state = call->InputAt(static_cast(buffer->descriptor->InputCount())); AddFrameStateInputs(frame_state, &buffer->instruction_args, buffer->frame_state_descriptor); } DCHECK(1 + buffer->frame_state_value_count() == buffer->instruction_args.size()); size_t input_count = static_cast(buffer->input_count()); // Split the arguments into pushed_nodes and instruction_args. Pushed // arguments require an explicit push instruction before the call and do // not appear as arguments to the call. Everything else ends up // as an InstructionOperand argument to the call. InputIter iter(call->inputs().begin()); int pushed_count = 0; for (size_t index = 0; index < input_count; ++iter, ++index) { DCHECK(iter != call->inputs().end()); DCHECK(index == static_cast(iter.index())); DCHECK((*iter)->op()->opcode() != IrOpcode::kFrameState); if (index == 0) continue; // The first argument (callee) is already done. InstructionOperand* op = g.UseLocation(*iter, buffer->descriptor->GetInputLocation(index), buffer->descriptor->GetInputType(index)); if (UnallocatedOperand::cast(op)->HasFixedSlotPolicy()) { int stack_index = -UnallocatedOperand::cast(op)->fixed_slot_index() - 1; if (static_cast(stack_index) >= buffer->pushed_nodes.size()) { buffer->pushed_nodes.resize(stack_index + 1, NULL); } DCHECK_EQ(NULL, buffer->pushed_nodes[stack_index]); buffer->pushed_nodes[stack_index] = *iter; pushed_count++; } else { buffer->instruction_args.push_back(op); } } CHECK_EQ(pushed_count, static_cast(buffer->pushed_nodes.size())); DCHECK(static_cast(input_count) == (buffer->instruction_args.size() + buffer->pushed_nodes.size() - buffer->frame_state_value_count())); } void InstructionSelector::VisitBlock(BasicBlock* block) { DCHECK_EQ(NULL, current_block_); current_block_ = block; int current_block_end = static_cast(instructions_.size()); // Generate code for the block control "top down", but schedule the code // "bottom up". VisitControl(block); std::reverse(instructions_.begin() + current_block_end, instructions_.end()); // Visit code in reverse control flow order, because architecture-specific // matching may cover more than one node at a time. for (BasicBlock::reverse_iterator i = block->rbegin(); i != block->rend(); ++i) { Node* node = *i; // Skip nodes that are unused or already defined. if (!IsUsed(node) || IsDefined(node)) continue; // Generate code for this node "top down", but schedule the code "bottom // up". size_t current_node_end = instructions_.size(); VisitNode(node); std::reverse(instructions_.begin() + current_node_end, instructions_.end()); } // We're done with the block. InstructionBlock* instruction_block = sequence()->InstructionBlockAt(block->GetRpoNumber()); instruction_block->set_code_start(static_cast(instructions_.size())); instruction_block->set_code_end(current_block_end); current_block_ = NULL; } static inline void CheckNoPhis(const BasicBlock* block) { #ifdef DEBUG // Branch targets should not have phis. for (BasicBlock::const_iterator i = block->begin(); i != block->end(); ++i) { const Node* node = *i; CHECK_NE(IrOpcode::kPhi, node->opcode()); } #endif } void InstructionSelector::VisitControl(BasicBlock* block) { Node* input = block->control_input(); switch (block->control()) { case BasicBlock::kGoto: return VisitGoto(block->SuccessorAt(0)); case BasicBlock::kBranch: { DCHECK_EQ(IrOpcode::kBranch, input->opcode()); BasicBlock* tbranch = block->SuccessorAt(0); BasicBlock* fbranch = block->SuccessorAt(1); // SSA deconstruction requires targets of branches not to have phis. // Edge split form guarantees this property, but is more strict. CheckNoPhis(tbranch); CheckNoPhis(fbranch); if (tbranch == fbranch) return VisitGoto(tbranch); return VisitBranch(input, tbranch, fbranch); } case BasicBlock::kReturn: { // If the result itself is a return, return its input. Node* value = (input != NULL && input->opcode() == IrOpcode::kReturn) ? input->InputAt(0) : input; return VisitReturn(value); } case BasicBlock::kThrow: return VisitThrow(input); case BasicBlock::kNone: { // TODO(titzer): exit block doesn't have control. DCHECK(input == NULL); break; } default: UNREACHABLE(); break; } } MachineType InstructionSelector::GetMachineType(Node* node) { DCHECK_NOT_NULL(schedule()->block(node)); // should only use scheduled nodes. switch (node->opcode()) { case IrOpcode::kStart: case IrOpcode::kLoop: case IrOpcode::kEnd: case IrOpcode::kBranch: case IrOpcode::kIfTrue: case IrOpcode::kIfFalse: case IrOpcode::kEffectPhi: case IrOpcode::kMerge: case IrOpcode::kTerminate: // No code needed for these graph artifacts. return kMachNone; case IrOpcode::kFinish: return kMachAnyTagged; case IrOpcode::kParameter: return linkage()->GetParameterType(OpParameter(node)); case IrOpcode::kPhi: return OpParameter(node); case IrOpcode::kProjection: // TODO(jarin) Really project from outputs. return kMachAnyTagged; case IrOpcode::kInt32Constant: return kMachInt32; case IrOpcode::kInt64Constant: return kMachInt64; case IrOpcode::kExternalConstant: return kMachPtr; case IrOpcode::kFloat64Constant: return kMachFloat64; case IrOpcode::kHeapConstant: case IrOpcode::kNumberConstant: return kMachAnyTagged; case IrOpcode::kCall: return kMachAnyTagged; case IrOpcode::kFrameState: case IrOpcode::kStateValues: return kMachNone; case IrOpcode::kLoad: return OpParameter(node); case IrOpcode::kStore: return kMachNone; case IrOpcode::kWord32And: case IrOpcode::kWord32Or: case IrOpcode::kWord32Xor: case IrOpcode::kWord32Shl: case IrOpcode::kWord32Shr: case IrOpcode::kWord32Sar: case IrOpcode::kWord32Ror: return kMachInt32; case IrOpcode::kWord32Equal: return kMachBool; case IrOpcode::kWord64And: case IrOpcode::kWord64Or: case IrOpcode::kWord64Xor: case IrOpcode::kWord64Shl: case IrOpcode::kWord64Shr: case IrOpcode::kWord64Sar: case IrOpcode::kWord64Ror: return kMachInt64; case IrOpcode::kWord64Equal: return kMachBool; case IrOpcode::kInt32Add: case IrOpcode::kInt32AddWithOverflow: case IrOpcode::kInt32Sub: case IrOpcode::kInt32SubWithOverflow: case IrOpcode::kInt32Mul: case IrOpcode::kInt32Div: case IrOpcode::kInt32Mod: return kMachInt32; case IrOpcode::kInt32LessThan: case IrOpcode::kInt32LessThanOrEqual: case IrOpcode::kUint32LessThan: case IrOpcode::kUint32LessThanOrEqual: return kMachBool; case IrOpcode::kInt64Add: case IrOpcode::kInt64Sub: case IrOpcode::kInt64Mul: case IrOpcode::kInt64Div: case IrOpcode::kInt64Mod: return kMachInt64; case IrOpcode::kInt64LessThan: case IrOpcode::kInt64LessThanOrEqual: return kMachBool; case IrOpcode::kChangeFloat32ToFloat64: case IrOpcode::kChangeInt32ToFloat64: case IrOpcode::kChangeUint32ToFloat64: return kMachFloat64; case IrOpcode::kChangeFloat64ToInt32: return kMachInt32; case IrOpcode::kChangeFloat64ToUint32: return kMachUint32; case IrOpcode::kChangeInt32ToInt64: return kMachInt64; case IrOpcode::kChangeUint32ToUint64: return kMachUint64; case IrOpcode::kTruncateFloat64ToFloat32: return kMachFloat32; case IrOpcode::kTruncateFloat64ToInt32: case IrOpcode::kTruncateInt64ToInt32: return kMachInt32; case IrOpcode::kFloat64Add: case IrOpcode::kFloat64Sub: case IrOpcode::kFloat64Mul: case IrOpcode::kFloat64Div: case IrOpcode::kFloat64Mod: case IrOpcode::kFloat64Sqrt: case IrOpcode::kFloat64Floor: case IrOpcode::kFloat64Ceil: case IrOpcode::kFloat64RoundTruncate: case IrOpcode::kFloat64RoundTiesAway: return kMachFloat64; case IrOpcode::kFloat64Equal: case IrOpcode::kFloat64LessThan: case IrOpcode::kFloat64LessThanOrEqual: return kMachBool; default: V8_Fatal(__FILE__, __LINE__, "Unexpected operator #%d:%s @ node #%d", node->opcode(), node->op()->mnemonic(), node->id()); } return kMachNone; } void InstructionSelector::VisitNode(Node* node) { DCHECK_NOT_NULL(schedule()->block(node)); // should only use scheduled nodes. SourcePosition source_position = source_positions_->GetSourcePosition(node); if (!source_position.IsUnknown()) { DCHECK(!source_position.IsInvalid()); if (FLAG_turbo_source_positions || node->opcode() == IrOpcode::kCall) { Emit(SourcePositionInstruction::New(instruction_zone(), source_position)); } } switch (node->opcode()) { case IrOpcode::kStart: case IrOpcode::kLoop: case IrOpcode::kEnd: case IrOpcode::kBranch: case IrOpcode::kIfTrue: case IrOpcode::kIfFalse: case IrOpcode::kEffectPhi: case IrOpcode::kMerge: // No code needed for these graph artifacts. return; case IrOpcode::kFinish: return MarkAsReference(node), VisitFinish(node); case IrOpcode::kParameter: { MachineType type = linkage()->GetParameterType(OpParameter(node)); MarkAsRepresentation(type, node); return VisitParameter(node); } case IrOpcode::kPhi: { MachineType type = OpParameter(node); MarkAsRepresentation(type, node); return VisitPhi(node); } case IrOpcode::kProjection: return VisitProjection(node); case IrOpcode::kInt32Constant: case IrOpcode::kInt64Constant: case IrOpcode::kExternalConstant: return VisitConstant(node); case IrOpcode::kFloat32Constant: return MarkAsDouble(node), VisitConstant(node); case IrOpcode::kFloat64Constant: return MarkAsDouble(node), VisitConstant(node); case IrOpcode::kHeapConstant: case IrOpcode::kNumberConstant: // TODO(turbofan): only mark non-smis as references. return MarkAsReference(node), VisitConstant(node); case IrOpcode::kCall: return VisitCall(node); case IrOpcode::kFrameState: case IrOpcode::kStateValues: return; case IrOpcode::kLoad: { LoadRepresentation rep = OpParameter(node); MarkAsRepresentation(rep, node); return VisitLoad(node); } case IrOpcode::kStore: return VisitStore(node); case IrOpcode::kWord32And: return VisitWord32And(node); case IrOpcode::kWord32Or: return VisitWord32Or(node); case IrOpcode::kWord32Xor: return VisitWord32Xor(node); case IrOpcode::kWord32Shl: return VisitWord32Shl(node); case IrOpcode::kWord32Shr: return VisitWord32Shr(node); case IrOpcode::kWord32Sar: return VisitWord32Sar(node); case IrOpcode::kWord32Ror: return VisitWord32Ror(node); case IrOpcode::kWord32Equal: return VisitWord32Equal(node); case IrOpcode::kWord64And: return VisitWord64And(node); case IrOpcode::kWord64Or: return VisitWord64Or(node); case IrOpcode::kWord64Xor: return VisitWord64Xor(node); case IrOpcode::kWord64Shl: return VisitWord64Shl(node); case IrOpcode::kWord64Shr: return VisitWord64Shr(node); case IrOpcode::kWord64Sar: return VisitWord64Sar(node); case IrOpcode::kWord64Ror: return VisitWord64Ror(node); case IrOpcode::kWord64Equal: return VisitWord64Equal(node); case IrOpcode::kInt32Add: return VisitInt32Add(node); case IrOpcode::kInt32AddWithOverflow: return VisitInt32AddWithOverflow(node); case IrOpcode::kInt32Sub: return VisitInt32Sub(node); case IrOpcode::kInt32SubWithOverflow: return VisitInt32SubWithOverflow(node); case IrOpcode::kInt32Mul: return VisitInt32Mul(node); case IrOpcode::kInt32MulHigh: return VisitInt32MulHigh(node); case IrOpcode::kInt32Div: return VisitInt32Div(node); case IrOpcode::kInt32Mod: return VisitInt32Mod(node); case IrOpcode::kInt32LessThan: return VisitInt32LessThan(node); case IrOpcode::kInt32LessThanOrEqual: return VisitInt32LessThanOrEqual(node); case IrOpcode::kUint32Div: return VisitUint32Div(node); case IrOpcode::kUint32LessThan: return VisitUint32LessThan(node); case IrOpcode::kUint32LessThanOrEqual: return VisitUint32LessThanOrEqual(node); case IrOpcode::kUint32Mod: return VisitUint32Mod(node); case IrOpcode::kUint32MulHigh: return VisitUint32MulHigh(node); case IrOpcode::kInt64Add: return VisitInt64Add(node); case IrOpcode::kInt64Sub: return VisitInt64Sub(node); case IrOpcode::kInt64Mul: return VisitInt64Mul(node); case IrOpcode::kInt64Div: return VisitInt64Div(node); case IrOpcode::kInt64Mod: return VisitInt64Mod(node); case IrOpcode::kInt64LessThan: return VisitInt64LessThan(node); case IrOpcode::kInt64LessThanOrEqual: return VisitInt64LessThanOrEqual(node); case IrOpcode::kUint64Div: return VisitUint64Div(node); case IrOpcode::kUint64LessThan: return VisitUint64LessThan(node); case IrOpcode::kUint64Mod: return VisitUint64Mod(node); case IrOpcode::kChangeFloat32ToFloat64: return MarkAsDouble(node), VisitChangeFloat32ToFloat64(node); case IrOpcode::kChangeInt32ToFloat64: return MarkAsDouble(node), VisitChangeInt32ToFloat64(node); case IrOpcode::kChangeUint32ToFloat64: return MarkAsDouble(node), VisitChangeUint32ToFloat64(node); case IrOpcode::kChangeFloat64ToInt32: return VisitChangeFloat64ToInt32(node); case IrOpcode::kChangeFloat64ToUint32: return VisitChangeFloat64ToUint32(node); case IrOpcode::kChangeInt32ToInt64: return VisitChangeInt32ToInt64(node); case IrOpcode::kChangeUint32ToUint64: return VisitChangeUint32ToUint64(node); case IrOpcode::kTruncateFloat64ToFloat32: return MarkAsDouble(node), VisitTruncateFloat64ToFloat32(node); case IrOpcode::kTruncateFloat64ToInt32: return VisitTruncateFloat64ToInt32(node); case IrOpcode::kTruncateInt64ToInt32: return VisitTruncateInt64ToInt32(node); case IrOpcode::kFloat64Add: return MarkAsDouble(node), VisitFloat64Add(node); case IrOpcode::kFloat64Sub: return MarkAsDouble(node), VisitFloat64Sub(node); case IrOpcode::kFloat64Mul: return MarkAsDouble(node), VisitFloat64Mul(node); case IrOpcode::kFloat64Div: return MarkAsDouble(node), VisitFloat64Div(node); case IrOpcode::kFloat64Mod: return MarkAsDouble(node), VisitFloat64Mod(node); case IrOpcode::kFloat64Sqrt: return MarkAsDouble(node), VisitFloat64Sqrt(node); case IrOpcode::kFloat64Equal: return VisitFloat64Equal(node); case IrOpcode::kFloat64LessThan: return VisitFloat64LessThan(node); case IrOpcode::kFloat64LessThanOrEqual: return VisitFloat64LessThanOrEqual(node); case IrOpcode::kFloat64Floor: return MarkAsDouble(node), VisitFloat64Floor(node); case IrOpcode::kFloat64Ceil: return MarkAsDouble(node), VisitFloat64Ceil(node); case IrOpcode::kFloat64RoundTruncate: return MarkAsDouble(node), VisitFloat64RoundTruncate(node); case IrOpcode::kFloat64RoundTiesAway: return MarkAsDouble(node), VisitFloat64RoundTiesAway(node); case IrOpcode::kLoadStackPointer: return VisitLoadStackPointer(node); default: V8_Fatal(__FILE__, __LINE__, "Unexpected operator #%d:%s @ node #%d", node->opcode(), node->op()->mnemonic(), node->id()); break; } } #if V8_TURBOFAN_BACKEND void InstructionSelector::VisitTruncateFloat64ToInt32(Node* node) { OperandGenerator g(this); Emit(kArchTruncateDoubleToI, g.DefineAsRegister(node), g.UseRegister(node->InputAt(0))); } void InstructionSelector::VisitLoadStackPointer(Node* node) { OperandGenerator g(this); Emit(kArchStackPointer, g.DefineAsRegister(node)); } #endif // V8_TURBOFAN_BACKEND // 32 bit targets do not implement the following instructions. #if V8_TARGET_ARCH_32_BIT && V8_TURBOFAN_BACKEND void InstructionSelector::VisitWord64And(Node* node) { UNIMPLEMENTED(); } void InstructionSelector::VisitWord64Or(Node* node) { UNIMPLEMENTED(); } void InstructionSelector::VisitWord64Xor(Node* node) { UNIMPLEMENTED(); } void InstructionSelector::VisitWord64Shl(Node* node) { UNIMPLEMENTED(); } void InstructionSelector::VisitWord64Shr(Node* node) { UNIMPLEMENTED(); } void InstructionSelector::VisitWord64Sar(Node* node) { UNIMPLEMENTED(); } void InstructionSelector::VisitWord64Ror(Node* node) { UNIMPLEMENTED(); } void InstructionSelector::VisitWord64Equal(Node* node) { UNIMPLEMENTED(); } void InstructionSelector::VisitInt64Add(Node* node) { UNIMPLEMENTED(); } void InstructionSelector::VisitInt64Sub(Node* node) { UNIMPLEMENTED(); } void InstructionSelector::VisitInt64Mul(Node* node) { UNIMPLEMENTED(); } void InstructionSelector::VisitInt64Div(Node* node) { UNIMPLEMENTED(); } void InstructionSelector::VisitInt64LessThan(Node* node) { UNIMPLEMENTED(); } void InstructionSelector::VisitInt64LessThanOrEqual(Node* node) { UNIMPLEMENTED(); } void InstructionSelector::VisitUint64Div(Node* node) { UNIMPLEMENTED(); } void InstructionSelector::VisitInt64Mod(Node* node) { UNIMPLEMENTED(); } void InstructionSelector::VisitUint64LessThan(Node* node) { UNIMPLEMENTED(); } void InstructionSelector::VisitUint64Mod(Node* node) { UNIMPLEMENTED(); } void InstructionSelector::VisitChangeInt32ToInt64(Node* node) { UNIMPLEMENTED(); } void InstructionSelector::VisitChangeUint32ToUint64(Node* node) { UNIMPLEMENTED(); } void InstructionSelector::VisitTruncateInt64ToInt32(Node* node) { UNIMPLEMENTED(); } #endif // V8_TARGET_ARCH_32_BIT && V8_TURBOFAN_BACKEND void InstructionSelector::VisitFinish(Node* node) { OperandGenerator g(this); Node* value = node->InputAt(0); Emit(kArchNop, g.DefineSameAsFirst(node), g.Use(value)); } void InstructionSelector::VisitParameter(Node* node) { OperandGenerator g(this); int index = OpParameter(node); Emit(kArchNop, g.DefineAsLocation(node, linkage()->GetParameterLocation(index), linkage()->GetParameterType(index))); } void InstructionSelector::VisitPhi(Node* node) { // TODO(bmeurer): Emit a PhiInstruction here. PhiInstruction* phi = new (instruction_zone()) PhiInstruction(instruction_zone(), GetVirtualRegister(node)); sequence()->InstructionBlockAt(current_block_->GetRpoNumber())->AddPhi(phi); const int input_count = node->op()->InputCount(); phi->operands().reserve(static_cast(input_count)); for (int i = 0; i < input_count; ++i) { Node* const input = node->InputAt(i); MarkAsUsed(input); phi->operands().push_back(GetVirtualRegister(input)); } } void InstructionSelector::VisitProjection(Node* node) { OperandGenerator g(this); Node* value = node->InputAt(0); switch (value->opcode()) { case IrOpcode::kInt32AddWithOverflow: case IrOpcode::kInt32SubWithOverflow: if (OpParameter(node) == 0) { Emit(kArchNop, g.DefineSameAsFirst(node), g.Use(value)); } else { DCHECK(OpParameter(node) == 1u); MarkAsUsed(value); } break; default: break; } } void InstructionSelector::VisitConstant(Node* node) { // We must emit a NOP here because every live range needs a defining // instruction in the register allocator. OperandGenerator g(this); Emit(kArchNop, g.DefineAsConstant(node)); } void InstructionSelector::VisitGoto(BasicBlock* target) { if (IsNextInAssemblyOrder(target)) { // fall through to the next block. Emit(kArchNop, NULL)->MarkAsControl(); } else { // jump to the next block. OperandGenerator g(this); Emit(kArchJmp, NULL, g.Label(target))->MarkAsControl(); } } void InstructionSelector::VisitReturn(Node* value) { OperandGenerator g(this); if (value != NULL) { Emit(kArchRet, NULL, g.UseLocation(value, linkage()->GetReturnLocation(), linkage()->GetReturnType())); } else { Emit(kArchRet, NULL); } } void InstructionSelector::VisitThrow(Node* value) { UNIMPLEMENTED(); // TODO(titzer) } void InstructionSelector::FillTypeVectorFromStateValues( ZoneVector* types, Node* state_values) { DCHECK(state_values->opcode() == IrOpcode::kStateValues); int count = state_values->InputCount(); types->reserve(static_cast(count)); for (int i = 0; i < count; i++) { types->push_back(GetMachineType(state_values->InputAt(i))); } } FrameStateDescriptor* InstructionSelector::GetFrameStateDescriptor( Node* state) { DCHECK(state->opcode() == IrOpcode::kFrameState); DCHECK_EQ(5, state->InputCount()); DCHECK_EQ(IrOpcode::kStateValues, state->InputAt(0)->opcode()); DCHECK_EQ(IrOpcode::kStateValues, state->InputAt(1)->opcode()); DCHECK_EQ(IrOpcode::kStateValues, state->InputAt(2)->opcode()); FrameStateCallInfo state_info = OpParameter(state); int parameters = state->InputAt(0)->InputCount(); int locals = state->InputAt(1)->InputCount(); int stack = state->InputAt(2)->InputCount(); FrameStateDescriptor* outer_state = NULL; Node* outer_node = state->InputAt(4); if (outer_node->opcode() == IrOpcode::kFrameState) { outer_state = GetFrameStateDescriptor(outer_node); } return new (instruction_zone()) FrameStateDescriptor( instruction_zone(), state_info, parameters, locals, stack, outer_state); } static InstructionOperand* UseOrImmediate(OperandGenerator* g, Node* input) { switch (input->opcode()) { case IrOpcode::kInt32Constant: case IrOpcode::kNumberConstant: case IrOpcode::kFloat64Constant: case IrOpcode::kHeapConstant: return g->UseImmediate(input); default: return g->UseUnique(input); } } void InstructionSelector::AddFrameStateInputs( Node* state, InstructionOperandVector* inputs, FrameStateDescriptor* descriptor) { DCHECK_EQ(IrOpcode::kFrameState, state->op()->opcode()); if (descriptor->outer_state() != NULL) { AddFrameStateInputs(state->InputAt(4), inputs, descriptor->outer_state()); } Node* parameters = state->InputAt(0); Node* locals = state->InputAt(1); Node* stack = state->InputAt(2); Node* context = state->InputAt(3); DCHECK_EQ(IrOpcode::kStateValues, parameters->op()->opcode()); DCHECK_EQ(IrOpcode::kStateValues, locals->op()->opcode()); DCHECK_EQ(IrOpcode::kStateValues, stack->op()->opcode()); DCHECK_EQ(static_cast(descriptor->parameters_count()), parameters->InputCount()); DCHECK_EQ(static_cast(descriptor->locals_count()), locals->InputCount()); DCHECK_EQ(static_cast(descriptor->stack_count()), stack->InputCount()); ZoneVector types(instruction_zone()); types.reserve(descriptor->GetSize()); OperandGenerator g(this); size_t value_index = 0; for (int i = 0; i < static_cast(descriptor->parameters_count()); i++) { Node* input_node = parameters->InputAt(i); inputs->push_back(UseOrImmediate(&g, input_node)); descriptor->SetType(value_index++, GetMachineType(input_node)); } if (descriptor->HasContext()) { inputs->push_back(UseOrImmediate(&g, context)); descriptor->SetType(value_index++, kMachAnyTagged); } for (int i = 0; i < static_cast(descriptor->locals_count()); i++) { Node* input_node = locals->InputAt(i); inputs->push_back(UseOrImmediate(&g, input_node)); descriptor->SetType(value_index++, GetMachineType(input_node)); } for (int i = 0; i < static_cast(descriptor->stack_count()); i++) { Node* input_node = stack->InputAt(i); inputs->push_back(UseOrImmediate(&g, input_node)); descriptor->SetType(value_index++, GetMachineType(input_node)); } DCHECK(value_index == descriptor->GetSize()); } #if !V8_TURBOFAN_BACKEND #define DECLARE_UNIMPLEMENTED_SELECTOR(x) \ void InstructionSelector::Visit##x(Node* node) { UNIMPLEMENTED(); } MACHINE_OP_LIST(DECLARE_UNIMPLEMENTED_SELECTOR) #undef DECLARE_UNIMPLEMENTED_SELECTOR void InstructionSelector::VisitCall(Node* node) { UNIMPLEMENTED(); } void InstructionSelector::VisitBranch(Node* branch, BasicBlock* tbranch, BasicBlock* fbranch) { UNIMPLEMENTED(); } // static MachineOperatorBuilder::Flags InstructionSelector::SupportedMachineOperatorFlags() { return MachineOperatorBuilder::Flag::kNoFlags; } #endif // !V8_TURBOFAN_BACKEND } // namespace compiler } // namespace internal } // namespace v8