19 if (v8_flags.trace_turbo_escape) PrintF(__VA_ARGS__); \
35 if (
id >=
map_.size()) {
51 auto iter =
map_.find(node->id());
52 if (iter !=
map_.end()) {
53 iter->second = std::move(value);
55 map_.insert(iter, std::make_pair(node->id(), std::move(value)));
59 auto iter =
map_.find(node->id());
107 CHECK(var != Variable::Invalid());
111 CHECK(var != Variable::Invalid());
136 Node* node = current_state_.Get(var);
137 if (node && node->opcode() == IrOpcode::kDead) {
143 return Nothing<Node*>();
188 VirtualObject* vobject = tracker_->virtual_objects_.Get(node);
194 DCHECK_EQ(IrOpcode::kAllocate, current_node()->opcode());
195 VirtualObject* vobject = tracker_->virtual_objects_.Get(current_node());
199 vobject = tracker_->NewVirtualObject(size);
207 vobject_ = tracker_->virtual_objects_.Get(
object);
211 if (
VirtualObject*
object = tracker_->virtual_objects_.Get(node)) {
212 if (object->HasEscaped())
return;
213 TRACE(
"Setting %s#%d to escaped because of use by %s#%d\n",
214 node->op()->mnemonic(), node->id(),
215 current_node()->op()->mnemonic(), current_node()->
id());
216 object->SetEscaped();
217 object->RevisitDependants(
reducer_);
223 return tracker_->ResolveReplacement(
224 NodeProperties::GetValueInput(current_node(),
i));
227 return tracker_->ResolveReplacement(
228 NodeProperties::GetContextInput(current_node()));
232 DCHECK_EQ(current_node()->opcode(), IrOpcode::kFrameState);
233 return current_node();
237 replacement_ = replacement;
239 replacement ? tracker_->virtual_objects_.Get(replacement) :
nullptr;
241 TRACE(
"Set %s#%d as replacement.\n", replacement->
op()->mnemonic(),
244 TRACE(
"Set nullptr as replacement.\n");
252 if (
auto it = tracker_->framestate_might_lazy_deopt_.find(framestate);
253 it != tracker_->framestate_might_lazy_deopt_.end()) {
256 for (
Node* use : framestate->
uses()) {
257 switch (use->opcode()) {
258 case IrOpcode::kCheckpoint:
259 case IrOpcode::kDeoptimize:
260 case IrOpcode::kDeoptimizeIf:
261 case IrOpcode::kDeoptimizeUnless:
265 if (use->opcode() == IrOpcode::kFrameState &&
266 !FrameStateMightLazyDeopt(use)) {
269 return tracker_->framestate_might_lazy_deopt_[framestate] =
true;
272 return tracker_->framestate_might_lazy_deopt_[framestate] =
false;
276 if (replacement_ != tracker_->replacements_[current_node()] ||
277 vobject_ != tracker_->virtual_objects_.Get(current_node())) {
278 reduction()->set_value_changed();
280 tracker_->replacements_[current_node()] = replacement_;
281 tracker_->virtual_objects_.Set(current_node(), vobject_);
324 state_(graph, kNumStates),
327 reduce_(
std::move(reduce)),
328 tick_counter_(tick_counter) {}
339 int& input_index =
stack_.top().input_index;
340 if (input_index < current->InputCount()) {
343 switch (
state_.Get(input)) {
362 for (
Edge edge : current->use_edges()) {
364 Node* use = edge.from();
366 if (reduction.effect_changed())
Revisit(use);
368 if (reduction.value_changed())
Revisit(use);
379 stack_.push({revisit, 0});
389 TRACE(
" Queueing for revisit: %s#%d\n", node->op()->mnemonic(),
403 tick_counter_(reducer->tick_counter()) {}
409 current_state_(states->
zone_) {
410 switch (node->opcode()) {
411 case IrOpcode::kEffectPhi:
412 current_state_ = states_->MergeInputs(node);
415 int effect_inputs = node->op()->EffectInputCount();
416 if (effect_inputs == 1) {
418 states_->table_.Get(NodeProperties::GetEffectInput(node, 0));
420 DCHECK_EQ(0, effect_inputs);
443 int arity = effect_phi->
op()->EffectInputCount();
445 TRACE(
"control: %s#%d\n", control->
op()->mnemonic(), control->
id());
446 bool is_loop = control->
opcode() == IrOpcode::kLoop;
451 for (std::pair<Variable, Node*> var_value : first_input) {
453 if (
Node* value = var_value.second) {
458 bool identical_inputs =
true;
459 int num_defined_inputs = 1;
460 TRACE(
" input 0: %s#%d\n", value->op()->mnemonic(), value->id());
461 for (
int i = 1;
i < arity; ++
i) {
464 if (next_value != value) identical_inputs =
false;
465 if (next_value !=
nullptr) {
466 num_defined_inputs++;
467 TRACE(
" input %i: %s#%d\n",
i, next_value->
op()->mnemonic(),
470 TRACE(
" input %i: nullptr\n",
i);
475 Node* old_value =
table_.Get(effect_phi).Get(var);
477 TRACE(
" old: %s#%d\n", old_value->
op()->mnemonic(), old_value->
id());
479 TRACE(
" old: nullptr\n");
482 if (old_value && old_value->
opcode() == IrOpcode::kPhi &&
487 for (
int i = 0;
i < arity; ++
i) {
490 if (old_input != new_input) {
495 result.Set(var, old_value);
497 if (num_defined_inputs == 1 && is_loop) {
503 }
else if (num_defined_inputs < arity) {
510 if (identical_inputs) {
513 TRACE(
"Creating new phi\n");
529 TRACE(
" result: %s#%d\n", result_node->op()->mnemonic(),
532 TRACE(
" result: nullptr\n");
542int OffsetOfFieldAccess(
const Operator* op) {
544 op->
opcode() == IrOpcode::kStoreField);
546 return access.offset;
549Maybe<int> OffsetOfElementAt(ElementAccess
const& access,
int index) {
552 if (representation == MachineRepresentation::kFloat64)
return Nothing<int>();
556 return Just(access.header_size +
560Maybe<int> OffsetOfElementsAccess(
const Operator* op, Node* index_node) {
561 DCHECK(op->opcode() == IrOpcode::kLoadElement ||
562 op->opcode() == IrOpcode::kStoreElement);
563 Type index_type = NodeProperties::GetType(index_node);
564 if (!index_type.Is(Type::OrderedNumber()))
return Nothing<int>();
565 double max = index_type.Max();
566 double min = index_type.Min();
567 int index =
static_cast<int>(min);
568 if (index < 0 || index != min || index != max)
return Nothing<int>();
569 return OffsetOfElementAt(ElementAccessOf(op), index);
572Node* LowerCompareMapsWithoutLoad(Node* checked_map,
573 ZoneRefSet<Map>
const& checked_against,
575 Node* true_node =
jsgraph->TrueConstant();
576 Node* false_node =
jsgraph->FalseConstant();
577 Node* replacement = false_node;
578 for (MapRef map : checked_against) {
583 Node* map_node =
jsgraph->HeapConstantMaybeHole(map.object());
585 NodeProperties::SetType(map_node, Type::Internal());
586 Node* comparison =
jsgraph->graph()->NewNode(
587 jsgraph->simplified()->ReferenceEqual(), checked_map, map_node);
588 NodeProperties::SetType(comparison, Type::Boolean());
589 if (replacement == false_node) {
590 replacement = comparison;
592 replacement =
jsgraph->graph()->NewNode(
593 jsgraph->common()->Select(MachineRepresentation::kTaggedPointer),
594 comparison, true_node, replacement);
595 NodeProperties::SetType(replacement, Type::Boolean());
602bool CheckMapsHelper(EscapeAnalysisTracker::Scope* current, Node* checked,
603 ZoneRefSet<Map> target) {
604 const VirtualObject* vobject = current->GetVirtualObject(checked);
607 if (vobject && !vobject->HasEscaped() &&
608 vobject->FieldAt(HeapObject::kMapOffset).To(&map_field) &&
609 current->Get(map_field).To(&map)) {
611 Type
const map_type = NodeProperties::GetType(map);
612 if (map_type.IsHeapConstant() &&
613 target.contains(map_type.AsHeapConstant()->Ref().AsMap())) {
614 current->MarkForDeletion();
627void ReduceNode(
const Operator* op, EscapeAnalysisTracker::Scope* current,
629 switch (op->opcode()) {
630 case IrOpcode::kAllocate: {
632 if (!size.HasResolvedValue())
break;
633 int size_int =
static_cast<int>(size.ResolvedValue());
634 if (size_int != size.ResolvedValue())
break;
635 if (
const VirtualObject* vobject = current->InitVirtualObject(size_int)) {
637 for (Variable field : *vobject) {
638 current->Set(field,
jsgraph->Dead());
643 case IrOpcode::kFinishRegion:
644 current->SetVirtualObject(current->ValueInput(0));
646 case IrOpcode::kStoreField: {
647 Node*
object = current->ValueInput(0);
648 Node* value = current->ValueInput(1);
649 const VirtualObject* vobject = current->GetVirtualObject(
object);
651 if (value->opcode() == IrOpcode::kTrustedHeapConstant) {
657 current->SetEscaped(
object);
658 current->SetEscaped(value);
663 if (vobject && !vobject->HasEscaped() &&
664 vobject->FieldAt(OffsetOfFieldAccess(op)).To(&var) &&
666 current->Set(var, value);
667 current->MarkForDeletion();
669 current->SetEscaped(
object);
670 current->SetEscaped(value);
674 case IrOpcode::kStoreElement: {
675 Node*
object = current->ValueInput(0);
676 Node* index = current->ValueInput(1);
677 Node* value = current->ValueInput(2);
678 const VirtualObject* vobject = current->GetVirtualObject(
object);
681 if (vobject && !vobject->HasEscaped() &&
682 OffsetOfElementsAccess(op, index).To(&
offset) &&
683 vobject->FieldAt(
offset).To(&var)) {
684 current->Set(var, value);
685 current->MarkForDeletion();
687 current->SetEscaped(value);
688 current->SetEscaped(
object);
692 case IrOpcode::kLoadField: {
693 Node*
object = current->ValueInput(0);
694 const VirtualObject* vobject = current->GetVirtualObject(
object);
697 if (vobject && !vobject->HasEscaped() &&
698 vobject->FieldAt(OffsetOfFieldAccess(op)).To(&var) &&
699 current->Get(var).To(&value)) {
700 current->SetReplacement(value);
702 current->SetEscaped(
object);
706 case IrOpcode::kLoadElement: {
707 Node*
object = current->ValueInput(0);
708 Node* index = current->ValueInput(1);
709 const VirtualObject* vobject = current->GetVirtualObject(
object);
713 if (vobject && !vobject->HasEscaped() &&
714 OffsetOfElementsAccess(op, index).To(&
offset) &&
715 vobject->FieldAt(
offset).To(&var) && current->Get(var).To(&value)) {
716 current->SetReplacement(value);
718 }
else if (vobject && !vobject->HasEscaped()) {
723 (vobject->size() - access.header_size) >>
729 vobject->FieldAt(OffsetOfElementAt(access, 0)).To(&var) &&
730 current->Get(var).To(&value) &&
732 NodeProperties::GetType(value).Is(access.type))) {
736 current->SetReplacement(value);
738 }
else if (length == 2 &&
739 vobject->FieldAt(OffsetOfElementAt(access, 0)).To(&var0) &&
740 current->Get(var0).To(&value0) &&
741 (value0 ==
nullptr ||
742 NodeProperties::GetType(value0).Is(access.type)) &&
743 vobject->FieldAt(OffsetOfElementAt(access, 1)).To(&var1) &&
744 current->Get(var1).To(&value1) &&
745 (value1 ==
nullptr ||
746 NodeProperties::GetType(value1).Is(access.type))) {
747 if (value0 && value1) {
756 index,
jsgraph->ZeroConstant());
757 NodeProperties::SetType(check, Type::Boolean());
758 Node* select =
jsgraph->graph()->NewNode(
759 jsgraph->common()->Select(access.machine_type.representation()),
760 check, value0, value1);
761 NodeProperties::SetType(select, access.type);
762 current->SetReplacement(select);
763 current->SetEscaped(value0);
764 current->SetEscaped(value1);
773 current->SetEscaped(
object);
776 case IrOpcode::kTypeGuard: {
777 current->SetVirtualObject(current->ValueInput(0));
780 case IrOpcode::kReferenceEqual: {
781 Node* left = current->ValueInput(0);
782 Node* right = current->ValueInput(1);
783 const VirtualObject* left_object = current->GetVirtualObject(left);
784 const VirtualObject* right_object = current->GetVirtualObject(right);
785 Node* replacement =
nullptr;
786 if (left_object && !left_object->HasEscaped()) {
787 if (right_object && !right_object->HasEscaped() &&
788 left_object->id() == right_object->id()) {
789 replacement =
jsgraph->TrueConstant();
791 replacement =
jsgraph->FalseConstant();
793 }
else if (right_object && !right_object->HasEscaped()) {
794 replacement =
jsgraph->FalseConstant();
802 if (replacement && !NodeProperties::GetType(left).
IsNone() &&
803 !NodeProperties::GetType(right).
IsNone()) {
804 current->SetReplacement(replacement);
807 current->SetEscaped(left);
808 current->SetEscaped(right);
811 case IrOpcode::kCheckMaps: {
813 Node* checked = current->ValueInput(0);
814 if (CheckMapsHelper(current, checked, params.maps())) {
817 current->SetEscaped(checked);
820 case IrOpcode::kTransitionElementsKindOrCheckMap: {
821 ElementsTransitionWithMultipleSources params =
823 Node* checked = current->ValueInput(0);
824 if (CheckMapsHelper(current, checked, ZoneRefSet<Map>(params.target()))) {
827 current->SetEscaped(checked);
830 case IrOpcode::kCompareMaps: {
831 Node*
object = current->ValueInput(0);
832 const VirtualObject* vobject = current->GetVirtualObject(
object);
835 if (vobject && !vobject->HasEscaped() &&
836 vobject->FieldAt(HeapObject::kMapOffset).To(&map_field) &&
837 current->Get(map_field).To(&object_map)) {
839 current->SetReplacement(LowerCompareMapsWithoutLoad(
848 current->SetEscaped(
object);
851 case IrOpcode::kCheckHeapObject: {
852 Node* checked = current->ValueInput(0);
853 switch (checked->opcode()) {
854 case IrOpcode::kAllocate:
855 case IrOpcode::kFinishRegion:
856 case IrOpcode::kHeapConstant:
857 current->SetReplacement(checked);
860 current->SetEscaped(checked);
865 case IrOpcode::kMapGuard: {
866 Node*
object = current->ValueInput(0);
867 const VirtualObject* vobject = current->GetVirtualObject(
object);
868 if (vobject && !vobject->HasEscaped()) {
869 current->MarkForDeletion();
873 case IrOpcode::kStateValues:
876 current->SetValueChanged();
878 case IrOpcode::kFrameState: {
881 FrameState frame_state{current->CurrentNode()};
885 if (type != FrameStateType::kUnoptimizedFunction &&
886 type != FrameStateType::kJavaScriptBuiltinContinuation &&
887 type != FrameStateType::kJavaScriptBuiltinContinuationWithCatch) {
890 if (!current->FrameStateMightLazyDeopt(current->CurrentNode())) {
894 StateValuesAccess::iterator it =
895 StateValuesAccess(frame_state.parameters()).begin();
900 current->SetEscaped(frame_state.function());
906 int value_input_count = op->ValueInputCount();
907 for (
int i = 0;
i < value_input_count; ++
i) {
908 Node* input = current->ValueInput(
i);
909 current->SetEscaped(input);
911 if (OperatorProperties::HasContextInput(op)) {
912 current->SetEscaped(current->ContextInput());
926 ReduceNode(op, ¤t,
jsgraph());
948 int field,
Node* effect) {
959 :
Dependable(var_states->zone()), id_(id), fields_(var_states->zone()) {
961 TRACE(
"Creating VirtualObject id:%d size:%d\n",
id, size);
964 for (
int i = 0;
i < num_fields; ++
i) {
void TickAndMaybeEnterSafepoint()
void reserve(size_t new_cap)
void push_back(const T &value)
const Operator * Phi(MachineRepresentation representation, int value_input_count)
void AddDependency(Node *node)
void set_effect_changed()
NodeMarker< State > state_
ZoneStack< NodeState > stack_
std::function< void(Node *, Reduction *)> reduce_
ZoneStack< Node * > revisit_
void ReduceFrom(Node *node)
EffectGraphReducer(TFGraph *graph, std::function< void(Node *, Reduction *)> reduce, TickCounter *tick_counter, Zone *zone)
TickCounter *const tick_counter_
Node * GetVirtualObjectField(const VirtualObject *vobject, int field, Node *effect)
const VirtualObject * GetVirtualObject(Node *node)
Node * GetReplacementOf(Node *node)
EscapeAnalysisTracker * tracker_
Scope(EffectGraphReducer *reducer, EscapeAnalysisTracker *tracker, Node *node, Reduction *reduction)
bool FrameStateMightLazyDeopt(Node *framestate)
void SetVirtualObject(Node *object)
const VirtualObject * InitVirtualObject(int size)
const VirtualObject * GetVirtualObject(Node *node)
void SetEscaped(Node *node)
EscapeAnalysisTracker * tracker_
void SetReplacement(Node *replacement)
EffectGraphReducer * reducer_
SparseSidetable< VirtualObject * > virtual_objects_
static constexpr int kTrackingBudget
Sidetable< Node * > replacements_
VariableTracker variable_states_
EscapeAnalysisTracker & operator=(const EscapeAnalysisTracker &)=delete
Node * ResolveReplacement(Node *node)
ZoneUnorderedMap< Node *, bool > framestate_might_lazy_deopt_
EscapeAnalysisTracker(const EscapeAnalysisTracker &)=delete
Node * GetReplacementOf(Node *node)
VirtualObject::Id next_object_id_
int number_of_tracked_bytes_
VirtualObject * NewVirtualObject(int size)
EscapeAnalysisTracker(JSGraph *jsgraph, EffectGraphReducer *reducer, Zone *zone)
void Reduce(Node *node, Reduction *reduction)
EscapeAnalysis(JSGraph *jsgraph, TickCounter *tick_counter, Zone *zone)
CommonOperatorBuilder * common() const
static Node * GetEffectInput(Node *node, int index=0)
static Node * GetValueInput(Node *node, int index)
static bool IsEffectEdge(Edge edge)
static void SetType(Node *node, Type type)
static void ReplaceValueInput(Node *node, Node *value, int index)
static Node * GetControlInput(Node *node, int index=0)
constexpr IrOpcode::Value opcode() const
const Operator * op() const
Node * InputAt(int index) const
const char * mnemonic() const
constexpr Opcode opcode() const
const Value & Get(const Key &key) const
void Set(Key key, Value value)
ReduceScope(Node *node, Reduction *reduction)
Node * current_node() const
T & operator[](const Node *node)
ZoneUnorderedMap< NodeId, T > map_
void Set(const Node *node, T value)
const T & Get(const Node *node) const
SparseSidetable(Zone *zone, T def_value=T())
Node * NewNode(const Operator *op, int input_count, Node *const *inputs, bool incomplete=false)
Scope(VariableTracker *tracker, Node *node, Reduction *reduction)
VariableTracker * states_
Maybe< Node * > Get(Variable var)
void Set(Variable var, Node *node)
Map::iterator end() const
Node * Get(Variable var) const
void Set(Variable var, Node *node)
Map::iterator begin() const
bool operator!=(const State &other) const
ZoneVector< Node * > buffer_
State MergeInputs(Node *effect_phi)
VariableTracker & operator=(const VariableTracker &)=delete
TickCounter *const tick_counter_
VariableTracker(const VariableTracker &)=delete
Node * Get(Variable var, Node *effect)
EffectGraphReducer * reducer_
VariableTracker(JSGraph *graph, EffectGraphReducer *reducer, Zone *zone)
SparseSidetable< State > table_
Maybe< Variable > FieldAt(int offset) const
VirtualObject(VariableTracker *var_states, Id id, int size)
ZoneVector< Variable > fields_
base::OwnedVector< uint8_t > buffer_
enum v8::internal::@1270::DeoptimizableCodeIterator::@67 state_
ZoneVector< RpoNumber > & result
SnapshotTable< OpIndex, VariableData >::Key Variable
CheckMapsParameters const & CheckMapsParametersOf(Operator const *op)
const FieldAccess & FieldAccessOf(const Operator *op)
const ElementAccess & ElementAccessOf(const Operator *op)
FloatMatcher< double, IrOpcode::kNumberConstant > NumberMatcher
ZoneRefSet< Map > const & CompareMapsParametersOf(Operator const *op)
ElementsTransitionWithMultipleSources const & ElementsTransitionWithMultipleSourcesOf(const Operator *op)
constexpr int kTaggedSize
bool IsNone(Tagged< FieldType > obj)
V8_EXPORT_PRIVATE constexpr int ElementSizeLog2Of(MachineRepresentation)
Maybe< T > Just(const T &t)
SourcePositionTable *const table_
#define DCHECK_GE(v1, v2)
#define DCHECK(condition)
#define DCHECK_EQ(v1, v2)
constexpr bool IsAligned(T value, U alignment)
std::unique_ptr< ValueMirror > value