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