v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
instruction.cc
Go to the documentation of this file.
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
6
7#include <cstddef>
8#include <iomanip>
9
10#include "src/base/iterator.h"
19#include "src/compiler/node.h"
30#include "src/utils/ostreams.h"
31
32#if V8_ENABLE_WEBASSEMBLY
33#include "src/wasm/value-type.h"
34#endif // V8_ENABLE_WEBASSEMBLY
35
36namespace v8 {
37namespace internal {
38namespace compiler {
39
41
91
93 const bool combine_fp_aliasing = kFPAliasing == AliasingKind::kCombine &&
94 this->IsFPLocationOperand() &&
95 other.IsFPLocationOperand();
96 const bool stack_slots = this->IsAnyStackSlot() && other.IsAnyStackSlot();
97 if (!combine_fp_aliasing && !stack_slots) {
98 return EqualsCanonicalized(other);
99 }
100 const LocationOperand& loc = *LocationOperand::cast(this);
101 const LocationOperand& other_loc = LocationOperand::cast(other);
103 MachineRepresentation other_rep = other_loc.representation();
105 LocationOperand::LocationKind other_kind = other_loc.location_kind();
106 if (kind != other_kind) return false;
107
108 if (combine_fp_aliasing && !stack_slots) {
109 if (rep == other_rep) return EqualsCanonicalized(other);
111 // FP register-register interference.
112 return GetRegConfig()->AreAliases(rep, loc.register_code(), other_rep,
113 other_loc.register_code());
114 }
115
117 int num_slots =
119 int num_slots_other =
121 const bool complex_stack_slot_interference =
122 (num_slots > 1 || num_slots_other > 1);
123 if (!complex_stack_slot_interference) {
124 return EqualsCanonicalized(other);
125 }
126
127 // Complex multi-slot operand interference:
128 // - slots of different FP reps can alias because the gap resolver may break a
129 // move into 2 or 4 equivalent smaller moves,
130 // - stack layout can be rearranged for tail calls
132 int index_hi = loc.index();
133 int index_lo =
134 index_hi -
136 int other_index_hi = other_loc.index();
137 int other_index_lo =
138 other_index_hi -
140 return other_index_hi >= index_lo && index_hi >= other_index_lo;
141}
142
144 if (IsRegister() || IsStackSlot()) {
145 return op->IsRegister() || op->IsStackSlot();
146 } else if (kFPAliasing != AliasingKind::kCombine) {
147 // A backend may choose to generate the same instruction sequence regardless
148 // of the FP representation. As a result, we can relax the compatibility and
149 // allow a Double to be moved in a Float for example. However, this is only
150 // allowed if registers do not overlap.
151 return (IsFPRegister() || IsFPStackSlot()) &&
152 (op->IsFPRegister() || op->IsFPStackSlot());
153 } else if (IsFloatRegister() || IsFloatStackSlot()) {
154 return op->IsFloatRegister() || op->IsFloatStackSlot();
155 } else if (IsDoubleRegister() || IsDoubleStackSlot()) {
156 return op->IsDoubleRegister() || op->IsDoubleStackSlot();
157 } else {
158 return (IsSimd128Register() || IsSimd128StackSlot()) &&
159 (op->IsSimd128Register() || op->IsSimd128StackSlot());
160 }
161}
162
163void InstructionOperand::Print() const { StdoutStream{} << *this << std::endl; }
164
165std::ostream& operator<<(std::ostream& os, const InstructionOperand& op) {
166 switch (op.kind()) {
168 const UnallocatedOperand* unalloc = UnallocatedOperand::cast(&op);
169 os << "v" << unalloc->virtual_register();
171 return os << "(=" << unalloc->fixed_slot_index() << "S)";
172 }
173 switch (unalloc->extended_policy()) {
175 return os;
177 return os << "(="
179 << ")";
181 return os << "(="
182 << (unalloc->IsSimd128Register()
183 ? i::RegisterName((Simd128Register::from_code(
184 unalloc->fixed_register_index())))
185 : i::RegisterName(DoubleRegister::from_code(
186 unalloc->fixed_register_index())))
187 << ")";
189 return os << "(R)";
191 return os << "(S)";
193 return os << "(" << unalloc->input_index() << ")";
195 return os << "(-)";
197 return os << "(*)";
198 }
199 }
201 return os << "[constant:v" << ConstantOperand::cast(op).virtual_register()
202 << "]";
204 ImmediateOperand imm = ImmediateOperand::cast(op);
205 switch (imm.type()) {
207 return os << "#" << imm.inline_int32_value();
209 return os << "#" << imm.inline_int64_value();
211 return os << "[rpo_immediate:" << imm.indexed_value() << "]";
213 return os << "[immediate:" << imm.indexed_value() << "]";
214 }
215 }
217 return os << "[pending: " << PendingOperand::cast(op).next() << "]";
220 if (op.IsStackSlot()) {
221 os << "[stack:" << allocated.index();
222 } else if (op.IsFPStackSlot()) {
223 os << "[fp_stack:" << allocated.index();
224 } else if (op.IsRegister()) {
225 const char* name =
226 allocated.register_code() < Register::kNumRegisters
227 ? RegisterName(Register::from_code(allocated.register_code()))
228 : Register::GetSpecialRegisterName(allocated.register_code());
229 os << "[" << name << "|R";
230 } else if (op.IsDoubleRegister()) {
231 os << "[" << DoubleRegister::from_code(allocated.register_code())
232 << "|R";
233 } else if (op.IsFloatRegister()) {
234 os << "[" << FloatRegister::from_code(allocated.register_code())
235 << "|R";
236#if V8_TARGET_ARCH_X64
237 } else if (op.IsSimd256Register()) {
238 os << "[" << Simd256Register::from_code(allocated.register_code())
239 << "|R";
240#endif // V8_TARGET_ARCH_X64
241 } else {
243 os << "[" << Simd128Register::from_code(allocated.register_code())
244 << "|R";
245 }
246 switch (allocated.representation()) {
248 os << "|-";
249 break;
251 os << "|b";
252 break;
254 os << "|w8";
255 break;
257 os << "|w16";
258 break;
260 os << "|w32";
261 break;
263 os << "|w64";
264 break;
266 os << "|f16";
267 break;
269 os << "|f32";
270 break;
272 os << "|f64";
273 break;
275 os << "|s128";
276 break;
278 os << "|s256";
279 break;
281 os << "|ts";
282 break;
284 os << "|tp";
285 break;
287 os << "|t";
288 break;
290 os << "|cp";
291 break;
293 os << "|c";
294 break;
296 os << "|pp";
297 break;
299 os << "|ip";
300 break;
302 os << "|sb";
303 break;
306 UNREACHABLE();
307 }
308 return os << "]";
309 }
311 return os << "(x)";
312 }
313 UNREACHABLE();
314}
315
317 StdoutStream{} << destination() << " = " << source() << std::endl;
318}
319
320std::ostream& operator<<(std::ostream& os, const MoveOperands& mo) {
321 os << mo.destination();
322 if (!mo.source().Equals(mo.destination())) {
323 os << " = " << mo.source();
324 }
325 return os;
326}
327
329 for (MoveOperands* move : *this) {
330 if (!move->IsRedundant()) return false;
331 }
332 return true;
333}
334
336 MoveOperands* move, ZoneVector<MoveOperands*>* to_eliminate) const {
337 bool no_aliasing = kFPAliasing != AliasingKind::kCombine ||
338 !move->destination().IsFPLocationOperand();
339 MoveOperands* replacement = nullptr;
340 MoveOperands* eliminated = nullptr;
341 for (MoveOperands* curr : *this) {
342 if (curr->IsEliminated()) continue;
343 if (curr->destination().EqualsCanonicalized(move->source())) {
344 // We must replace move's source with curr's destination in order to
345 // insert it into this ParallelMove.
346 DCHECK(!replacement);
347 replacement = curr;
348 if (no_aliasing && eliminated != nullptr) break;
349 } else if (curr->destination().InterferesWith(move->destination())) {
350 // We can eliminate curr, since move overwrites at least a part of its
351 // destination, implying its value is no longer live.
352 eliminated = curr;
353 to_eliminate->push_back(curr);
354 if (no_aliasing && replacement != nullptr) break;
355 }
356 }
357 if (replacement != nullptr) move->set_source(replacement->source());
358}
359
360bool ParallelMove::Equals(const ParallelMove& that) const {
361 if (this->size() != that.size()) return false;
362 for (size_t i = 0; i < this->size(); ++i) {
363 if (!(*this)[i]->Equals(*that[i])) return false;
364 }
365 return true;
366}
367
369 for (MoveOperands* move : *this) {
370 move->Eliminate();
371 }
372}
373
375 : opcode_(opcode),
376 bit_field_(OutputCountField::encode(0) | InputCountField::encode(0) |
377 TempCountField::encode(0) | IsCallField::encode(false)),
378 reference_map_(nullptr),
379 block_(nullptr) {
380 parallel_moves_[0] = nullptr;
381 parallel_moves_[1] = nullptr;
382
383 // PendingOperands are required to be 8 byte aligned.
384 static_assert(offsetof(Instruction, operands_) % 8 == 0);
385}
386
387Instruction::Instruction(InstructionCode opcode, size_t output_count,
388 InstructionOperand* outputs, size_t input_count,
389 InstructionOperand* inputs, size_t temp_count,
390 InstructionOperand* temps)
391 : opcode_(opcode),
392 bit_field_(OutputCountField::encode(output_count) |
393 InputCountField::encode(input_count) |
394 TempCountField::encode(temp_count) |
395 IsCallField::encode(false)),
396 reference_map_(nullptr),
397 block_(nullptr) {
398 parallel_moves_[0] = nullptr;
399 parallel_moves_[1] = nullptr;
400 size_t offset = 0;
401 for (size_t i = 0; i < output_count; ++i) {
402 DCHECK(!outputs[i].IsInvalid());
403 operands_[offset++] = outputs[i];
404 }
405 for (size_t i = 0; i < input_count; ++i) {
406 DCHECK(!inputs[i].IsInvalid());
407 operands_[offset++] = inputs[i];
408 }
409 for (size_t i = 0; i < temp_count; ++i) {
410 DCHECK(!temps[i].IsInvalid());
411 operands_[offset++] = temps[i];
412 }
413}
414
418 if (parallel_moves_[i] != nullptr && !parallel_moves_[i]->IsRedundant()) {
419 return false;
420 }
421 }
422 return true;
423}
424
425void Instruction::Print() const { StdoutStream{} << *this << std::endl; }
426
427std::ostream& operator<<(std::ostream& os, const ParallelMove& pm) {
428 const char* delimiter = "";
429 for (MoveOperands* move : pm) {
430 if (move->IsEliminated()) continue;
431 os << delimiter << *move;
432 delimiter = "; ";
433 }
434 return os;
435}
436
438 // Do not record arguments as pointers.
439 if (op.IsStackSlot() && LocationOperand::cast(op).index() < 0) return;
440 DCHECK(!op.IsFPRegister() && !op.IsFPStackSlot());
442}
443
444std::ostream& operator<<(std::ostream& os, const ReferenceMap& pm) {
445 os << "{";
446 const char* separator = "";
447 for (const InstructionOperand& op : pm.reference_operands_) {
448 os << separator << op;
449 separator = ";";
450 }
451 return os << "}";
452}
453
454std::ostream& operator<<(std::ostream& os, const ArchOpcode& ao) {
455 switch (ao) {
456#define CASE(Name) \
457 case k##Name: \
458 return os << #Name;
460#undef CASE
461 }
462 UNREACHABLE();
463}
464
465std::ostream& operator<<(std::ostream& os, const AddressingMode& am) {
466 switch (am) {
467 case kMode_None:
468 return os;
469#define CASE(Name) \
470 case kMode_##Name: \
471 return os << #Name;
473#undef CASE
474 }
475 UNREACHABLE();
476}
477
478std::ostream& operator<<(std::ostream& os, const FlagsMode& fm) {
479 switch (fm) {
480 case kFlags_none:
481 return os;
482 case kFlags_branch:
483 return os << "branch";
485 return os << "deoptimize";
486 case kFlags_set:
487 return os << "set";
488 case kFlags_trap:
489 return os << "trap";
490 case kFlags_select:
491 return os << "select";
493 return os << "conditional set";
495 return os << "conditional branch";
496 }
497 UNREACHABLE();
498}
499
500std::ostream& operator<<(std::ostream& os, const FlagsCondition& fc) {
501 switch (fc) {
502 case kEqual:
503 return os << "equal";
504 case kNotEqual:
505 return os << "not equal";
506 case kSignedLessThan:
507 return os << "signed less than";
509 return os << "signed greater than or equal";
511 return os << "signed less than or equal";
513 return os << "signed greater than";
515 return os << "unsigned less than";
517 return os << "unsigned greater than or equal";
519 return os << "unsigned less than or equal";
521 return os << "unsigned greater than";
523 return os << "less than or unordered (FP)";
525 return os << "greater than or equal (FP)";
527 return os << "less than or equal (FP)";
529 return os << "greater than or unordered (FP)";
530 case kFloatLessThan:
531 return os << "less than (FP)";
533 return os << "greater than, equal or unordered (FP)";
535 return os << "less than, equal or unordered (FP)";
537 return os << "greater than (FP)";
538 case kUnorderedEqual:
539 return os << "unordered equal";
541 return os << "unordered not equal";
542 case kOverflow:
543 return os << "overflow";
544 case kNotOverflow:
545 return os << "not overflow";
546 case kPositiveOrZero:
547 return os << "positive or zero";
548 case kNegative:
549 return os << "negative";
550 case kIsNaN:
551 return os << "is nan";
552 case kIsNotNaN:
553 return os << "is not nan";
554 }
555 UNREACHABLE();
556}
557
558std::ostream& operator<<(std::ostream& os, const Instruction& instr) {
559 os << "gap ";
562 os << "(";
563 if (instr.parallel_moves()[i] != nullptr) {
564 os << *instr.parallel_moves()[i];
565 }
566 os << ") ";
567 }
568 os << "\n ";
569
570 if (instr.OutputCount() == 1) {
571 os << *instr.OutputAt(0) << " = ";
572 } else if (instr.OutputCount() > 1) {
573 os << "(" << *instr.OutputAt(0);
574 for (size_t i = 1; i < instr.OutputCount(); i++) {
575 os << ", " << *instr.OutputAt(i);
576 }
577 os << ") = ";
578 }
579
582 if (am != kMode_None) {
583 os << " : " << AddressingModeField::decode(instr.opcode());
584 }
586 if (fm != kFlags_none) {
587 os << " && " << fm << " if " << FlagsConditionField::decode(instr.opcode());
588 }
589 for (size_t i = 0; i < instr.InputCount(); i++) {
590 os << " " << *instr.InputAt(i);
591 }
592 return os;
593}
594
596
598 if (info.type() == RelocatablePtrConstantInfo::kInt32) {
599 type_ = kInt32;
600 } else if (info.type() == RelocatablePtrConstantInfo::kInt64) {
601 type_ = kInt64;
602 } else {
603 UNREACHABLE();
604 }
605 value_ = info.value();
606 rmode_ = info.rmode();
607}
608
612 reinterpret_cast<Address*>(static_cast<intptr_t>(value_)));
613 return value;
614}
615
619 reinterpret_cast<Address*>(static_cast<intptr_t>(value_)));
620 DCHECK(IsCode(*value));
621 return value;
622}
623
624std::ostream& operator<<(std::ostream& os, const Constant& constant) {
625 switch (constant.type()) {
626 case Constant::kInt32:
627 return os << constant.ToInt32();
628 case Constant::kInt64:
629 return os << constant.ToInt64() << "l";
631 return os << constant.ToFloat32() << "f";
633 return os << constant.ToFloat64().value();
635 return os << constant.ToExternalReference();
636 case Constant::kHeapObject: // Fall through.
638 return os << Brief(*constant.ToHeapObject());
640 return os << "RPO" << constant.ToRpoNumber().ToInt();
641 }
642 UNREACHABLE();
643}
644
645PhiInstruction::PhiInstruction(Zone* zone, int virtual_register,
646 size_t input_count)
647 : virtual_register_(virtual_register),
649 operands_(input_count, InstructionOperand::kInvalidVirtualRegister,
650 zone) {}
651
656
661
663 RpoNumber loop_header, RpoNumber loop_end,
664 RpoNumber dominator, bool deferred,
665 bool handler)
666 : successors_(zone),
667 predecessors_(zone),
668 phis_(zone),
669 ao_number_(RpoNumber::Invalid()),
670 rpo_number_(rpo_number),
671 loop_header_(loop_header),
672 loop_end_(loop_end),
673 dominator_(dominator),
674 deferred_(deferred),
675 handler_(handler),
676 switch_target_(false),
677 code_target_alignment_(false),
678 loop_header_alignment_(false),
679 needs_frame_(!v8_flags.turbo_elide_frames),
680 must_construct_frame_(false),
681 must_deconstruct_frame_(false),
682 omitted_by_jump_threading_(false) {}
683
685 size_t j = 0;
687 i != predecessors_.end(); ++i, ++j) {
688 if (*i == rpo_number) break;
689 }
690 return j;
691}
692
693static RpoNumber GetRpo(const BasicBlock* block) {
694 if (block == nullptr) return RpoNumber::Invalid();
695 return RpoNumber::FromInt(block->rpo_number());
696}
697
698static RpoNumber GetRpo(const turboshaft::Block* block) {
699 if (block == nullptr) return RpoNumber::Invalid();
700 return RpoNumber::FromInt(block->index().id());
701}
702
703static RpoNumber GetLoopEndRpo(const BasicBlock* block) {
704 if (!block->IsLoopHeader()) return RpoNumber::Invalid();
705 return RpoNumber::FromInt(block->loop_end()->rpo_number());
706}
707
709 if (!block->IsLoop()) return RpoNumber::Invalid();
710 // In Turbofan, the `block->loop_end()` refers to the first after (outside)
711 // the loop. In the relevant use cases, we retrieve the backedge block by
712 // subtracting one from the rpo_number, so for Turboshaft we "fake" this by
713 // adding 1 to the backedge block's rpo_number.
714 return RpoNumber::FromInt(GetRpo(block->LastPredecessor()).ToInt() + 1);
715}
716
718 const BasicBlock* block) {
719 bool is_handler =
720 !block->empty() && block->front()->opcode() == IrOpcode::kIfException;
721 InstructionBlock* instr_block = zone->New<InstructionBlock>(
722 zone, GetRpo(block), GetRpo(block->loop_header()), GetLoopEndRpo(block),
723 GetRpo(block->dominator()), block->deferred(), is_handler);
724 // Map successors and predecessors
725 instr_block->successors().reserve(block->SuccessorCount());
726 for (BasicBlock* successor : block->successors()) {
727 instr_block->successors().push_back(GetRpo(successor));
728 }
729 instr_block->predecessors().reserve(block->PredecessorCount());
730 for (BasicBlock* predecessor : block->predecessors()) {
731 instr_block->predecessors().push_back(GetRpo(predecessor));
732 }
733 if (block->PredecessorCount() == 1 &&
734 block->predecessors()[0]->control() == BasicBlock::Control::kSwitch) {
735 instr_block->set_switch_target(true);
736 }
737 return instr_block;
738}
739
741 Zone* zone, const turboshaft::Graph& graph, const turboshaft::Block* block,
742 const turboshaft::Block* loop_header) {
743 bool is_handler =
744 block->FirstOperation(graph).Is<turboshaft::CatchBlockBeginOp>();
745 bool deferred = block->get_custom_data(
747 InstructionBlock* instr_block = zone->New<InstructionBlock>(
748 zone, GetRpo(block), GetRpo(loop_header), GetLoopEndRpo(block),
749 GetRpo(block->GetDominator()), deferred, is_handler);
750 if (block->PredecessorCount() == 1) {
751 const turboshaft::Block* predecessor = block->LastPredecessor();
752 if (V8_UNLIKELY(
753 predecessor->LastOperation(graph).Is<turboshaft::SwitchOp>())) {
754 instr_block->set_switch_target(true);
755 }
756 }
757 // Map successors and predecessors.
759 turboshaft::SuccessorBlocks(block->LastOperation(graph));
760 instr_block->successors().reserve(succs.size());
761 for (const turboshaft::Block* successor : succs) {
762 instr_block->successors().push_back(GetRpo(successor));
763 }
764 instr_block->predecessors().reserve(block->PredecessorCount());
765 for (const turboshaft::Block* predecessor = block->LastPredecessor();
766 predecessor; predecessor = predecessor->NeighboringPredecessor()) {
767 instr_block->predecessors().push_back(GetRpo(predecessor));
768 }
769 std::reverse(instr_block->predecessors().begin(),
770 instr_block->predecessors().end());
771 return instr_block;
772}
773
774std::ostream& operator<<(std::ostream& os,
775 const PrintableInstructionBlock& printable_block) {
776 const InstructionBlock* block = printable_block.block_;
777 const InstructionSequence* code = printable_block.code_;
778
779 os << "B" << block->rpo_number();
780 if (block->ao_number().IsValid()) {
781 os << ": AO#" << block->ao_number();
782 } else {
783 os << ": AO#?";
784 }
785 if (block->IsDeferred()) os << " (deferred)";
786 if (!block->needs_frame()) os << " (no frame)";
787 if (block->must_construct_frame()) os << " (construct frame)";
788 if (block->must_deconstruct_frame()) os << " (deconstruct frame)";
789 if (block->IsLoopHeader()) {
790 os << " loop blocks: [" << block->rpo_number() << ", " << block->loop_end()
791 << ")";
792 }
793 os << " instructions: [" << block->code_start() << ", " << block->code_end()
794 << ")" << std::endl
795 << " predecessors:";
796
797 for (RpoNumber pred : block->predecessors()) {
798 os << " B" << pred.ToInt();
799 }
800 os << std::endl;
801
802 for (const PhiInstruction* phi : block->phis()) {
803 os << " phi: " << phi->output() << " =";
804 for (int input : phi->operands()) {
805 os << " v" << input;
806 }
807 os << std::endl;
808 }
809
810 for (int j = block->first_instruction_index();
811 j <= block->last_instruction_index(); j++) {
812 os << " " << std::setw(5) << j << ": " << *code->InstructionAt(j)
813 << std::endl;
814 }
815
816 os << " successors:";
817 for (RpoNumber succ : block->successors()) {
818 os << " B" << succ.ToInt();
819 }
820 os << std::endl;
821 return os;
822}
823
825 Zone* zone, const Schedule* schedule) {
827 new (blocks) InstructionBlocks(
828 static_cast<int>(schedule->rpo_order()->size()), nullptr, zone);
829 size_t rpo_number = 0;
830 for (BasicBlockVector::const_iterator it = schedule->rpo_order()->begin();
831 it != schedule->rpo_order()->end(); ++it, ++rpo_number) {
832 DCHECK(!(*blocks)[rpo_number]);
833 DCHECK_EQ(GetRpo(*it).ToSize(), rpo_number);
834 (*blocks)[rpo_number] = InstructionBlockFor(zone, *it);
835 }
836 return blocks;
837}
838
840 Zone* zone, const turboshaft::Graph& graph) {
842 new (blocks)
843 InstructionBlocks(static_cast<int>(graph.block_count()), nullptr, zone);
844 size_t rpo_number = 0;
845 // TODO(dmercadier): currently, the LoopFinder is just used to compute loop
846 // headers. Since it's somewhat expensive to compute this, we should also use
847 // the LoopFinder to compute the special RPO (we would only need to run the
848 // LoopFinder once to compute both the special RPO and the loop headers).
849 turboshaft::LoopFinder loop_finder(zone, &graph);
850 for (const turboshaft::Block& block : graph.blocks()) {
851 DCHECK(!(*blocks)[rpo_number]);
852 DCHECK_EQ(RpoNumber::FromInt(block.index().id()).ToSize(), rpo_number);
853 (*blocks)[rpo_number] = InstructionBlockFor(
854 zone, graph, &block, loop_finder.GetLoopHeader(&block));
855 ++rpo_number;
856 }
857 return blocks;
858}
859
861 // Validate blocks are in edge-split form: no block with multiple successors
862 // has an edge to a block (== a successor) with more than one predecessors.
863 for (const InstructionBlock* block : instruction_blocks()) {
864 if (block->SuccessorCount() > 1) {
865 for (const RpoNumber& successor_id : block->successors()) {
866 const InstructionBlock* successor = InstructionBlockAt(successor_id);
867 // Expect precisely one predecessor: "block".
868 CHECK(successor->PredecessorCount() == 1 &&
869 successor->predecessors()[0] == block->rpo_number());
870 }
871 }
872 }
873}
874
876 // A deferred block with more than one successor must have all its successors
877 // deferred.
878 for (const InstructionBlock* block : instruction_blocks()) {
879 if (!block->IsDeferred() || block->SuccessorCount() <= 1) continue;
880 for (RpoNumber successor_id : block->successors()) {
881 CHECK(InstructionBlockAt(successor_id)->IsDeferred());
882 }
883 }
884}
885
887 // If a deferred block has multiple predecessors, they have to
888 // all be deferred. Otherwise, we can run into a situation where a range
889 // that spills only in deferred blocks inserts its spill in the block, but
890 // other ranges need moves inserted by ResolveControlFlow in the predecessors,
891 // which may clobber the register of this range.
892 for (const InstructionBlock* block : instruction_blocks()) {
893 if (!block->IsDeferred() || block->PredecessorCount() <= 1) continue;
894 for (RpoNumber predecessor_id : block->predecessors()) {
895 CHECK(InstructionBlockAt(predecessor_id)->IsDeferred());
896 }
897 }
898}
899
901 // TODO(mtrofin): We could use a local zone here instead.
902 BitVector definitions(VirtualRegisterCount(), zone());
903 for (const Instruction* instruction : *this) {
904 for (size_t i = 0; i < instruction->OutputCount(); ++i) {
905 const InstructionOperand* output = instruction->OutputAt(i);
906 int vreg = (output->IsConstant())
907 ? ConstantOperand::cast(output)->virtual_register()
908 : UnallocatedOperand::cast(output)->virtual_register();
909 CHECK(!definitions.Contains(vreg));
910 definitions.Add(vreg);
911 }
912 }
913}
914
916 int ao = 0;
917 RpoNumber invalid = RpoNumber::Invalid();
918
922
923 // Place non-deferred blocks.
924 for (InstructionBlock* const block : *instruction_blocks_) {
925 DCHECK_NOT_NULL(block);
926 if (block->IsDeferred()) continue; // skip deferred blocks.
927 if (block->ao_number() != invalid) continue; // loop rotated.
928 if (block->IsLoopHeader()) {
929 bool header_align = true;
930 if (v8_flags.turbo_loop_rotation) {
931 // Perform loop rotation for non-deferred loops.
932 InstructionBlock* loop_end =
933 instruction_blocks_->at(block->loop_end().ToSize() - 1);
934 if (!loop_end->IsDeferred() && /* Ignore deferred loop ends */
935 loop_end->SuccessorCount() == 1 && /* ends with goto */
936 loop_end != block /* not a degenerate infinite loop */) {
937 // If the last block has an unconditional jump back to the header,
938 // then move it to be in front of the header in the assembly order.
939 DCHECK_EQ(block->rpo_number(), loop_end->successors()[0]);
940 loop_end->set_ao_number(RpoNumber::FromInt(ao++));
941 ao_blocks_->push_back(loop_end);
942 // This block will be the new machine-level loop header, so align
943 // this block instead of the loop header block.
944 loop_end->set_loop_header_alignment(true);
945 header_align = false;
946 }
947 }
948 block->set_loop_header_alignment(header_align);
949 }
950 if (block->loop_header().IsValid() && block->IsSwitchTarget()) {
951 block->set_code_target_alignment(true);
952 }
953 block->set_ao_number(RpoNumber::FromInt(ao++));
954 ao_blocks_->push_back(block);
955 }
956 // Add all leftover (deferred) blocks.
957 for (InstructionBlock* const block : *instruction_blocks_) {
958 if (block->ao_number() == invalid) {
959 block->set_ao_number(RpoNumber::FromInt(ao++));
960 ao_blocks_->push_back(block);
961 }
962 }
964}
965
967 RpoNumber invalid = RpoNumber::Invalid();
968 for (InstructionBlock* block : *instruction_blocks_) {
969 block->set_ao_number(invalid);
970 }
972}
973
975 Zone* instruction_zone,
976 InstructionBlocks* instruction_blocks)
977 : isolate_(isolate),
978 zone_(instruction_zone),
979 instruction_blocks_(instruction_blocks),
980 ao_blocks_(nullptr),
981 // Pre-allocate the hash map of source positions based on the block count.
982 // (The actual number of instructions is only known after instruction
983 // selection, but should at least correlate with the block count.)
984 source_positions_(zone(), instruction_blocks->size() * 2),
985 // Avoid collisions for functions with 256 or less constant vregs.
986 constants_(zone(), 256),
987 immediates_(zone()),
988 rpo_immediates_(instruction_blocks->size(), zone()),
989 instructions_(zone()),
990 next_virtual_register_(0),
991 reference_maps_(zone()),
992 representations_(zone()),
993 representation_mask_(0),
994 deoptimization_entries_(zone()),
995 current_block_(nullptr) {
997}
998
1000 int virtual_register = next_virtual_register_++;
1002 return virtual_register;
1003}
1004
1006 const InstructionBlock* block = InstructionBlockAt(rpo);
1007 return InstructionAt(block->code_start());
1008}
1009
1013 int code_start = static_cast<int>(instructions_.size());
1014 current_block_->set_code_start(code_start);
1015}
1016
1018 int end = static_cast<int>(instructions_.size());
1023 current_block_ = nullptr;
1024}
1025
1028 int index = static_cast<int>(instructions_.size());
1029 instr->set_block(current_block_);
1031 if (instr->NeedsReferenceMap()) {
1032 DCHECK_NULL(instr->reference_map());
1033 ReferenceMap* reference_map = zone()->New<ReferenceMap>(zone());
1034 reference_map->set_instruction_position(index);
1035 instr->set_reference_map(reference_map);
1036 reference_maps_.push_back(reference_map);
1037 }
1038 return index;
1039}
1040
1070
1072 int virtual_register) const {
1073 DCHECK_LE(0, virtual_register);
1074 DCHECK_LT(virtual_register, VirtualRegisterCount());
1075 if (virtual_register >= static_cast<int>(representations_.size())) {
1076 return DefaultRepresentation();
1077 }
1078 return representations_[virtual_register];
1079}
1080
1082 int virtual_register) {
1083 DCHECK_LE(0, virtual_register);
1084 DCHECK_LT(virtual_register, VirtualRegisterCount());
1085 if (virtual_register >= static_cast<int>(representations_.size())) {
1087 }
1088 rep = FilterRepresentation(rep);
1089 DCHECK_IMPLIES(representations_[virtual_register] != rep,
1090 representations_[virtual_register] == DefaultRepresentation());
1091 representations_[virtual_register] = rep;
1093}
1094
1097 DeoptimizeReason reason, NodeId node_id, FeedbackSource const& feedback) {
1098 int deoptimization_id = static_cast<int>(deoptimization_entries_.size());
1100 DeoptimizationEntry(descriptor, kind, reason, node_id, feedback));
1101 return deoptimization_id;
1102}
1103
1105 int state_id) {
1106 return deoptimization_entries_[state_id];
1107}
1108
1110 InstructionOperand* operand = instr->InputAt(index);
1111 Constant constant =
1112 operand->IsImmediate()
1113 ? GetImmediate(ImmediateOperand::cast(operand))
1114 : GetConstant(ConstantOperand::cast(operand)->virtual_register());
1115 return constant.ToRpoNumber();
1116}
1117
1119 SourcePosition* result) const {
1120 auto it = source_positions_.find(instr);
1121 if (it == source_positions_.end()) return false;
1122 *result = it->second;
1123 return true;
1124}
1125
1127 SourcePosition value) {
1128 source_positions_.insert(std::make_pair(instr, value));
1129}
1130
1132 StdoutStream{} << *this << std::endl;
1133}
1134
1135void InstructionSequence::PrintBlock(int block_id) const {
1136 RpoNumber rpo = RpoNumber::FromInt(block_id);
1137 const InstructionBlock* block = InstructionBlockAt(rpo);
1138 CHECK(block->rpo_number() == rpo);
1139 StdoutStream{} << PrintableInstructionBlock{block, this} << std::endl;
1140}
1141
1144
1150
1156
1157namespace {
1158
1159size_t GetConservativeFrameSizeInBytes(FrameStateType type,
1160 size_t parameters_count,
1161 size_t locals_count,
1162 BytecodeOffset bailout_id,
1163 uint32_t wasm_liftoff_frame_size) {
1164 switch (type) {
1167 static_cast<int>(parameters_count), static_cast<int>(locals_count));
1168 return info.frame_size_in_bytes();
1169 }
1171 // The inlined extra arguments frame state is only used in the deoptimizer
1172 // and does not occupy any extra space in the stack.
1173 // Check out the design doc:
1174 // https://docs.google.com/document/d/150wGaUREaZI6YWqOQFD5l2mWQXaPbbZjcAIJLOFrzMs/edit
1175 // We just need to account for the additional parameters we might push
1176 // here.
1178 static_cast<int>(parameters_count));
1179#if V8_ENABLE_WEBASSEMBLY
1180 case FrameStateType::kWasmInlinedIntoJS:
1181#endif
1184 static_cast<int>(parameters_count));
1185 return info.frame_size_in_bytes();
1186 }
1190#if V8_ENABLE_WEBASSEMBLY
1191 case FrameStateType::kJSToWasmBuiltinContinuation:
1192#endif // V8_ENABLE_WEBASSEMBLY
1195 const RegisterConfiguration* config = RegisterConfiguration::Default();
1197 static_cast<int>(parameters_count),
1200 config);
1201 return info.frame_size_in_bytes();
1202 }
1203#if V8_ENABLE_WEBASSEMBLY
1204 case FrameStateType::kLiftoffFunction:
1205 return wasm_liftoff_frame_size;
1206#endif // V8_ENABLE_WEBASSEMBLY
1207 }
1208 UNREACHABLE();
1209}
1210
1211size_t GetTotalConservativeFrameSizeInBytes(FrameStateType type,
1212 size_t parameters_count,
1213 size_t locals_count,
1214 BytecodeOffset bailout_id,
1215 uint32_t wasm_liftoff_frame_size,
1216 FrameStateDescriptor* outer_state) {
1217 size_t outer_total_conservative_frame_size_in_bytes =
1218 (outer_state == nullptr)
1219 ? 0
1220 : outer_state->total_conservative_frame_size_in_bytes();
1221 return GetConservativeFrameSizeInBytes(type, parameters_count, locals_count,
1222 bailout_id, wasm_liftoff_frame_size) +
1223 outer_total_conservative_frame_size_in_bytes;
1224}
1225
1226} // namespace
1227
1229 Zone* zone, FrameStateType type, BytecodeOffset bailout_id,
1230 OutputFrameStateCombine state_combine, uint16_t parameters_count,
1231 uint16_t max_arguments, size_t locals_count, size_t stack_count,
1234 FrameStateDescriptor* outer_state, uint32_t wasm_liftoff_frame_size,
1235 uint32_t wasm_function_index)
1236 : type_(type),
1237 bailout_id_(bailout_id),
1238 frame_state_combine_(state_combine),
1239 parameters_count_(parameters_count),
1240 max_arguments_(max_arguments),
1241 locals_count_(locals_count),
1242 stack_count_(stack_count),
1243 total_conservative_frame_size_in_bytes_(
1244 GetTotalConservativeFrameSizeInBytes(
1245 type, parameters_count, locals_count, bailout_id,
1246 wasm_liftoff_frame_size, outer_state)),
1247 values_(zone),
1248 shared_info_(shared_info),
1249 bytecode_array_(bytecode_array),
1250 outer_state_(outer_state),
1251 wasm_function_index_(wasm_function_index) {}
1252
1254 switch (type()) {
1256 return locals_count(); // The accumulator is *not* included.
1258#if V8_ENABLE_WEBASSEMBLY
1259 case FrameStateType::kJSToWasmBuiltinContinuation:
1260 case FrameStateType::kWasmInlinedIntoJS:
1261#endif
1262 // Custom, non-JS calling convention (that does not have a notion of
1263 // a receiver or context).
1264 return parameters_count();
1270 // JS linkage. The parameters count
1271 // - includes the receiver (input 1 in CreateArtificialFrameState, and
1272 // passed as part of stack parameters to
1273 // CreateJavaScriptBuiltinContinuationFrameState), and
1274 // - does *not* include the context.
1275 return parameters_count();
1276#if V8_ENABLE_WEBASSEMBLY
1277 case FrameStateType::kLiftoffFunction:
1278 return locals_count() + parameters_count();
1279#endif
1280 }
1281 UNREACHABLE();
1282}
1283
1285 return (HasClosure() ? 1 : 0) + parameters_count() + locals_count() +
1286 stack_count() + (HasContext() ? 1 : 0);
1287}
1288
1290 size_t total_size = 0;
1291 for (const FrameStateDescriptor* iter = this; iter != nullptr;
1292 iter = iter->outer_state_) {
1293 total_size += iter->GetSize();
1294 }
1295 return total_size;
1296}
1297
1299 size_t count = 0;
1300 for (const FrameStateDescriptor* iter = this; iter != nullptr;
1301 iter = iter->outer_state_) {
1302 ++count;
1303 }
1304 return count;
1305}
1306
1308 size_t count = 0;
1309 for (const FrameStateDescriptor* iter = this; iter != nullptr;
1310 iter = iter->outer_state_) {
1312 ++count;
1313 }
1314 }
1315 return count;
1316}
1317
1318#if V8_ENABLE_WEBASSEMBLY
1319JSToWasmFrameStateDescriptor::JSToWasmFrameStateDescriptor(
1320 Zone* zone, FrameStateType type, BytecodeOffset bailout_id,
1321 OutputFrameStateCombine state_combine, uint16_t parameters_count,
1322 size_t locals_count, size_t stack_count,
1324 FrameStateDescriptor* outer_state, const wasm::CanonicalSig* wasm_signature)
1325 : FrameStateDescriptor(zone, type, bailout_id, state_combine,
1326 parameters_count, 0, locals_count, stack_count,
1327 shared_info, {}, outer_state),
1328 return_kind_(wasm::WasmReturnTypeFromSignature(wasm_signature)) {}
1329#endif // V8_ENABLE_WEBASSEMBLY
1330
1331std::ostream& operator<<(std::ostream& os, const RpoNumber& rpo) {
1332 return os << rpo.ToSize();
1333}
1334
1335std::ostream& operator<<(std::ostream& os, const InstructionSequence& code) {
1336 for (size_t i = 0; i < code.immediates_.size(); ++i) {
1337 Constant constant = code.immediates_[i];
1338 os << "IMM#" << i << ": " << constant << "\n";
1339 }
1340 int n = 0;
1341 for (ConstantMap::const_iterator it = code.constants_.begin();
1342 it != code.constants_.end(); ++n, ++it) {
1343 os << "CST#" << n << ": v" << it->first << " = " << it->second << "\n";
1344 }
1345 for (int i = 0; i < code.InstructionBlockCount(); i++) {
1346 auto* block = code.InstructionBlockAt(RpoNumber::FromInt(i));
1347 os << PrintableInstructionBlock{block, &code};
1348 }
1349 return os;
1350}
1351
1352std::ostream& operator<<(std::ostream& os, StateValueKind kind) {
1353 switch (kind) {
1355 return os << "ArgumentsElements";
1357 return os << "ArgumentsLength";
1359 return os << "RestLength";
1361 return os << "Plain";
1363 return os << "OptimizedOut";
1365 return os << "NestedObject";
1367 return os << "Duplicate";
1369 return os << "StringConcat";
1370 }
1371}
1372
1373void StateValueDescriptor::Print(std::ostream& os) const {
1374 os << "kind=" << kind_ << ", type=" << type_;
1377 os << ", id=" << id_;
1379 os << ", args_type=" << args_type_;
1380 }
1381}
1382
1383} // namespace compiler
1384} // namespace internal
1385} // namespace v8
Schedule * schedule
Isolate * isolate_
uint32_t bit_field_
Builtins::Kind kind
Definition builtins.cc:40
static constexpr T decode(U value)
Definition bit-field.h:66
size_t size() const
bool Contains(int i) const
Definition bit-vector.h:180
static BuiltinContinuationFrameInfo Conservative(int parameters_count, const CallInterfaceDescriptor &continuation_descriptor, const RegisterConfiguration *register_config)
Definition frames.h:2008
static CallInterfaceDescriptor CallInterfaceDescriptorFor(Builtin builtin)
Definition builtins.cc:189
static Builtin GetBuiltinFromBytecodeOffset(BytecodeOffset)
Definition builtins.cc:104
static ConstructStubFrameInfo Conservative(int parameters_count)
Definition frames.h:1948
static FastConstructStubFrameInfo Conservative()
Definition frames.h:1971
static constexpr QwNeonRegister from_code(int8_t code)
static const RegisterConfiguration * Default()
static constexpr Register from_code(int code)
static const char * GetSpecialRegisterName(int code)
static uint32_t GetStackSizeForAdditionalArguments(int parameters_count)
Definition frames.cc:4237
static UnoptimizedFrameInfo Conservative(int parameters_count_with_receiver, int locals_count)
Definition frames.h:1915
static constexpr YMMRegister from_code(int code)
void reserve(size_t new_cap)
void resize(size_t new_size)
void push_back(const T &value)
T * AllocateArray(size_t length)
Definition zone.h:127
T * New(Args &&... args)
Definition zone.h:114
BasicBlockVector & successors()
Definition schedule.h:82
BasicBlockVector & predecessors()
Definition schedule.h:73
IndirectHandle< HeapObject > ToHeapObject() const
IndirectHandle< Code > ToCode() const
FrameStateDescriptor *const outer_state_
FrameStateDescriptor(Zone *zone, FrameStateType type, BytecodeOffset bailout_id, OutputFrameStateCombine state_combine, uint16_t parameters_count, uint16_t max_arguments, size_t locals_count, size_t stack_count, MaybeIndirectHandle< SharedFunctionInfo > shared_info, MaybeIndirectHandle< BytecodeArray > bytecode_array, FrameStateDescriptor *outer_state=nullptr, uint32_t wasm_liftoff_frame_size=std::numeric_limits< uint32_t >::max(), uint32_t wasm_function_index=std::numeric_limits< uint32_t >::max())
static bool IsJSFunctionType(FrameStateType type)
size_t PredecessorIndexOf(RpoNumber rpo_number) const
void set_ao_number(RpoNumber ao_number)
InstructionBlock(Zone *zone, RpoNumber rpo_number, RpoNumber loop_header, RpoNumber loop_end, RpoNumber dominator, bool deferred, bool handler)
bool EqualsCanonicalized(const InstructionOperand &that) const
bool InterferesWith(const InstructionOperand &other) const
DeoptimizationEntry const & GetDeoptimizationEntry(int deoptimization_id)
Constant GetImmediate(const ImmediateOperand *op) const
RpoNumber InputRpo(Instruction *instr, size_t index)
static const RegisterConfiguration * registerConfigurationForTesting_
Instruction * InstructionAt(int index) const
int AddDeoptimizationEntry(FrameStateDescriptor *descriptor, DeoptimizeKind kind, DeoptimizeReason reason, NodeId node_id, FeedbackSource const &feedback)
Constant GetConstant(int virtual_register) const
ZoneVector< MachineRepresentation > representations_
InstructionBlocks *const instruction_blocks_
static InstructionBlocks * InstructionBlocksFor(Zone *zone, const Schedule *schedule)
Instruction * GetBlockStart(RpoNumber rpo) const
bool GetSourcePosition(const Instruction *instr, SourcePosition *result) const
MachineRepresentation GetRepresentation(int virtual_register) const
static const RegisterConfiguration * RegisterConfigurationForTesting()
static void SetRegisterConfigurationForTesting(const RegisterConfiguration *regConfig)
InstructionSequence(Isolate *isolate, Zone *zone, InstructionBlocks *instruction_blocks)
void MarkAsRepresentation(MachineRepresentation rep, int virtual_register)
void SetSourcePosition(const Instruction *instr, SourcePosition value)
static MachineRepresentation DefaultRepresentation()
const InstructionBlocks & instruction_blocks() const
InstructionBlock * InstructionBlockAt(RpoNumber rpo_number)
const InstructionOperand * OutputAt(size_t i) const
InstructionCode opcode() const
const InstructionOperand * InputAt(size_t i) const
ParallelMove *const * parallel_moves() const
Instruction(const Instruction &)=delete
MachineRepresentation representation() const
bool IsCompatible(LocationOperand *op)
static LocationOperand * cast(InstructionOperand *op)
const InstructionOperand & source() const
const InstructionOperand & destination() const
void set_source(const InstructionOperand &operand)
void PrepareInsertAfter(MoveOperands *move, ZoneVector< MoveOperands * > *to_eliminate) const
bool Equals(const ParallelMove &that) const
void RenameInput(size_t offset, int virtual_register)
PhiInstruction(Zone *zone, int virtual_register, size_t input_count)
void SetInput(size_t offset, int virtual_register)
ZoneVector< InstructionOperand > reference_operands_
void RecordReference(const AllocatedOperand &op)
static RpoNumber FromInt(int index)
const Operation & LastOperation(const Graph &graph) const
Definition graph.h:1242
const Block * GetLoopHeader(const Block *block) const
Definition loop-finder.h:71
Zone * zone_
Register const value_
const ObjectRef type_
uint32_t count
#define TARGET_ADDRESSING_MODE_LIST(V)
#define ARCH_OPCODE_LIST(V)
ArchOpcode opcode_
int32_t offset
Instruction * instr
RpoNumber block
ZoneVector< RpoNumber > & result
Comparator::Output * output_
BasicBlock * current_block_
base::SmallVector< int32_t, 1 > stack_slots
base::SmallVector< Block *, 4 > SuccessorBlocks(const Block &block, const Graph &graph)
static RpoNumber GetLoopEndRpo(const BasicBlock *block)
static MachineRepresentation FilterRepresentation(MachineRepresentation rep)
ZoneVector< InstructionBlock * > InstructionBlocks
FlagsCondition CommuteFlagsCondition(FlagsCondition condition)
static RpoNumber GetRpo(const BasicBlock *block)
static InstructionBlock * InstructionBlockFor(Zone *zone, const BasicBlock *block)
std::ostream & operator<<(std::ostream &os, AccessMode access_mode)
const RegisterConfiguration *(* GetRegConfig)()
std::optional< wasm::ValueKind > WasmReturnTypeFromSignature(const CanonicalSig *wasm_signature)
constexpr AliasingKind kFPAliasing
V8_EXPORT_PRIVATE constexpr int RepresentationBit(MachineRepresentation rep)
V8_EXPORT_PRIVATE FlagValues v8_flags
return value
Definition map-inl.h:893
V8_EXPORT_PRIVATE constexpr int ElementSizeInBytes(MachineRepresentation)
#define DCHECK_LE(v1, v2)
Definition logging.h:490
#define DCHECK_NULL(val)
Definition logging.h:491
#define CHECK(condition)
Definition logging.h:124
#define DCHECK_NOT_NULL(val)
Definition logging.h:492
#define DCHECK_IMPLIES(v1, v2)
Definition logging.h:493
#define DCHECK_NE(v1, v2)
Definition logging.h:486
#define CHECK_NE(lhs, rhs)
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_LT(v1, v2)
Definition logging.h:489
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
EmbedderRootsHandler * handler_
#define V8_UNLIKELY(condition)
Definition v8config.h:660