• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2022 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 #ifndef V8_MAGLEV_MAGLEV_INTERPRETER_FRAME_STATE_H_
6 #define V8_MAGLEV_MAGLEV_INTERPRETER_FRAME_STATE_H_
7 
8 #include "src/base/logging.h"
9 #include "src/base/threaded-list.h"
10 #include "src/compiler/bytecode-analysis.h"
11 #include "src/compiler/bytecode-liveness-map.h"
12 #include "src/interpreter/bytecode-register.h"
13 #include "src/maglev/maglev-compilation-unit.h"
14 #include "src/maglev/maglev-ir.h"
15 #include "src/maglev/maglev-regalloc-data.h"
16 #include "src/maglev/maglev-register-frame-array.h"
17 #include "src/zone/zone.h"
18 
19 namespace v8 {
20 namespace internal {
21 namespace maglev {
22 
23 class BasicBlock;
24 class MergePointInterpreterFrameState;
25 
26 class InterpreterFrameState {
27  public:
InterpreterFrameState(const MaglevCompilationUnit & info)28   explicit InterpreterFrameState(const MaglevCompilationUnit& info)
29       : frame_(info) {}
30 
InterpreterFrameState(const MaglevCompilationUnit & info,const InterpreterFrameState & state)31   InterpreterFrameState(const MaglevCompilationUnit& info,
32                         const InterpreterFrameState& state)
33       : frame_(info) {
34     frame_.CopyFrom(info, state.frame_, nullptr);
35   }
36 
CopyFrom(const MaglevCompilationUnit & info,const InterpreterFrameState & state)37   void CopyFrom(const MaglevCompilationUnit& info,
38                 const InterpreterFrameState& state) {
39     frame_.CopyFrom(info, state.frame_, nullptr);
40   }
41 
42   inline void CopyFrom(const MaglevCompilationUnit& info,
43                        const MergePointInterpreterFrameState& state);
44 
set_accumulator(ValueNode * value)45   void set_accumulator(ValueNode* value) {
46     frame_[interpreter::Register::virtual_accumulator()] = value;
47   }
accumulator()48   ValueNode* accumulator() const {
49     return frame_[interpreter::Register::virtual_accumulator()];
50   }
51 
set(interpreter::Register reg,ValueNode * value)52   void set(interpreter::Register reg, ValueNode* value) {
53     DCHECK_IMPLIES(reg.is_parameter(),
54                    reg == interpreter::Register::current_context() ||
55                        reg == interpreter::Register::function_closure() ||
56                        reg == interpreter::Register::virtual_accumulator() ||
57                        reg.ToParameterIndex() >= 0);
58     frame_[reg] = value;
59   }
get(interpreter::Register reg)60   ValueNode* get(interpreter::Register reg) const {
61     DCHECK_IMPLIES(reg.is_parameter(),
62                    reg == interpreter::Register::current_context() ||
63                        reg == interpreter::Register::function_closure() ||
64                        reg == interpreter::Register::virtual_accumulator() ||
65                        reg.ToParameterIndex() >= 0);
66     return frame_[reg];
67   }
68 
frame()69   const RegisterFrameArray<ValueNode*>& frame() const { return frame_; }
70 
71  private:
72   RegisterFrameArray<ValueNode*> frame_;
73 };
74 
75 class CompactInterpreterFrameState {
76  public:
CompactInterpreterFrameState(const MaglevCompilationUnit & info,const compiler::BytecodeLivenessState * liveness)77   CompactInterpreterFrameState(const MaglevCompilationUnit& info,
78                                const compiler::BytecodeLivenessState* liveness)
79       : live_registers_and_accumulator_(
80             info.zone()->NewArray<ValueNode*>(SizeFor(info, liveness))),
81         liveness_(liveness) {}
82 
CompactInterpreterFrameState(const MaglevCompilationUnit & info,const compiler::BytecodeLivenessState * liveness,const InterpreterFrameState & state)83   CompactInterpreterFrameState(const MaglevCompilationUnit& info,
84                                const compiler::BytecodeLivenessState* liveness,
85                                const InterpreterFrameState& state)
86       : CompactInterpreterFrameState(info, liveness) {
87     ForEachValue(info, [&](ValueNode*& entry, interpreter::Register reg) {
88       entry = state.get(reg);
89     });
90   }
91 
92   CompactInterpreterFrameState(const CompactInterpreterFrameState&) = delete;
93   CompactInterpreterFrameState(CompactInterpreterFrameState&&) = delete;
94   CompactInterpreterFrameState& operator=(const CompactInterpreterFrameState&) =
95       delete;
96   CompactInterpreterFrameState& operator=(CompactInterpreterFrameState&&) =
97       delete;
98 
99   template <typename Function>
ForEachParameter(const MaglevCompilationUnit & info,Function && f)100   void ForEachParameter(const MaglevCompilationUnit& info, Function&& f) const {
101     for (int i = 0; i < info.parameter_count(); i++) {
102       interpreter::Register reg = interpreter::Register::FromParameterIndex(i);
103       f(live_registers_and_accumulator_[i], reg);
104     }
105   }
106 
107   template <typename Function>
ForEachParameter(const MaglevCompilationUnit & info,Function && f)108   void ForEachParameter(const MaglevCompilationUnit& info, Function&& f) {
109     for (int i = 0; i < info.parameter_count(); i++) {
110       interpreter::Register reg = interpreter::Register::FromParameterIndex(i);
111       f(live_registers_and_accumulator_[i], reg);
112     }
113   }
114 
115   template <typename Function>
ForEachLocal(const MaglevCompilationUnit & info,Function && f)116   void ForEachLocal(const MaglevCompilationUnit& info, Function&& f) const {
117     int live_reg = 0;
118     for (int register_index : *liveness_) {
119       interpreter::Register reg = interpreter::Register(register_index);
120       f(live_registers_and_accumulator_[info.parameter_count() + live_reg++],
121         reg);
122     }
123   }
124 
125   template <typename Function>
ForEachLocal(const MaglevCompilationUnit & info,Function && f)126   void ForEachLocal(const MaglevCompilationUnit& info, Function&& f) {
127     int live_reg = 0;
128     for (int register_index : *liveness_) {
129       interpreter::Register reg = interpreter::Register(register_index);
130       f(live_registers_and_accumulator_[info.parameter_count() + live_reg++],
131         reg);
132     }
133   }
134 
135   template <typename Function>
ForEachRegister(const MaglevCompilationUnit & info,Function && f)136   void ForEachRegister(const MaglevCompilationUnit& info, Function&& f) {
137     ForEachParameter(info, f);
138     ForEachLocal(info, f);
139   }
140 
141   template <typename Function>
ForEachRegister(const MaglevCompilationUnit & info,Function && f)142   void ForEachRegister(const MaglevCompilationUnit& info, Function&& f) const {
143     ForEachParameter(info, f);
144     ForEachLocal(info, f);
145   }
146 
147   template <typename Function>
ForEachValue(const MaglevCompilationUnit & info,Function && f)148   void ForEachValue(const MaglevCompilationUnit& info, Function&& f) {
149     ForEachRegister(info, f);
150     if (liveness_->AccumulatorIsLive()) {
151       f(accumulator(info), interpreter::Register::virtual_accumulator());
152     }
153   }
154 
155   template <typename Function>
ForEachValue(const MaglevCompilationUnit & info,Function && f)156   void ForEachValue(const MaglevCompilationUnit& info, Function&& f) const {
157     ForEachRegister(info, f);
158     if (liveness_->AccumulatorIsLive()) {
159       f(accumulator(info), interpreter::Register::virtual_accumulator());
160     }
161   }
162 
liveness()163   const compiler::BytecodeLivenessState* liveness() const { return liveness_; }
164 
accumulator(const MaglevCompilationUnit & info)165   ValueNode*& accumulator(const MaglevCompilationUnit& info) {
166     return live_registers_and_accumulator_[size(info) - 1];
167   }
accumulator(const MaglevCompilationUnit & info)168   ValueNode* accumulator(const MaglevCompilationUnit& info) const {
169     return live_registers_and_accumulator_[size(info) - 1];
170   }
171 
size(const MaglevCompilationUnit & info)172   size_t size(const MaglevCompilationUnit& info) const {
173     return SizeFor(info, liveness_);
174   }
175 
176  private:
SizeFor(const MaglevCompilationUnit & info,const compiler::BytecodeLivenessState * liveness)177   static size_t SizeFor(const MaglevCompilationUnit& info,
178                         const compiler::BytecodeLivenessState* liveness) {
179     return info.parameter_count() + liveness->live_value_count();
180   }
181 
182   ValueNode** const live_registers_and_accumulator_;
183   const compiler::BytecodeLivenessState* const liveness_;
184 };
185 
186 class MergePointRegisterState {
187  public:
188   class Iterator {
189    public:
190     struct Entry {
191       RegisterState& state;
192       Register reg;
193     };
Iterator(RegisterState * value_pointer,RegList::Iterator reg_iterator)194     explicit Iterator(RegisterState* value_pointer,
195                       RegList::Iterator reg_iterator)
196         : current_value_(value_pointer), reg_iterator_(reg_iterator) {}
197     Entry operator*() { return {*current_value_, *reg_iterator_}; }
198     void operator++() {
199       ++current_value_;
200       ++reg_iterator_;
201     }
202     bool operator!=(const Iterator& other) const {
203       return current_value_ != other.current_value_;
204     }
205 
206    private:
207     RegisterState* current_value_;
208     RegList::Iterator reg_iterator_;
209   };
210 
is_initialized()211   bool is_initialized() const { return values_[0].GetPayload().is_initialized; }
212 
begin()213   Iterator begin() {
214     return Iterator(values_, kAllocatableGeneralRegisters.begin());
215   }
end()216   Iterator end() {
217     return Iterator(values_ + kAllocatableGeneralRegisterCount,
218                     kAllocatableGeneralRegisters.end());
219   }
220 
221  private:
222   RegisterState values_[kAllocatableGeneralRegisterCount] = {{}};
223 };
224 
225 class MergePointInterpreterFrameState {
226  public:
227   static constexpr BasicBlock* kDeadPredecessor = nullptr;
228 
CheckIsLoopPhiIfNeeded(const MaglevCompilationUnit & compilation_unit,int merge_offset,interpreter::Register reg,ValueNode * value)229   void CheckIsLoopPhiIfNeeded(const MaglevCompilationUnit& compilation_unit,
230                               int merge_offset, interpreter::Register reg,
231                               ValueNode* value) {
232 #ifdef DEBUG
233     const auto& analysis = compilation_unit.bytecode_analysis();
234     if (!analysis.IsLoopHeader(merge_offset)) return;
235     auto& assignments = analysis.GetLoopInfoFor(merge_offset).assignments();
236     if (reg.is_parameter()) {
237       if (!assignments.ContainsParameter(reg.ToParameterIndex())) return;
238     } else {
239       DCHECK(
240           analysis.GetInLivenessFor(merge_offset)->RegisterIsLive(reg.index()));
241       if (!assignments.ContainsLocal(reg.index())) return;
242     }
243     DCHECK(value->Is<Phi>());
244 #endif
245   }
246 
MergePointInterpreterFrameState(const MaglevCompilationUnit & info,const InterpreterFrameState & state,int merge_offset,int predecessor_count,BasicBlock * predecessor,const compiler::BytecodeLivenessState * liveness)247   MergePointInterpreterFrameState(
248       const MaglevCompilationUnit& info, const InterpreterFrameState& state,
249       int merge_offset, int predecessor_count, BasicBlock* predecessor,
250       const compiler::BytecodeLivenessState* liveness)
251       : predecessor_count_(predecessor_count),
252         predecessors_so_far_(1),
253         predecessors_(info.zone()->NewArray<BasicBlock*>(predecessor_count)),
254         frame_state_(info, liveness, state) {
255     predecessors_[0] = predecessor;
256   }
257 
MergePointInterpreterFrameState(const MaglevCompilationUnit & info,int merge_offset,int predecessor_count,const compiler::BytecodeLivenessState * liveness,const compiler::LoopInfo * loop_info)258   MergePointInterpreterFrameState(
259       const MaglevCompilationUnit& info, int merge_offset,
260       int predecessor_count, const compiler::BytecodeLivenessState* liveness,
261       const compiler::LoopInfo* loop_info)
262       : predecessor_count_(predecessor_count),
263         predecessors_so_far_(1),
264         predecessors_(info.zone()->NewArray<BasicBlock*>(predecessor_count)),
265         frame_state_(info, liveness) {
266     auto& assignments = loop_info->assignments();
267     frame_state_.ForEachParameter(
268         info, [&](ValueNode*& entry, interpreter::Register reg) {
269           entry = nullptr;
270           if (assignments.ContainsParameter(reg.ToParameterIndex())) {
271             entry = NewLoopPhi(info.zone(), reg, merge_offset);
272           }
273         });
274     frame_state_.ForEachLocal(
275         info, [&](ValueNode*& entry, interpreter::Register reg) {
276           entry = nullptr;
277           if (assignments.ContainsLocal(reg.index())) {
278             entry = NewLoopPhi(info.zone(), reg, merge_offset);
279           }
280         });
281     DCHECK(!frame_state_.liveness()->AccumulatorIsLive());
282 
283 #ifdef DEBUG
284     predecessors_[0] = nullptr;
285 #endif
286   }
287 
288   // Merges an unmerged framestate with a possibly merged framestate into |this|
289   // framestate.
Merge(MaglevCompilationUnit & compilation_unit,const InterpreterFrameState & unmerged,BasicBlock * predecessor,int merge_offset)290   void Merge(MaglevCompilationUnit& compilation_unit,
291              const InterpreterFrameState& unmerged, BasicBlock* predecessor,
292              int merge_offset) {
293     DCHECK_GT(predecessor_count_, 1);
294     DCHECK_LT(predecessors_so_far_, predecessor_count_);
295     predecessors_[predecessors_so_far_] = predecessor;
296 
297     frame_state_.ForEachValue(
298         compilation_unit, [&](ValueNode*& value, interpreter::Register reg) {
299           CheckIsLoopPhiIfNeeded(compilation_unit, merge_offset, reg, value);
300 
301           value = MergeValue(compilation_unit, reg, value, unmerged.get(reg),
302                              merge_offset);
303         });
304     predecessors_so_far_++;
305     DCHECK_LE(predecessors_so_far_, predecessor_count_);
306   }
307 
308   // Merges an unmerged framestate with a possibly merged framestate into |this|
309   // framestate.
MergeLoop(const MaglevCompilationUnit & compilation_unit,const InterpreterFrameState & loop_end_state,BasicBlock * loop_end_block,int merge_offset)310   void MergeLoop(const MaglevCompilationUnit& compilation_unit,
311                  const InterpreterFrameState& loop_end_state,
312                  BasicBlock* loop_end_block, int merge_offset) {
313     DCHECK_EQ(predecessors_so_far_, predecessor_count_);
314     DCHECK_NULL(predecessors_[0]);
315     predecessors_[0] = loop_end_block;
316 
317     frame_state_.ForEachValue(
318         compilation_unit, [&](ValueNode* value, interpreter::Register reg) {
319           CheckIsLoopPhiIfNeeded(compilation_unit, merge_offset, reg, value);
320 
321           MergeLoopValue(compilation_unit.zone(), reg, value,
322                          loop_end_state.get(reg), merge_offset);
323         });
324   }
325 
326   // Merges a dead framestate (e.g. one which has been early terminated with a
327   // deopt).
MergeDead()328   void MergeDead() {
329     DCHECK_GT(predecessor_count_, 1);
330     DCHECK_LT(predecessors_so_far_, predecessor_count_);
331     predecessors_[predecessors_so_far_] = kDeadPredecessor;
332     predecessors_so_far_++;
333     DCHECK_LE(predecessors_so_far_, predecessor_count_);
334   }
335 
336   // Merges a dead loop framestate (e.g. one where the block containing the
337   // JumpLoop has been early terminated with a deopt).
MergeDeadLoop()338   void MergeDeadLoop() {
339     DCHECK_EQ(predecessors_so_far_, predecessor_count_);
340     DCHECK_NULL(predecessors_[0]);
341     predecessors_[0] = kDeadPredecessor;
342   }
343 
frame_state()344   const CompactInterpreterFrameState& frame_state() const {
345     return frame_state_;
346   }
register_state()347   MergePointRegisterState& register_state() { return register_state_; }
348 
has_phi()349   bool has_phi() const { return !phis_.is_empty(); }
phis()350   Phi::List* phis() { return &phis_; }
351 
SetPhis(Phi::List && phis)352   void SetPhis(Phi::List&& phis) {
353     // Move the collected phis to the live interpreter frame.
354     DCHECK(phis_.is_empty());
355     phis_.MoveTail(&phis, phis.begin());
356   }
357 
predecessor_count()358   int predecessor_count() const { return predecessor_count_; }
359 
predecessor_at(int i)360   BasicBlock* predecessor_at(int i) const {
361     DCHECK_EQ(predecessors_so_far_, predecessor_count_);
362     DCHECK_LT(i, predecessor_count_);
363     return predecessors_[i];
364   }
365 
366  private:
367   friend void InterpreterFrameState::CopyFrom(
368       const MaglevCompilationUnit& info,
369       const MergePointInterpreterFrameState& state);
370 
TagValue(MaglevCompilationUnit & compilation_unit,ValueNode * value)371   ValueNode* TagValue(MaglevCompilationUnit& compilation_unit,
372                       ValueNode* value) {
373     DCHECK(value->is_untagged_value());
374     if (value->Is<CheckedSmiUntag>()) {
375       return value->input(0).node();
376     }
377     DCHECK(value->Is<Int32AddWithOverflow>() || value->Is<Int32Constant>());
378     // Check if the next Node in the block after value is its CheckedSmiTag
379     // version and reuse it.
380     if (value->NextNode()) {
381       CheckedSmiTag* tagged = value->NextNode()->TryCast<CheckedSmiTag>();
382       if (tagged != nullptr && value == tagged->input().node()) {
383         return tagged;
384       }
385     }
386     // Otherwise create a tagged version.
387     ValueNode* tagged =
388         Node::New<CheckedSmiTag, std::initializer_list<ValueNode*>>(
389             compilation_unit.zone(), compilation_unit,
390             value->eager_deopt_info()->state, {value});
391     value->AddNodeAfter(tagged);
392     compilation_unit.RegisterNodeInGraphLabeller(tagged);
393     return tagged;
394   }
395 
EnsureTagged(MaglevCompilationUnit & compilation_unit,ValueNode * value)396   ValueNode* EnsureTagged(MaglevCompilationUnit& compilation_unit,
397                           ValueNode* value) {
398     if (value->is_untagged_value()) return TagValue(compilation_unit, value);
399     return value;
400   }
401 
MergeValue(MaglevCompilationUnit & compilation_unit,interpreter::Register owner,ValueNode * merged,ValueNode * unmerged,int merge_offset)402   ValueNode* MergeValue(MaglevCompilationUnit& compilation_unit,
403                         interpreter::Register owner, ValueNode* merged,
404                         ValueNode* unmerged, int merge_offset) {
405     // If the merged node is null, this is a pre-created loop header merge
406     // frame will null values for anything that isn't a loop Phi.
407     if (merged == nullptr) {
408       DCHECK_NULL(predecessors_[0]);
409       DCHECK_EQ(predecessors_so_far_, 1);
410       return unmerged;
411     }
412 
413     Phi* result = merged->TryCast<Phi>();
414     if (result != nullptr && result->merge_offset() == merge_offset) {
415       // It's possible that merged == unmerged at this point since loop-phis are
416       // not dropped if they are only assigned to themselves in the loop.
417       DCHECK_EQ(result->owner(), owner);
418       unmerged = EnsureTagged(compilation_unit, unmerged);
419       result->set_input(predecessors_so_far_, unmerged);
420       return result;
421     }
422 
423     if (merged == unmerged) return merged;
424 
425     // We guarantee that the values are tagged.
426     // TODO(victorgomes): Support Phi nodes of untagged values.
427     merged = EnsureTagged(compilation_unit, merged);
428     unmerged = EnsureTagged(compilation_unit, unmerged);
429 
430     // Tagged versions could point to the same value, avoid Phi nodes in this
431     // case.
432     if (merged == unmerged) return merged;
433 
434     // Up to this point all predecessors had the same value for this interpreter
435     // frame slot. Now that we find a distinct value, insert a copy of the first
436     // value for each predecessor seen so far, in addition to the new value.
437     // TODO(verwaest): Unclear whether we want this for Maglev: Instead of
438     // letting the register allocator remove phis, we could always merge through
439     // the frame slot. In that case we only need the inputs for representation
440     // selection, and hence could remove duplicate inputs. We'd likely need to
441     // attach the interpreter register to the phi in that case?
442     result = Node::New<Phi>(compilation_unit.zone(), predecessor_count_, owner,
443                             merge_offset);
444 
445     for (int i = 0; i < predecessors_so_far_; i++) result->set_input(i, merged);
446     result->set_input(predecessors_so_far_, unmerged);
447 
448     phis_.Add(result);
449     return result;
450   }
451 
MergeLoopValue(Zone * zone,interpreter::Register owner,ValueNode * merged,ValueNode * unmerged,int merge_offset)452   void MergeLoopValue(Zone* zone, interpreter::Register owner,
453                       ValueNode* merged, ValueNode* unmerged,
454                       int merge_offset) {
455     Phi* result = merged->TryCast<Phi>();
456     if (result == nullptr || result->merge_offset() != merge_offset) {
457       DCHECK_EQ(merged, unmerged);
458       return;
459     }
460     DCHECK_EQ(result->owner(), owner);
461     // The loop jump is defined to unconditionally be index 0.
462 #ifdef DEBUG
463     DCHECK_NULL(result->input(0).node());
464 #endif
465     result->set_input(0, unmerged);
466   }
467 
NewLoopPhi(Zone * zone,interpreter::Register reg,int merge_offset)468   ValueNode* NewLoopPhi(Zone* zone, interpreter::Register reg,
469                         int merge_offset) {
470     DCHECK_EQ(predecessors_so_far_, 1);
471     // Create a new loop phi, which for now is empty.
472     Phi* result = Node::New<Phi>(zone, predecessor_count_, reg, merge_offset);
473 #ifdef DEBUG
474     result->set_input(0, nullptr);
475 #endif
476     phis_.Add(result);
477     return result;
478   }
479 
480   int predecessor_count_;
481   int predecessors_so_far_;
482   Phi::List phis_;
483   BasicBlock** predecessors_;
484 
485   CompactInterpreterFrameState frame_state_;
486   MergePointRegisterState register_state_;
487 };
488 
CopyFrom(const MaglevCompilationUnit & info,const MergePointInterpreterFrameState & state)489 void InterpreterFrameState::CopyFrom(
490     const MaglevCompilationUnit& info,
491     const MergePointInterpreterFrameState& state) {
492   state.frame_state().ForEachValue(
493       info, [&](ValueNode* value, interpreter::Register reg) {
494         frame_[reg] = value;
495       });
496 }
497 
498 }  // namespace maglev
499 }  // namespace internal
500 }  // namespace v8
501 
502 #endif  // V8_MAGLEV_MAGLEV_INTERPRETER_FRAME_STATE_H_
503