ebd8125848eb16d1a897e5f9df0d7bd61b1b5dc0
[platform/upstream/nodejs.git] / deps / v8 / src / compiler / instruction.cc
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.
4
5 #include "src/compiler/common-operator.h"
6 #include "src/compiler/graph.h"
7 #include "src/compiler/instruction.h"
8
9 namespace v8 {
10 namespace internal {
11 namespace compiler {
12
13 std::ostream& operator<<(std::ostream& os,
14                          const PrintableInstructionOperand& printable) {
15   const InstructionOperand& op = *printable.op_;
16   const RegisterConfiguration* conf = printable.register_configuration_;
17   switch (op.kind()) {
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)";
23       }
24       switch (unalloc->extended_policy()) {
25         case UnallocatedOperand::NONE:
26           return os;
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:
34           return os << "(R)";
35         case UnallocatedOperand::SAME_AS_FIRST_INPUT:
36           return os << "(1)";
37         case UnallocatedOperand::ANY:
38           return os << "(-)";
39       }
40     }
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:
54       return os << "(x)";
55   }
56   UNREACHABLE();
57   return os;
58 }
59
60
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_,
65                                               mo.destination()};
66
67   os << printable_op;
68   if (!mo.source()->Equals(mo.destination())) {
69     printable_op.op_ = mo.source();
70     os << " = " << printable_op;
71   }
72   return os << ";";
73 }
74
75
76 bool ParallelMove::IsRedundant() const {
77   for (int i = 0; i < move_operands_.length(); ++i) {
78     if (!move_operands_[i].IsRedundant()) return false;
79   }
80   return true;
81 }
82
83
84 Instruction::Instruction(InstructionCode opcode)
85     : opcode_(opcode),
86       bit_field_(OutputCountField::encode(0) | InputCountField::encode(0) |
87                  TempCountField::encode(0) | IsCallField::encode(false) |
88                  IsControlField::encode(false)),
89       pointer_map_(NULL) {}
90
91
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)
96     : opcode_(opcode),
97       bit_field_(OutputCountField::encode(output_count) |
98                  InputCountField::encode(input_count) |
99                  TempCountField::encode(temp_count) |
100                  IsCallField::encode(false) | IsControlField::encode(false)),
101       pointer_map_(NULL) {
102   size_t offset = 0;
103   for (size_t i = 0; i < output_count; ++i) {
104     DCHECK(!outputs[i].IsInvalid());
105     operands_[offset++] = outputs[i];
106   }
107   for (size_t i = 0; i < input_count; ++i) {
108     DCHECK(!inputs[i].IsInvalid());
109     operands_[offset++] = inputs[i];
110   }
111   for (size_t i = 0; i < temp_count; ++i) {
112     DCHECK(!temps[i].IsInvalid());
113     operands_[offset++] = temps[i];
114   }
115 }
116
117
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())
122       return false;
123   }
124   return true;
125 }
126
127
128 std::ostream& operator<<(std::ostream& os,
129                          const PrintableParallelMove& printable) {
130   const ParallelMove& pm = *printable.parallel_move_;
131   bool first = true;
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 << " ";
136     first = false;
137     PrintableMoveOperands pmo = {printable.register_configuration_, move};
138     os << pmo;
139   }
140   return os;
141 }
142
143
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);
149 }
150
151
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);
159       --i;
160     }
161   }
162 }
163
164
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);
170 }
171
172
173 std::ostream& operator<<(std::ostream& os, const PointerMap& pm) {
174   os << "{";
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 << ";";
179     os << *op;
180   }
181   return os << "}";
182 }
183
184
185 std::ostream& operator<<(std::ostream& os, const ArchOpcode& ao) {
186   switch (ao) {
187 #define CASE(Name) \
188   case k##Name:    \
189     return os << #Name;
190     ARCH_OPCODE_LIST(CASE)
191 #undef CASE
192   }
193   UNREACHABLE();
194   return os;
195 }
196
197
198 std::ostream& operator<<(std::ostream& os, const AddressingMode& am) {
199   switch (am) {
200     case kMode_None:
201       return os;
202 #define CASE(Name)   \
203   case kMode_##Name: \
204     return os << #Name;
205       TARGET_ADDRESSING_MODE_LIST(CASE)
206 #undef CASE
207   }
208   UNREACHABLE();
209   return os;
210 }
211
212
213 std::ostream& operator<<(std::ostream& os, const FlagsMode& fm) {
214   switch (fm) {
215     case kFlags_none:
216       return os;
217     case kFlags_branch:
218       return os << "branch";
219     case kFlags_set:
220       return os << "set";
221   }
222   UNREACHABLE();
223   return os;
224 }
225
226
227 std::ostream& operator<<(std::ostream& os, const FlagsCondition& fc) {
228   switch (fc) {
229     case kEqual:
230       return os << "equal";
231     case kNotEqual:
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";
253     case kOverflow:
254       return os << "overflow";
255     case kNotOverflow:
256       return os << "not overflow";
257   }
258   UNREACHABLE();
259   return os;
260 }
261
262
263 std::ostream& operator<<(std::ostream& os,
264                          const PrintableInstruction& printable) {
265   const Instruction& instr = *printable.instr_;
266   PrintableInstructionOperand printable_op = {printable.register_configuration_,
267                                               NULL};
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);
272     os << printable_op;
273   }
274
275   if (instr.OutputCount() > 1) os << ") = ";
276   if (instr.OutputCount() == 1) os << " = ";
277
278   if (instr.IsGapMoves()) {
279     const GapInstruction* gap = GapInstruction::cast(&instr);
280     os << "gap ";
281     for (int i = GapInstruction::FIRST_INNER_POSITION;
282          i <= GapInstruction::LAST_INNER_POSITION; i++) {
283       os << "(";
284       if (gap->parallel_moves_[i] != NULL) {
285         PrintableParallelMove ppm = {printable.register_configuration_,
286                                      gap->parallel_moves_[i]};
287         os << ppm;
288       }
289       os << ") ";
290     }
291   } else if (instr.IsSourcePosition()) {
292     const SourcePositionInstruction* pos =
293         SourcePositionInstruction::cast(&instr);
294     os << "position (" << pos->source_position().raw() << ")";
295   } else {
296     os << ArchOpcodeField::decode(instr.opcode());
297     AddressingMode am = AddressingModeField::decode(instr.opcode());
298     if (am != kMode_None) {
299       os << " : " << AddressingModeField::decode(instr.opcode());
300     }
301     FlagsMode fm = FlagsModeField::decode(instr.opcode());
302     if (fm != kFlags_none) {
303       os << " && " << fm << " if "
304          << FlagsConditionField::decode(instr.opcode());
305     }
306   }
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;
311     }
312   }
313   return os;
314 }
315
316
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();
334   }
335   UNREACHABLE();
336   return os;
337 }
338
339
340 PhiInstruction::PhiInstruction(Zone* zone, int virtual_register,
341                                size_t input_count)
342     : virtual_register_(virtual_register),
343       output_(UnallocatedOperand(UnallocatedOperand::NONE, virtual_register)),
344       operands_(input_count, zone),
345       inputs_(input_count, zone) {}
346
347
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;
353 }
354
355
356 InstructionBlock::InstructionBlock(Zone* zone, BasicBlock::Id id,
357                                    BasicBlock::RpoNumber rpo_number,
358                                    BasicBlock::RpoNumber loop_header,
359                                    BasicBlock::RpoNumber loop_end,
360                                    bool deferred)
361     : successors_(zone),
362       predecessors_(zone),
363       phis_(zone),
364       id_(id),
365       ao_number_(rpo_number),
366       rpo_number_(rpo_number),
367       loop_header_(loop_header),
368       loop_end_(loop_end),
369       code_start_(-1),
370       code_end_(-1),
371       deferred_(deferred) {}
372
373
374 size_t InstructionBlock::PredecessorIndexOf(
375     BasicBlock::RpoNumber rpo_number) const {
376   size_t j = 0;
377   for (InstructionBlock::Predecessors::const_iterator i = predecessors_.begin();
378        i != predecessors_.end(); ++i, ++j) {
379     if (*i == rpo_number) break;
380   }
381   return j;
382 }
383
384
385 static BasicBlock::RpoNumber GetRpo(BasicBlock* block) {
386   if (block == NULL) return BasicBlock::RpoNumber::Invalid();
387   return block->GetRpoNumber();
388 }
389
390
391 static BasicBlock::RpoNumber GetLoopEndRpo(const BasicBlock* block) {
392   if (!block->IsLoopHeader()) return BasicBlock::RpoNumber::Invalid();
393   return block->loop_end()->GetRpoNumber();
394 }
395
396
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());
406   }
407   instr_block->predecessors().reserve(block->PredecessorCount());
408   for (BasicBlock* predecessor : block->predecessors()) {
409     instr_block->predecessors().push_back(predecessor->GetRpoNumber());
410   }
411   return instr_block;
412 }
413
414
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);
426   }
427   ComputeAssemblyOrder(blocks);
428   return blocks;
429 }
430
431
432 void InstructionSequence::ComputeAssemblyOrder(InstructionBlocks* blocks) {
433   int ao = 0;
434   for (auto const block : *blocks) {
435     if (!block->IsDeferred()) {
436       block->set_ao_number(BasicBlock::RpoNumber::FromInt(ao++));
437     }
438   }
439   for (auto const block : *blocks) {
440     if (block->IsDeferred()) {
441       block->set_ao_number(BasicBlock::RpoNumber::FromInt(ao++));
442     }
443   }
444 }
445
446
447 InstructionSequence::InstructionSequence(Isolate* isolate,
448                                          Zone* instruction_zone,
449                                          InstructionBlocks* instruction_blocks)
450     : isolate_(isolate),
451       zone_(instruction_zone),
452       instruction_blocks_(instruction_blocks),
453       block_starts_(zone()),
454       constants_(ConstantMap::key_compare(),
455                  ConstantMap::allocator_type(zone())),
456       immediates_(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());
464 }
465
466
467 int InstructionSequence::NextVirtualRegister() {
468   int virtual_register = next_virtual_register_++;
469   CHECK_NE(virtual_register, InstructionOperand::kInvalidVirtualRegister);
470   return virtual_register;
471 }
472
473
474 GapInstruction* InstructionSequence::GetBlockStart(
475     BasicBlock::RpoNumber rpo) const {
476   const InstructionBlock* block = InstructionBlockAt(rpo);
477   return GapInstruction::cast(InstructionAt(block->code_start()));
478 }
479
480
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);
487 }
488
489
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());
496   }
497   DCHECK(block->code_start() >= 0 && block->code_start() < end);
498   block->set_code_end(end);
499 }
500
501
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);
513   }
514   return index;
515 }
516
517
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());
528   return block;
529 }
530
531
532 bool InstructionSequence::IsReference(int virtual_register) const {
533   return references_.find(virtual_register) != references_.end();
534 }
535
536
537 bool InstructionSequence::IsDouble(int virtual_register) const {
538   return doubles_.find(virtual_register) != doubles_.end();
539 }
540
541
542 void InstructionSequence::MarkAsReference(int virtual_register) {
543   references_.insert(virtual_register);
544 }
545
546
547 void InstructionSequence::MarkAsDouble(int virtual_register) {
548   doubles_.insert(virtual_register);
549 }
550
551
552 void InstructionSequence::AddGapMove(int index, InstructionOperand* from,
553                                      InstructionOperand* to) {
554   GapAt(index)->GetOrCreateParallelMove(GapInstruction::START, zone())->AddMove(
555       from, to, zone());
556 }
557
558
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);
564 }
565
566 FrameStateDescriptor* InstructionSequence::GetFrameStateDescriptor(
567     InstructionSequence::StateId state_id) {
568   return deoptimization_entries_[state_id.ToInt()];
569 }
570
571
572 int InstructionSequence::GetFrameStateDescriptorCount() {
573   return static_cast<int>(deoptimization_entries_.size());
574 }
575
576
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),
586       types_(zone),
587       outer_state_(outer_state),
588       jsfunction_(state_info.jsfunction()) {
589   types_.resize(GetSize(), kMachNone);
590 }
591
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();
598       break;
599     case OutputFrameStateCombine::kPokeAt:
600       break;
601   }
602   return size;
603 }
604
605
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();
611   }
612   return total_size;
613 }
614
615
616 size_t FrameStateDescriptor::GetFrameCount() const {
617   size_t count = 0;
618   for (const FrameStateDescriptor* iter = this; iter != NULL;
619        iter = iter->outer_state_) {
620     ++count;
621   }
622   return count;
623 }
624
625
626 size_t FrameStateDescriptor::GetJSFrameCount() const {
627   size_t count = 0;
628   for (const FrameStateDescriptor* iter = this; iter != NULL;
629        iter = iter->outer_state_) {
630     if (iter->type_ == JS_FRAME) {
631       ++count;
632     }
633   }
634   return count;
635 }
636
637
638 MachineType FrameStateDescriptor::GetType(size_t index) const {
639   return types_[index];
640 }
641
642
643 void FrameStateDescriptor::SetType(size_t index, MachineType type) {
644   DCHECK(index < GetSize());
645   types_[index] = type;
646 }
647
648
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";
655   }
656   int i = 0;
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";
660   }
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);
665
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() << ")";
673     }
674     os << "  instructions: [" << block->code_start() << ", "
675        << block->code_end() << ")\n  predecessors:";
676
677     for (auto pred : block->predecessors()) {
678       const InstructionBlock* pred_block = code.InstructionBlockAt(pred);
679       os << " B" << pred_block->id();
680     }
681     os << "\n";
682
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;
690       }
691       os << "\n";
692     }
693
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";
703     }
704
705     for (auto succ : block->successors()) {
706       const InstructionBlock* succ_block = code.InstructionBlockAt(succ);
707       os << " B" << succ_block->id();
708     }
709     os << "\n";
710   }
711   return os;
712 }
713
714 }  // namespace compiler
715 }  // namespace internal
716 }  // namespace v8