1 // Copyright 2014 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "src/compiler/common-operator.h"
6 #include "src/compiler/graph.h"
7 #include "src/compiler/instruction.h"
13 std::ostream& operator<<(std::ostream& os,
14 const PrintableInstructionOperand& printable) {
15 const InstructionOperand& op = *printable.op_;
16 const RegisterConfiguration* conf = printable.register_configuration_;
18 case InstructionOperand::UNALLOCATED: {
19 const UnallocatedOperand* unalloc = UnallocatedOperand::cast(&op);
20 os << "v" << unalloc->virtual_register();
21 if (unalloc->basic_policy() == UnallocatedOperand::FIXED_SLOT) {
22 return os << "(=" << unalloc->fixed_slot_index() << "S)";
24 switch (unalloc->extended_policy()) {
25 case UnallocatedOperand::NONE:
27 case UnallocatedOperand::FIXED_REGISTER:
28 return os << "(=" << conf->general_register_name(
29 unalloc->fixed_register_index()) << ")";
30 case UnallocatedOperand::FIXED_DOUBLE_REGISTER:
31 return os << "(=" << conf->double_register_name(
32 unalloc->fixed_register_index()) << ")";
33 case UnallocatedOperand::MUST_HAVE_REGISTER:
35 case UnallocatedOperand::SAME_AS_FIRST_INPUT:
37 case UnallocatedOperand::ANY:
41 case InstructionOperand::CONSTANT:
42 return os << "[constant:" << op.index() << "]";
43 case InstructionOperand::IMMEDIATE:
44 return os << "[immediate:" << op.index() << "]";
45 case InstructionOperand::STACK_SLOT:
46 return os << "[stack:" << op.index() << "]";
47 case InstructionOperand::DOUBLE_STACK_SLOT:
48 return os << "[double_stack:" << op.index() << "]";
49 case InstructionOperand::REGISTER:
50 return os << "[" << conf->general_register_name(op.index()) << "|R]";
51 case InstructionOperand::DOUBLE_REGISTER:
52 return os << "[" << conf->double_register_name(op.index()) << "|R]";
53 case InstructionOperand::INVALID:
61 std::ostream& operator<<(std::ostream& os,
62 const PrintableMoveOperands& printable) {
63 const MoveOperands& mo = *printable.move_operands_;
64 PrintableInstructionOperand printable_op = {printable.register_configuration_,
68 if (!mo.source()->Equals(mo.destination())) {
69 printable_op.op_ = mo.source();
70 os << " = " << printable_op;
76 bool ParallelMove::IsRedundant() const {
77 for (int i = 0; i < move_operands_.length(); ++i) {
78 if (!move_operands_[i].IsRedundant()) return false;
84 Instruction::Instruction(InstructionCode opcode)
86 bit_field_(OutputCountField::encode(0) | InputCountField::encode(0) |
87 TempCountField::encode(0) | IsCallField::encode(false) |
88 IsControlField::encode(false)),
92 Instruction::Instruction(InstructionCode opcode, size_t output_count,
93 InstructionOperand* outputs, size_t input_count,
94 InstructionOperand* inputs, size_t temp_count,
95 InstructionOperand* temps)
97 bit_field_(OutputCountField::encode(output_count) |
98 InputCountField::encode(input_count) |
99 TempCountField::encode(temp_count) |
100 IsCallField::encode(false) | IsControlField::encode(false)),
103 for (size_t i = 0; i < output_count; ++i) {
104 DCHECK(!outputs[i].IsInvalid());
105 operands_[offset++] = outputs[i];
107 for (size_t i = 0; i < input_count; ++i) {
108 DCHECK(!inputs[i].IsInvalid());
109 operands_[offset++] = inputs[i];
111 for (size_t i = 0; i < temp_count; ++i) {
112 DCHECK(!temps[i].IsInvalid());
113 operands_[offset++] = temps[i];
118 bool GapInstruction::IsRedundant() const {
119 for (int i = GapInstruction::FIRST_INNER_POSITION;
120 i <= GapInstruction::LAST_INNER_POSITION; i++) {
121 if (parallel_moves_[i] != NULL && !parallel_moves_[i]->IsRedundant())
128 std::ostream& operator<<(std::ostream& os,
129 const PrintableParallelMove& printable) {
130 const ParallelMove& pm = *printable.parallel_move_;
132 for (ZoneList<MoveOperands>::iterator move = pm.move_operands()->begin();
133 move != pm.move_operands()->end(); ++move) {
134 if (move->IsEliminated()) continue;
135 if (!first) os << " ";
137 PrintableMoveOperands pmo = {printable.register_configuration_, move};
144 void PointerMap::RecordPointer(InstructionOperand* op, Zone* zone) {
145 // Do not record arguments as pointers.
146 if (op->IsStackSlot() && op->index() < 0) return;
147 DCHECK(!op->IsDoubleRegister() && !op->IsDoubleStackSlot());
148 pointer_operands_.Add(op, zone);
152 void PointerMap::RemovePointer(InstructionOperand* op) {
153 // Do not record arguments as pointers.
154 if (op->IsStackSlot() && op->index() < 0) return;
155 DCHECK(!op->IsDoubleRegister() && !op->IsDoubleStackSlot());
156 for (int i = 0; i < pointer_operands_.length(); ++i) {
157 if (pointer_operands_[i]->Equals(op)) {
158 pointer_operands_.Remove(i);
165 void PointerMap::RecordUntagged(InstructionOperand* op, Zone* zone) {
166 // Do not record arguments as pointers.
167 if (op->IsStackSlot() && op->index() < 0) return;
168 DCHECK(!op->IsDoubleRegister() && !op->IsDoubleStackSlot());
169 untagged_operands_.Add(op, zone);
173 std::ostream& operator<<(std::ostream& os, const PointerMap& pm) {
175 for (ZoneList<InstructionOperand*>::iterator op =
176 pm.pointer_operands_.begin();
177 op != pm.pointer_operands_.end(); ++op) {
178 if (op != pm.pointer_operands_.begin()) os << ";";
185 std::ostream& operator<<(std::ostream& os, const ArchOpcode& ao) {
190 ARCH_OPCODE_LIST(CASE)
198 std::ostream& operator<<(std::ostream& os, const AddressingMode& am) {
205 TARGET_ADDRESSING_MODE_LIST(CASE)
213 std::ostream& operator<<(std::ostream& os, const FlagsMode& fm) {
218 return os << "branch";
227 std::ostream& operator<<(std::ostream& os, const FlagsCondition& fc) {
230 return os << "equal";
232 return os << "not equal";
233 case kSignedLessThan:
234 return os << "signed less than";
235 case kSignedGreaterThanOrEqual:
236 return os << "signed greater than or equal";
237 case kSignedLessThanOrEqual:
238 return os << "signed less than or equal";
239 case kSignedGreaterThan:
240 return os << "signed greater than";
241 case kUnsignedLessThan:
242 return os << "unsigned less than";
243 case kUnsignedGreaterThanOrEqual:
244 return os << "unsigned greater than or equal";
245 case kUnsignedLessThanOrEqual:
246 return os << "unsigned less than or equal";
247 case kUnsignedGreaterThan:
248 return os << "unsigned greater than";
249 case kUnorderedEqual:
250 return os << "unordered equal";
251 case kUnorderedNotEqual:
252 return os << "unordered not equal";
254 return os << "overflow";
256 return os << "not overflow";
263 std::ostream& operator<<(std::ostream& os,
264 const PrintableInstruction& printable) {
265 const Instruction& instr = *printable.instr_;
266 PrintableInstructionOperand printable_op = {printable.register_configuration_,
268 if (instr.OutputCount() > 1) os << "(";
269 for (size_t i = 0; i < instr.OutputCount(); i++) {
270 if (i > 0) os << ", ";
271 printable_op.op_ = instr.OutputAt(i);
275 if (instr.OutputCount() > 1) os << ") = ";
276 if (instr.OutputCount() == 1) os << " = ";
278 if (instr.IsGapMoves()) {
279 const GapInstruction* gap = GapInstruction::cast(&instr);
281 for (int i = GapInstruction::FIRST_INNER_POSITION;
282 i <= GapInstruction::LAST_INNER_POSITION; i++) {
284 if (gap->parallel_moves_[i] != NULL) {
285 PrintableParallelMove ppm = {printable.register_configuration_,
286 gap->parallel_moves_[i]};
291 } else if (instr.IsSourcePosition()) {
292 const SourcePositionInstruction* pos =
293 SourcePositionInstruction::cast(&instr);
294 os << "position (" << pos->source_position().raw() << ")";
296 os << ArchOpcodeField::decode(instr.opcode());
297 AddressingMode am = AddressingModeField::decode(instr.opcode());
298 if (am != kMode_None) {
299 os << " : " << AddressingModeField::decode(instr.opcode());
301 FlagsMode fm = FlagsModeField::decode(instr.opcode());
302 if (fm != kFlags_none) {
303 os << " && " << fm << " if "
304 << FlagsConditionField::decode(instr.opcode());
307 if (instr.InputCount() > 0) {
308 for (size_t i = 0; i < instr.InputCount(); i++) {
309 printable_op.op_ = instr.InputAt(i);
310 os << " " << printable_op;
317 std::ostream& operator<<(std::ostream& os, const Constant& constant) {
318 switch (constant.type()) {
319 case Constant::kInt32:
320 return os << constant.ToInt32();
321 case Constant::kInt64:
322 return os << constant.ToInt64() << "l";
323 case Constant::kFloat32:
324 return os << constant.ToFloat32() << "f";
325 case Constant::kFloat64:
326 return os << constant.ToFloat64();
327 case Constant::kExternalReference:
328 return os << static_cast<const void*>(
329 constant.ToExternalReference().address());
330 case Constant::kHeapObject:
331 return os << Brief(*constant.ToHeapObject());
332 case Constant::kRpoNumber:
333 return os << "RPO" << constant.ToRpoNumber().ToInt();
340 PhiInstruction::PhiInstruction(Zone* zone, int virtual_register,
342 : virtual_register_(virtual_register),
343 output_(UnallocatedOperand(UnallocatedOperand::NONE, virtual_register)),
344 operands_(input_count, zone),
345 inputs_(input_count, zone) {}
348 void PhiInstruction::SetInput(size_t offset, int virtual_register) {
349 DCHECK(inputs_[offset].IsInvalid());
350 auto input = UnallocatedOperand(UnallocatedOperand::ANY, virtual_register);
351 inputs_[offset] = input;
352 operands_[offset] = virtual_register;
356 InstructionBlock::InstructionBlock(Zone* zone, BasicBlock::Id id,
357 BasicBlock::RpoNumber rpo_number,
358 BasicBlock::RpoNumber loop_header,
359 BasicBlock::RpoNumber loop_end,
365 ao_number_(rpo_number),
366 rpo_number_(rpo_number),
367 loop_header_(loop_header),
371 deferred_(deferred) {}
374 size_t InstructionBlock::PredecessorIndexOf(
375 BasicBlock::RpoNumber rpo_number) const {
377 for (InstructionBlock::Predecessors::const_iterator i = predecessors_.begin();
378 i != predecessors_.end(); ++i, ++j) {
379 if (*i == rpo_number) break;
385 static BasicBlock::RpoNumber GetRpo(BasicBlock* block) {
386 if (block == NULL) return BasicBlock::RpoNumber::Invalid();
387 return block->GetRpoNumber();
391 static BasicBlock::RpoNumber GetLoopEndRpo(const BasicBlock* block) {
392 if (!block->IsLoopHeader()) return BasicBlock::RpoNumber::Invalid();
393 return block->loop_end()->GetRpoNumber();
397 static InstructionBlock* InstructionBlockFor(Zone* zone,
398 const BasicBlock* block) {
399 InstructionBlock* instr_block = new (zone) InstructionBlock(
400 zone, block->id(), block->GetRpoNumber(), GetRpo(block->loop_header()),
401 GetLoopEndRpo(block), block->deferred());
402 // Map successors and precessors
403 instr_block->successors().reserve(block->SuccessorCount());
404 for (BasicBlock* successor : block->successors()) {
405 instr_block->successors().push_back(successor->GetRpoNumber());
407 instr_block->predecessors().reserve(block->PredecessorCount());
408 for (BasicBlock* predecessor : block->predecessors()) {
409 instr_block->predecessors().push_back(predecessor->GetRpoNumber());
415 InstructionBlocks* InstructionSequence::InstructionBlocksFor(
416 Zone* zone, const Schedule* schedule) {
417 InstructionBlocks* blocks = zone->NewArray<InstructionBlocks>(1);
418 new (blocks) InstructionBlocks(
419 static_cast<int>(schedule->rpo_order()->size()), NULL, zone);
420 size_t rpo_number = 0;
421 for (BasicBlockVector::const_iterator it = schedule->rpo_order()->begin();
422 it != schedule->rpo_order()->end(); ++it, ++rpo_number) {
423 DCHECK(!(*blocks)[rpo_number]);
424 DCHECK((*it)->GetRpoNumber().ToSize() == rpo_number);
425 (*blocks)[rpo_number] = InstructionBlockFor(zone, *it);
427 ComputeAssemblyOrder(blocks);
432 void InstructionSequence::ComputeAssemblyOrder(InstructionBlocks* blocks) {
434 for (auto const block : *blocks) {
435 if (!block->IsDeferred()) {
436 block->set_ao_number(BasicBlock::RpoNumber::FromInt(ao++));
439 for (auto const block : *blocks) {
440 if (block->IsDeferred()) {
441 block->set_ao_number(BasicBlock::RpoNumber::FromInt(ao++));
447 InstructionSequence::InstructionSequence(Isolate* isolate,
448 Zone* instruction_zone,
449 InstructionBlocks* instruction_blocks)
451 zone_(instruction_zone),
452 instruction_blocks_(instruction_blocks),
453 block_starts_(zone()),
454 constants_(ConstantMap::key_compare(),
455 ConstantMap::allocator_type(zone())),
457 instructions_(zone()),
458 next_virtual_register_(0),
459 pointer_maps_(zone()),
460 doubles_(std::less<int>(), VirtualRegisterSet::allocator_type(zone())),
461 references_(std::less<int>(), VirtualRegisterSet::allocator_type(zone())),
462 deoptimization_entries_(zone()) {
463 block_starts_.reserve(instruction_blocks_->size());
467 int InstructionSequence::NextVirtualRegister() {
468 int virtual_register = next_virtual_register_++;
469 CHECK_NE(virtual_register, InstructionOperand::kInvalidVirtualRegister);
470 return virtual_register;
474 GapInstruction* InstructionSequence::GetBlockStart(
475 BasicBlock::RpoNumber rpo) const {
476 const InstructionBlock* block = InstructionBlockAt(rpo);
477 return GapInstruction::cast(InstructionAt(block->code_start()));
481 void InstructionSequence::StartBlock(BasicBlock::RpoNumber rpo) {
482 DCHECK(block_starts_.size() == rpo.ToSize());
483 InstructionBlock* block = InstructionBlockAt(rpo);
484 int code_start = static_cast<int>(instructions_.size());
485 block->set_code_start(code_start);
486 block_starts_.push_back(code_start);
490 void InstructionSequence::EndBlock(BasicBlock::RpoNumber rpo) {
491 int end = static_cast<int>(instructions_.size());
492 InstructionBlock* block = InstructionBlockAt(rpo);
493 if (block->code_start() == end) { // Empty block. Insert a nop.
494 AddInstruction(Instruction::New(zone(), kArchNop));
495 end = static_cast<int>(instructions_.size());
497 DCHECK(block->code_start() >= 0 && block->code_start() < end);
498 block->set_code_end(end);
502 int InstructionSequence::AddInstruction(Instruction* instr) {
503 GapInstruction* gap = GapInstruction::New(zone());
504 instructions_.push_back(gap);
505 int index = static_cast<int>(instructions_.size());
506 instructions_.push_back(instr);
507 if (instr->NeedsPointerMap()) {
508 DCHECK(instr->pointer_map() == NULL);
509 PointerMap* pointer_map = new (zone()) PointerMap(zone());
510 pointer_map->set_instruction_position(index);
511 instr->set_pointer_map(pointer_map);
512 pointer_maps_.push_back(pointer_map);
518 const InstructionBlock* InstructionSequence::GetInstructionBlock(
519 int instruction_index) const {
520 DCHECK(instruction_blocks_->size() == block_starts_.size());
521 auto begin = block_starts_.begin();
522 auto end = std::lower_bound(begin, block_starts_.end(), instruction_index,
523 std::less_equal<int>());
524 size_t index = std::distance(begin, end) - 1;
525 auto block = instruction_blocks_->at(index);
526 DCHECK(block->code_start() <= instruction_index &&
527 instruction_index < block->code_end());
532 bool InstructionSequence::IsReference(int virtual_register) const {
533 return references_.find(virtual_register) != references_.end();
537 bool InstructionSequence::IsDouble(int virtual_register) const {
538 return doubles_.find(virtual_register) != doubles_.end();
542 void InstructionSequence::MarkAsReference(int virtual_register) {
543 references_.insert(virtual_register);
547 void InstructionSequence::MarkAsDouble(int virtual_register) {
548 doubles_.insert(virtual_register);
552 void InstructionSequence::AddGapMove(int index, InstructionOperand* from,
553 InstructionOperand* to) {
554 GapAt(index)->GetOrCreateParallelMove(GapInstruction::START, zone())->AddMove(
559 InstructionSequence::StateId InstructionSequence::AddFrameStateDescriptor(
560 FrameStateDescriptor* descriptor) {
561 int deoptimization_id = static_cast<int>(deoptimization_entries_.size());
562 deoptimization_entries_.push_back(descriptor);
563 return StateId::FromInt(deoptimization_id);
566 FrameStateDescriptor* InstructionSequence::GetFrameStateDescriptor(
567 InstructionSequence::StateId state_id) {
568 return deoptimization_entries_[state_id.ToInt()];
572 int InstructionSequence::GetFrameStateDescriptorCount() {
573 return static_cast<int>(deoptimization_entries_.size());
577 FrameStateDescriptor::FrameStateDescriptor(
578 Zone* zone, const FrameStateCallInfo& state_info, size_t parameters_count,
579 size_t locals_count, size_t stack_count, FrameStateDescriptor* outer_state)
580 : type_(state_info.type()),
581 bailout_id_(state_info.bailout_id()),
582 frame_state_combine_(state_info.state_combine()),
583 parameters_count_(parameters_count),
584 locals_count_(locals_count),
585 stack_count_(stack_count),
587 outer_state_(outer_state),
588 jsfunction_(state_info.jsfunction()) {
589 types_.resize(GetSize(), kMachNone);
592 size_t FrameStateDescriptor::GetSize(OutputFrameStateCombine combine) const {
593 size_t size = parameters_count() + locals_count() + stack_count() +
594 (HasContext() ? 1 : 0);
595 switch (combine.kind()) {
596 case OutputFrameStateCombine::kPushOutput:
597 size += combine.GetPushCount();
599 case OutputFrameStateCombine::kPokeAt:
606 size_t FrameStateDescriptor::GetTotalSize() const {
607 size_t total_size = 0;
608 for (const FrameStateDescriptor* iter = this; iter != NULL;
609 iter = iter->outer_state_) {
610 total_size += iter->GetSize();
616 size_t FrameStateDescriptor::GetFrameCount() const {
618 for (const FrameStateDescriptor* iter = this; iter != NULL;
619 iter = iter->outer_state_) {
626 size_t FrameStateDescriptor::GetJSFrameCount() const {
628 for (const FrameStateDescriptor* iter = this; iter != NULL;
629 iter = iter->outer_state_) {
630 if (iter->type_ == JS_FRAME) {
638 MachineType FrameStateDescriptor::GetType(size_t index) const {
639 return types_[index];
643 void FrameStateDescriptor::SetType(size_t index, MachineType type) {
644 DCHECK(index < GetSize());
645 types_[index] = type;
649 std::ostream& operator<<(std::ostream& os,
650 const PrintableInstructionSequence& printable) {
651 const InstructionSequence& code = *printable.sequence_;
652 for (size_t i = 0; i < code.immediates_.size(); ++i) {
653 Constant constant = code.immediates_[i];
654 os << "IMM#" << i << ": " << constant << "\n";
657 for (ConstantMap::const_iterator it = code.constants_.begin();
658 it != code.constants_.end(); ++i, ++it) {
659 os << "CST#" << i << ": v" << it->first << " = " << it->second << "\n";
661 for (int i = 0; i < code.InstructionBlockCount(); i++) {
662 BasicBlock::RpoNumber rpo = BasicBlock::RpoNumber::FromInt(i);
663 const InstructionBlock* block = code.InstructionBlockAt(rpo);
664 CHECK(block->rpo_number() == rpo);
666 os << "RPO#" << block->rpo_number();
667 os << ": AO#" << block->ao_number();
668 os << ": B" << block->id();
669 if (block->IsDeferred()) os << " (deferred)";
670 if (block->IsLoopHeader()) {
671 os << " loop blocks: [" << block->rpo_number() << ", "
672 << block->loop_end() << ")";
674 os << " instructions: [" << block->code_start() << ", "
675 << block->code_end() << ")\n predecessors:";
677 for (auto pred : block->predecessors()) {
678 const InstructionBlock* pred_block = code.InstructionBlockAt(pred);
679 os << " B" << pred_block->id();
683 for (auto phi : block->phis()) {
684 PrintableInstructionOperand printable_op = {
685 printable.register_configuration_, &phi->output()};
686 os << " phi: " << printable_op << " =";
687 for (auto input : phi->inputs()) {
688 printable_op.op_ = &input;
689 os << " " << printable_op;
694 ScopedVector<char> buf(32);
695 PrintableInstruction printable_instr;
696 printable_instr.register_configuration_ = printable.register_configuration_;
697 for (int j = block->first_instruction_index();
698 j <= block->last_instruction_index(); j++) {
699 // TODO(svenpanne) Add some basic formatting to our streams.
700 SNPrintF(buf, "%5d", j);
701 printable_instr.instr_ = code.InstructionAt(j);
702 os << " " << buf.start() << ": " << printable_instr << "\n";
705 for (auto succ : block->successors()) {
706 const InstructionBlock* succ_block = code.InstructionBlockAt(succ);
707 os << " B" << succ_block->id();
714 } // namespace compiler
715 } // namespace internal