1 // Copyright 2017 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "src/wasm/baseline/liftoff-assembler.h"
6 
7 #include <sstream>
8 
9 #include "src/base/optional.h"
10 #include "src/base/platform/wrappers.h"
11 #include "src/codegen/assembler-inl.h"
12 #include "src/codegen/macro-assembler-inl.h"
13 #include "src/compiler/linkage.h"
14 #include "src/compiler/wasm-compiler.h"
15 #include "src/utils/ostreams.h"
16 #include "src/wasm/baseline/liftoff-register.h"
17 #include "src/wasm/function-body-decoder-impl.h"
18 #include "src/wasm/object-access.h"
19 #include "src/wasm/wasm-linkage.h"
20 #include "src/wasm/wasm-opcodes.h"
21 
22 namespace v8 {
23 namespace internal {
24 namespace wasm {
25 
26 using VarState = LiftoffAssembler::VarState;
27 using ValueKindSig = LiftoffAssembler::ValueKindSig;
28 
29 constexpr ValueKind LiftoffAssembler::kPointerKind;
30 constexpr ValueKind LiftoffAssembler::kTaggedKind;
31 constexpr ValueKind LiftoffAssembler::kSmiKind;
32 
33 namespace {
34 
35 class StackTransferRecipe {
36   struct RegisterMove {
37     LiftoffRegister src;
38     ValueKind kind;
RegisterMovev8::internal::wasm::__anonf890b2d00111::StackTransferRecipe::RegisterMove39     constexpr RegisterMove(LiftoffRegister src, ValueKind kind)
40         : src(src), kind(kind) {}
41   };
42 
43   struct RegisterLoad {
44     enum LoadKind : uint8_t {
45       kNop,           // no-op, used for high fp of a fp pair.
46       kConstant,      // load a constant value into a register.
47       kStack,         // fill a register from a stack slot.
48       kLowHalfStack,  // fill a register from the low half of a stack slot.
49       kHighHalfStack  // fill a register from the high half of a stack slot.
50     };
51 
52     LoadKind load_kind;
53     ValueKind kind;
54     int32_t value;  // i32 constant value or stack offset, depending on kind.
55 
56     // Named constructors.
Constv8::internal::wasm::__anonf890b2d00111::StackTransferRecipe::RegisterLoad57     static RegisterLoad Const(WasmValue constant) {
58       if (constant.type().kind() == kI32) {
59         return {kConstant, kI32, constant.to_i32()};
60       }
61       DCHECK_EQ(kI64, constant.type().kind());
62       int32_t i32_const = static_cast<int32_t>(constant.to_i64());
63       DCHECK_EQ(constant.to_i64(), i32_const);
64       return {kConstant, kI64, i32_const};
65     }
Stackv8::internal::wasm::__anonf890b2d00111::StackTransferRecipe::RegisterLoad66     static RegisterLoad Stack(int32_t offset, ValueKind kind) {
67       return {kStack, kind, offset};
68     }
HalfStackv8::internal::wasm::__anonf890b2d00111::StackTransferRecipe::RegisterLoad69     static RegisterLoad HalfStack(int32_t offset, RegPairHalf half) {
70       return {half == kLowWord ? kLowHalfStack : kHighHalfStack, kI32, offset};
71     }
Nopv8::internal::wasm::__anonf890b2d00111::StackTransferRecipe::RegisterLoad72     static RegisterLoad Nop() {
73       // ValueKind does not matter.
74       return {kNop, kI32, 0};
75     }
76 
77    private:
RegisterLoadv8::internal::wasm::__anonf890b2d00111::StackTransferRecipe::RegisterLoad78     RegisterLoad(LoadKind load_kind, ValueKind kind, int32_t value)
79         : load_kind(load_kind), kind(kind), value(value) {}
80   };
81 
82  public:
StackTransferRecipe(LiftoffAssembler * wasm_asm)83   explicit StackTransferRecipe(LiftoffAssembler* wasm_asm) : asm_(wasm_asm) {}
84   StackTransferRecipe(const StackTransferRecipe&) = delete;
85   StackTransferRecipe& operator=(const StackTransferRecipe&) = delete;
~StackTransferRecipe()86   ~StackTransferRecipe() { Execute(); }
87 
Execute()88   void Execute() {
89     // First, execute register moves. Then load constants and stack values into
90     // registers.
91     ExecuteMoves();
92     DCHECK(move_dst_regs_.is_empty());
93     ExecuteLoads();
94     DCHECK(load_dst_regs_.is_empty());
95   }
96 
TransferStackSlot(const VarState & dst,const VarState & src)97   V8_INLINE void TransferStackSlot(const VarState& dst, const VarState& src) {
98     DCHECK(CheckCompatibleStackSlotTypes(dst.kind(), src.kind()));
99     if (dst.is_reg()) {
100       LoadIntoRegister(dst.reg(), src, src.offset());
101       return;
102     }
103     if (dst.is_const()) {
104       DCHECK_EQ(dst.i32_const(), src.i32_const());
105       return;
106     }
107     DCHECK(dst.is_stack());
108     switch (src.loc()) {
109       case VarState::kStack:
110         if (src.offset() != dst.offset()) {
111           asm_->MoveStackValue(dst.offset(), src.offset(), src.kind());
112         }
113         break;
114       case VarState::kRegister:
115         asm_->Spill(dst.offset(), src.reg(), src.kind());
116         break;
117       case VarState::kIntConst:
118         asm_->Spill(dst.offset(), src.constant());
119         break;
120     }
121   }
122 
LoadIntoRegister(LiftoffRegister dst,const LiftoffAssembler::VarState & src,uint32_t src_offset)123   V8_INLINE void LoadIntoRegister(LiftoffRegister dst,
124                                   const LiftoffAssembler::VarState& src,
125                                   uint32_t src_offset) {
126     switch (src.loc()) {
127       case VarState::kStack:
128         LoadStackSlot(dst, src_offset, src.kind());
129         break;
130       case VarState::kRegister:
131         DCHECK_EQ(dst.reg_class(), src.reg_class());
132         if (dst != src.reg()) MoveRegister(dst, src.reg(), src.kind());
133         break;
134       case VarState::kIntConst:
135         LoadConstant(dst, src.constant());
136         break;
137     }
138   }
139 
LoadI64HalfIntoRegister(LiftoffRegister dst,const LiftoffAssembler::VarState & src,int offset,RegPairHalf half)140   void LoadI64HalfIntoRegister(LiftoffRegister dst,
141                                const LiftoffAssembler::VarState& src,
142                                int offset, RegPairHalf half) {
143     // Use CHECK such that the remaining code is statically dead if
144     // {kNeedI64RegPair} is false.
145     CHECK(kNeedI64RegPair);
146     DCHECK_EQ(kI64, src.kind());
147     switch (src.loc()) {
148       case VarState::kStack:
149         LoadI64HalfStackSlot(dst, offset, half);
150         break;
151       case VarState::kRegister: {
152         LiftoffRegister src_half =
153             half == kLowWord ? src.reg().low() : src.reg().high();
154         if (dst != src_half) MoveRegister(dst, src_half, kI32);
155         break;
156       }
157       case VarState::kIntConst:
158         int32_t value = src.i32_const();
159         // The high word is the sign extension of the low word.
160         if (half == kHighWord) value = value >> 31;
161         LoadConstant(dst, WasmValue(value));
162         break;
163     }
164   }
165 
MoveRegister(LiftoffRegister dst,LiftoffRegister src,ValueKind kind)166   void MoveRegister(LiftoffRegister dst, LiftoffRegister src, ValueKind kind) {
167     DCHECK_NE(dst, src);
168     DCHECK_EQ(dst.reg_class(), src.reg_class());
169     DCHECK_EQ(reg_class_for(kind), src.reg_class());
170     if (src.is_gp_pair()) {
171       DCHECK_EQ(kI64, kind);
172       if (dst.low() != src.low()) MoveRegister(dst.low(), src.low(), kI32);
173       if (dst.high() != src.high()) MoveRegister(dst.high(), src.high(), kI32);
174       return;
175     }
176     if (src.is_fp_pair()) {
177       DCHECK_EQ(kS128, kind);
178       if (dst.low() != src.low()) {
179         MoveRegister(dst.low(), src.low(), kF64);
180         MoveRegister(dst.high(), src.high(), kF64);
181       }
182       return;
183     }
184     if (move_dst_regs_.has(dst)) {
185       DCHECK_EQ(register_move(dst)->src, src);
186       // Non-fp registers can only occur with the exact same type.
187       DCHECK_IMPLIES(!dst.is_fp(), register_move(dst)->kind == kind);
188       // It can happen that one fp register holds both the f32 zero and the f64
189       // zero, as the initial value for local variables. Move the value as f64
190       // in that case.
191       if (kind == kF64) register_move(dst)->kind = kF64;
192       return;
193     }
194     move_dst_regs_.set(dst);
195     ++*src_reg_use_count(src);
196     *register_move(dst) = {src, kind};
197   }
198 
LoadConstant(LiftoffRegister dst,WasmValue value)199   void LoadConstant(LiftoffRegister dst, WasmValue value) {
200     DCHECK(!load_dst_regs_.has(dst));
201     load_dst_regs_.set(dst);
202     if (dst.is_gp_pair()) {
203       DCHECK_EQ(kI64, value.type().kind());
204       int64_t i64 = value.to_i64();
205       *register_load(dst.low()) =
206           RegisterLoad::Const(WasmValue(static_cast<int32_t>(i64)));
207       *register_load(dst.high()) =
208           RegisterLoad::Const(WasmValue(static_cast<int32_t>(i64 >> 32)));
209     } else {
210       *register_load(dst) = RegisterLoad::Const(value);
211     }
212   }
213 
LoadStackSlot(LiftoffRegister dst,uint32_t stack_offset,ValueKind kind)214   void LoadStackSlot(LiftoffRegister dst, uint32_t stack_offset,
215                      ValueKind kind) {
216     if (load_dst_regs_.has(dst)) {
217       // It can happen that we spilled the same register to different stack
218       // slots, and then we reload them later into the same dst register.
219       // In that case, it is enough to load one of the stack slots.
220       return;
221     }
222     load_dst_regs_.set(dst);
223     if (dst.is_gp_pair()) {
224       DCHECK_EQ(kI64, kind);
225       *register_load(dst.low()) =
226           RegisterLoad::HalfStack(stack_offset, kLowWord);
227       *register_load(dst.high()) =
228           RegisterLoad::HalfStack(stack_offset, kHighWord);
229     } else if (dst.is_fp_pair()) {
230       DCHECK_EQ(kS128, kind);
231       // Only need register_load for low_gp since we load 128 bits at one go.
232       // Both low and high need to be set in load_dst_regs_ but when iterating
233       // over it, both low and high will be cleared, so we won't load twice.
234       *register_load(dst.low()) = RegisterLoad::Stack(stack_offset, kind);
235       *register_load(dst.high()) = RegisterLoad::Nop();
236     } else {
237       *register_load(dst) = RegisterLoad::Stack(stack_offset, kind);
238     }
239   }
240 
LoadI64HalfStackSlot(LiftoffRegister dst,int offset,RegPairHalf half)241   void LoadI64HalfStackSlot(LiftoffRegister dst, int offset, RegPairHalf half) {
242     if (load_dst_regs_.has(dst)) {
243       // It can happen that we spilled the same register to different stack
244       // slots, and then we reload them later into the same dst register.
245       // In that case, it is enough to load one of the stack slots.
246       return;
247     }
248     load_dst_regs_.set(dst);
249     *register_load(dst) = RegisterLoad::HalfStack(offset, half);
250   }
251 
252  private:
253   using MovesStorage =
254       std::aligned_storage<kAfterMaxLiftoffRegCode * sizeof(RegisterMove),
255                            alignof(RegisterMove)>::type;
256   using LoadsStorage =
257       std::aligned_storage<kAfterMaxLiftoffRegCode * sizeof(RegisterLoad),
258                            alignof(RegisterLoad)>::type;
259 
260   ASSERT_TRIVIALLY_COPYABLE(RegisterMove);
261   ASSERT_TRIVIALLY_COPYABLE(RegisterLoad);
262 
263   MovesStorage register_moves_;  // uninitialized
264   LoadsStorage register_loads_;  // uninitialized
265   int src_reg_use_count_[kAfterMaxLiftoffRegCode] = {0};
266   LiftoffRegList move_dst_regs_;
267   LiftoffRegList load_dst_regs_;
268   LiftoffAssembler* const asm_;
269 
register_move(LiftoffRegister reg)270   RegisterMove* register_move(LiftoffRegister reg) {
271     return reinterpret_cast<RegisterMove*>(&register_moves_) +
272            reg.liftoff_code();
273   }
register_load(LiftoffRegister reg)274   RegisterLoad* register_load(LiftoffRegister reg) {
275     return reinterpret_cast<RegisterLoad*>(&register_loads_) +
276            reg.liftoff_code();
277   }
src_reg_use_count(LiftoffRegister reg)278   int* src_reg_use_count(LiftoffRegister reg) {
279     return src_reg_use_count_ + reg.liftoff_code();
280   }
281 
ExecuteMove(LiftoffRegister dst)282   void ExecuteMove(LiftoffRegister dst) {
283     RegisterMove* move = register_move(dst);
284     DCHECK_EQ(0, *src_reg_use_count(dst));
285     asm_->Move(dst, move->src, move->kind);
286     ClearExecutedMove(dst);
287   }
288 
ClearExecutedMove(LiftoffRegister dst)289   void ClearExecutedMove(LiftoffRegister dst) {
290     DCHECK(move_dst_regs_.has(dst));
291     move_dst_regs_.clear(dst);
292     RegisterMove* move = register_move(dst);
293     DCHECK_LT(0, *src_reg_use_count(move->src));
294     if (--*src_reg_use_count(move->src)) return;
295     // src count dropped to zero. If this is a destination register, execute
296     // that move now.
297     if (!move_dst_regs_.has(move->src)) return;
298     ExecuteMove(move->src);
299   }
300 
ExecuteMoves()301   void ExecuteMoves() {
302     // Execute all moves whose {dst} is not being used as src in another move.
303     // If any src count drops to zero, also (transitively) execute the
304     // corresponding move to that register.
305     for (LiftoffRegister dst : move_dst_regs_) {
306       // Check if already handled via transitivity in {ClearExecutedMove}.
307       if (!move_dst_regs_.has(dst)) continue;
308       if (*src_reg_use_count(dst)) continue;
309       ExecuteMove(dst);
310     }
311 
312     // All remaining moves are parts of a cycle. Just spill the first one, then
313     // process all remaining moves in that cycle. Repeat for all cycles.
314     int last_spill_offset = asm_->TopSpillOffset();
315     while (!move_dst_regs_.is_empty()) {
316       // TODO(clemensb): Use an unused register if available.
317       LiftoffRegister dst = move_dst_regs_.GetFirstRegSet();
318       RegisterMove* move = register_move(dst);
319       last_spill_offset += LiftoffAssembler::SlotSizeForType(move->kind);
320       LiftoffRegister spill_reg = move->src;
321       asm_->Spill(last_spill_offset, spill_reg, move->kind);
322       // Remember to reload into the destination register later.
323       LoadStackSlot(dst, last_spill_offset, move->kind);
324       ClearExecutedMove(dst);
325     }
326   }
327 
ExecuteLoads()328   void ExecuteLoads() {
329     for (LiftoffRegister dst : load_dst_regs_) {
330       RegisterLoad* load = register_load(dst);
331       switch (load->load_kind) {
332         case RegisterLoad::kNop:
333           break;
334         case RegisterLoad::kConstant:
335           asm_->LoadConstant(dst, load->kind == kI64
336                                       ? WasmValue(int64_t{load->value})
337                                       : WasmValue(int32_t{load->value}));
338           break;
339         case RegisterLoad::kStack:
340           if (kNeedS128RegPair && load->kind == kS128) {
341             asm_->Fill(LiftoffRegister::ForFpPair(dst.fp()), load->value,
342                        load->kind);
343           } else {
344             asm_->Fill(dst, load->value, load->kind);
345           }
346           break;
347         case RegisterLoad::kLowHalfStack:
348           // Half of a register pair, {dst} must be a gp register.
349           asm_->FillI64Half(dst.gp(), load->value, kLowWord);
350           break;
351         case RegisterLoad::kHighHalfStack:
352           // Half of a register pair, {dst} must be a gp register.
353           asm_->FillI64Half(dst.gp(), load->value, kHighWord);
354           break;
355       }
356     }
357     load_dst_regs_ = {};
358   }
359 };
360 
361 class RegisterReuseMap {
362  public:
Add(LiftoffRegister src,LiftoffRegister dst)363   void Add(LiftoffRegister src, LiftoffRegister dst) {
364     if (auto previous = Lookup(src)) {
365       DCHECK_EQ(previous, dst);
366       return;
367     }
368     map_.emplace_back(src);
369     map_.emplace_back(dst);
370   }
371 
Lookup(LiftoffRegister src)372   base::Optional<LiftoffRegister> Lookup(LiftoffRegister src) {
373     for (auto it = map_.begin(), end = map_.end(); it != end; it += 2) {
374       if (it->is_gp_pair() == src.is_gp_pair() &&
375           it->is_fp_pair() == src.is_fp_pair() && *it == src)
376         return *(it + 1);
377     }
378     return {};
379   }
380 
381  private:
382   // {map_} holds pairs of <src, dst>.
383   base::SmallVector<LiftoffRegister, 8> map_;
384 };
385 
386 enum MergeKeepStackSlots : bool {
387   kKeepStackSlots = true,
388   kTurnStackSlotsIntoRegisters = false
389 };
390 enum MergeAllowConstants : bool {
391   kConstantsAllowed = true,
392   kConstantsNotAllowed = false
393 };
394 enum ReuseRegisters : bool {
395   kReuseRegisters = true,
396   kNoReuseRegisters = false
397 };
InitMergeRegion(LiftoffAssembler::CacheState * state,const VarState * source,VarState * target,uint32_t count,MergeKeepStackSlots keep_stack_slots,MergeAllowConstants allow_constants,ReuseRegisters reuse_registers,LiftoffRegList used_regs)398 void InitMergeRegion(LiftoffAssembler::CacheState* state,
399                      const VarState* source, VarState* target, uint32_t count,
400                      MergeKeepStackSlots keep_stack_slots,
401                      MergeAllowConstants allow_constants,
402                      ReuseRegisters reuse_registers, LiftoffRegList used_regs) {
403   RegisterReuseMap register_reuse_map;
404   for (const VarState* source_end = source + count; source < source_end;
405        ++source, ++target) {
406     if ((source->is_stack() && keep_stack_slots) ||
407         (source->is_const() && allow_constants)) {
408       *target = *source;
409       continue;
410     }
411     base::Optional<LiftoffRegister> reg;
412     // First try: Keep the same register, if it's free.
413     if (source->is_reg() && state->is_free(source->reg())) {
414       reg = source->reg();
415     }
416     // Second try: Use the same register we used before (if we reuse registers).
417     if (!reg && reuse_registers) {
418       reg = register_reuse_map.Lookup(source->reg());
419     }
420     // Third try: Use any free register.
421     RegClass rc = reg_class_for(source->kind());
422     if (!reg && state->has_unused_register(rc, used_regs)) {
423       reg = state->unused_register(rc, used_regs);
424     }
425     if (!reg) {
426       // No free register; make this a stack slot.
427       *target = VarState(source->kind(), source->offset());
428       continue;
429     }
430     if (reuse_registers) register_reuse_map.Add(source->reg(), *reg);
431     state->inc_used(*reg);
432     *target = VarState(source->kind(), *reg, source->offset());
433   }
434 }
435 
436 }  // namespace
437 
438 // TODO(clemensb): Don't copy the full parent state (this makes us N^2).
InitMerge(const CacheState & source,uint32_t num_locals,uint32_t arity,uint32_t stack_depth)439 void LiftoffAssembler::CacheState::InitMerge(const CacheState& source,
440                                              uint32_t num_locals,
441                                              uint32_t arity,
442                                              uint32_t stack_depth) {
443   // |------locals------|---(in between)----|--(discarded)--|----merge----|
444   //  <-- num_locals --> <-- stack_depth -->^stack_base      <-- arity -->
445 
446   if (source.cached_instance != no_reg) {
447     SetInstanceCacheRegister(source.cached_instance);
448   }
449 
450   if (source.cached_mem_start != no_reg) {
451     SetMemStartCacheRegister(source.cached_mem_start);
452   }
453 
454   uint32_t stack_base = stack_depth + num_locals;
455   uint32_t target_height = stack_base + arity;
456   uint32_t discarded = source.stack_height() - target_height;
457   DCHECK(stack_state.empty());
458 
459   DCHECK_GE(source.stack_height(), stack_base);
460   stack_state.resize_no_init(target_height);
461 
462   const VarState* source_begin = source.stack_state.data();
463   VarState* target_begin = stack_state.data();
464 
465   // Try to keep locals and the merge region in their registers. Register used
466   // multiple times need to be copied to another free register. Compute the list
467   // of used registers.
468   LiftoffRegList used_regs;
469   for (auto& src : base::VectorOf(source_begin, num_locals)) {
470     if (src.is_reg()) used_regs.set(src.reg());
471   }
472   for (auto& src :
473        base::VectorOf(source_begin + stack_base + discarded, arity)) {
474     if (src.is_reg()) used_regs.set(src.reg());
475   }
476 
477   // Initialize the merge region. If this region moves, try to turn stack slots
478   // into registers since we need to load the value anyways.
479   MergeKeepStackSlots keep_merge_stack_slots =
480       discarded == 0 ? kKeepStackSlots : kTurnStackSlotsIntoRegisters;
481   InitMergeRegion(this, source_begin + stack_base + discarded,
482                   target_begin + stack_base, arity, keep_merge_stack_slots,
483                   kConstantsNotAllowed, kNoReuseRegisters, used_regs);
484 
485   // Initialize the locals region. Here, stack slots stay stack slots (because
486   // they do not move). Try to keep register in registers, but avoid duplicates.
487   InitMergeRegion(this, source_begin, target_begin, num_locals, kKeepStackSlots,
488                   kConstantsNotAllowed, kNoReuseRegisters, used_regs);
489   // Consistency check: All the {used_regs} are really in use now.
490   DCHECK_EQ(used_regs, used_registers & used_regs);
491 
492   // Last, initialize the section in between. Here, constants are allowed, but
493   // registers which are already used for the merge region or locals must be
494   // moved to other registers or spilled. If a register appears twice in the
495   // source region, ensure to use the same register twice in the target region.
496   InitMergeRegion(this, source_begin + num_locals, target_begin + num_locals,
497                   stack_depth, kKeepStackSlots, kConstantsAllowed,
498                   kReuseRegisters, used_regs);
499 }
500 
Steal(const CacheState & source)501 void LiftoffAssembler::CacheState::Steal(const CacheState& source) {
502   // Just use the move assignment operator.
503   *this = std::move(source);
504 }
505 
Split(const CacheState & source)506 void LiftoffAssembler::CacheState::Split(const CacheState& source) {
507   // Call the private copy assignment operator.
508   *this = source;
509 }
510 
511 namespace {
GetSafepointIndexForStackSlot(const VarState & slot)512 int GetSafepointIndexForStackSlot(const VarState& slot) {
513   // index = 0 is for the stack slot at 'fp + kFixedFrameSizeAboveFp -
514   // kSystemPointerSize', the location of the current stack slot is 'fp -
515   // slot.offset()'. The index we need is therefore '(fp +
516   // kFixedFrameSizeAboveFp - kSystemPointerSize) - (fp - slot.offset())' =
517   // 'slot.offset() + kFixedFrameSizeAboveFp - kSystemPointerSize'.
518   // Concretely, the index of the first stack slot is '4'.
519   return (slot.offset() + StandardFrameConstants::kFixedFrameSizeAboveFp -
520           kSystemPointerSize) /
521          kSystemPointerSize;
522 }
523 }  // namespace
524 
GetTaggedSlotsForOOLCode(ZoneVector<int> * slots,LiftoffRegList * spills,SpillLocation spill_location)525 void LiftoffAssembler::CacheState::GetTaggedSlotsForOOLCode(
526     ZoneVector<int>* slots, LiftoffRegList* spills,
527     SpillLocation spill_location) {
528   for (const auto& slot : stack_state) {
529     if (!is_reference(slot.kind())) continue;
530 
531     if (spill_location == SpillLocation::kTopOfStack && slot.is_reg()) {
532       // Registers get spilled just before the call to the runtime. In {spills}
533       // we store which of the spilled registers contain references, so that we
534       // can add the spill slots to the safepoint.
535       spills->set(slot.reg());
536       continue;
537     }
538     DCHECK_IMPLIES(slot.is_reg(), spill_location == SpillLocation::kStackSlots);
539 
540     slots->push_back(GetSafepointIndexForStackSlot(slot));
541   }
542 }
543 
DefineSafepoint(Safepoint & safepoint)544 void LiftoffAssembler::CacheState::DefineSafepoint(Safepoint& safepoint) {
545   for (const auto& slot : stack_state) {
546     if (is_reference(slot.kind())) {
547       DCHECK(slot.is_stack());
548       safepoint.DefinePointerSlot(GetSafepointIndexForStackSlot(slot));
549     }
550   }
551 }
552 
DefineSafepointWithCalleeSavedRegisters(Safepoint & safepoint)553 void LiftoffAssembler::CacheState::DefineSafepointWithCalleeSavedRegisters(
554     Safepoint& safepoint) {
555   for (const auto& slot : stack_state) {
556     if (!is_reference(slot.kind())) continue;
557     if (slot.is_stack()) {
558       safepoint.DefinePointerSlot(GetSafepointIndexForStackSlot(slot));
559     } else {
560       DCHECK(slot.is_reg());
561       safepoint.DefineRegister(slot.reg().gp().code());
562     }
563   }
564   if (cached_instance != no_reg) {
565     safepoint.DefineRegister(cached_instance.code());
566   }
567 }
568 
GetTotalFrameSlotCountForGC() const569 int LiftoffAssembler::GetTotalFrameSlotCountForGC() const {
570   // The GC does not care about the actual number of spill slots, just about
571   // the number of references that could be there in the spilling area. Note
572   // that the offset of the first spill slot is kSystemPointerSize and not
573   // '0'. Therefore we don't have to add '+1' here.
574   return (max_used_spill_offset_ +
575           StandardFrameConstants::kFixedFrameSizeAboveFp +
576           ool_spill_space_size_) /
577          kSystemPointerSize;
578 }
579 
580 namespace {
581 
DefaultLiftoffOptions()582 AssemblerOptions DefaultLiftoffOptions() { return AssemblerOptions{}; }
583 
584 }  // namespace
585 
LiftoffAssembler(std::unique_ptr<AssemblerBuffer> buffer)586 LiftoffAssembler::LiftoffAssembler(std::unique_ptr<AssemblerBuffer> buffer)
587     : TurboAssembler(nullptr, DefaultLiftoffOptions(), CodeObjectRequired::kNo,
588                      std::move(buffer)) {
589   set_abort_hard(true);  // Avoid calls to Abort.
590 }
591 
~LiftoffAssembler()592 LiftoffAssembler::~LiftoffAssembler() {
593   if (num_locals_ > kInlineLocalKinds) {
594     base::Free(more_local_kinds_);
595   }
596 }
597 
LoadToRegister(VarState slot,LiftoffRegList pinned)598 LiftoffRegister LiftoffAssembler::LoadToRegister(VarState slot,
599                                                  LiftoffRegList pinned) {
600   if (slot.is_reg()) return slot.reg();
601   LiftoffRegister reg = GetUnusedRegister(reg_class_for(slot.kind()), pinned);
602   if (slot.is_const()) {
603     LoadConstant(reg, slot.constant());
604   } else {
605     DCHECK(slot.is_stack());
606     Fill(reg, slot.offset(), slot.kind());
607   }
608   return reg;
609 }
610 
LoadI64HalfIntoRegister(VarState slot,RegPairHalf half)611 LiftoffRegister LiftoffAssembler::LoadI64HalfIntoRegister(VarState slot,
612                                                           RegPairHalf half) {
613   if (slot.is_reg()) {
614     return half == kLowWord ? slot.reg().low() : slot.reg().high();
615   }
616   LiftoffRegister dst = GetUnusedRegister(kGpReg, {});
617   if (slot.is_stack()) {
618     FillI64Half(dst.gp(), slot.offset(), half);
619     return dst;
620   }
621   DCHECK(slot.is_const());
622   int32_t half_word =
623       static_cast<int32_t>(half == kLowWord ? slot.constant().to_i64()
624                                             : slot.constant().to_i64() >> 32);
625   LoadConstant(dst, WasmValue(half_word));
626   return dst;
627 }
628 
PeekToRegister(int index,LiftoffRegList pinned)629 LiftoffRegister LiftoffAssembler::PeekToRegister(int index,
630                                                  LiftoffRegList pinned) {
631   DCHECK_LT(index, cache_state_.stack_state.size());
632   VarState& slot = cache_state_.stack_state.end()[-1 - index];
633   if (slot.is_reg()) {
634     return slot.reg();
635   }
636   LiftoffRegister reg = LoadToRegister(slot, pinned);
637   cache_state_.inc_used(reg);
638   slot.MakeRegister(reg);
639   return reg;
640 }
641 
DropValues(int count)642 void LiftoffAssembler::DropValues(int count) {
643   for (int i = 0; i < count; ++i) {
644     DCHECK(!cache_state_.stack_state.empty());
645     VarState slot = cache_state_.stack_state.back();
646     cache_state_.stack_state.pop_back();
647     if (slot.is_reg()) {
648       cache_state_.dec_used(slot.reg());
649     }
650   }
651 }
652 
DropValue(int depth)653 void LiftoffAssembler::DropValue(int depth) {
654   auto* dropped = cache_state_.stack_state.begin() + depth;
655   if (dropped->is_reg()) {
656     cache_state_.dec_used(dropped->reg());
657   }
658   std::copy(dropped + 1, cache_state_.stack_state.end(), dropped);
659   cache_state_.stack_state.pop_back();
660 }
661 
PrepareLoopArgs(int num)662 void LiftoffAssembler::PrepareLoopArgs(int num) {
663   for (int i = 0; i < num; ++i) {
664     VarState& slot = cache_state_.stack_state.end()[-1 - i];
665     if (slot.is_stack()) continue;
666     RegClass rc = reg_class_for(slot.kind());
667     if (slot.is_reg()) {
668       if (cache_state_.get_use_count(slot.reg()) > 1) {
669         // If the register is used more than once, we cannot use it for the
670         // merge. Move it to an unused register instead.
671         LiftoffRegList pinned;
672         pinned.set(slot.reg());
673         LiftoffRegister dst_reg = GetUnusedRegister(rc, pinned);
674         Move(dst_reg, slot.reg(), slot.kind());
675         cache_state_.dec_used(slot.reg());
676         cache_state_.inc_used(dst_reg);
677         slot.MakeRegister(dst_reg);
678       }
679       continue;
680     }
681     LiftoffRegister reg = GetUnusedRegister(rc, {});
682     LoadConstant(reg, slot.constant());
683     slot.MakeRegister(reg);
684     cache_state_.inc_used(reg);
685   }
686 }
687 
MaterializeMergedConstants(uint32_t arity)688 void LiftoffAssembler::MaterializeMergedConstants(uint32_t arity) {
689   // Materialize constants on top of the stack ({arity} many), and locals.
690   VarState* stack_base = cache_state_.stack_state.data();
691   for (auto slots :
692        {base::VectorOf(stack_base + cache_state_.stack_state.size() - arity,
693                        arity),
694         base::VectorOf(stack_base, num_locals())}) {
695     for (VarState& slot : slots) {
696       if (!slot.is_const()) continue;
697       RegClass rc = reg_class_for(slot.kind());
698       if (cache_state_.has_unused_register(rc)) {
699         LiftoffRegister reg = cache_state_.unused_register(rc);
700         LoadConstant(reg, slot.constant());
701         cache_state_.inc_used(reg);
702         slot.MakeRegister(reg);
703       } else {
704         Spill(slot.offset(), slot.constant());
705         slot.MakeStack();
706       }
707     }
708   }
709 }
710 
MergeFullStackWith(CacheState & target,const CacheState & source)711 void LiftoffAssembler::MergeFullStackWith(CacheState& target,
712                                           const CacheState& source) {
713   DCHECK_EQ(source.stack_height(), target.stack_height());
714   // TODO(clemensb): Reuse the same StackTransferRecipe object to save some
715   // allocations.
716   StackTransferRecipe transfers(this);
717   for (uint32_t i = 0, e = source.stack_height(); i < e; ++i) {
718     transfers.TransferStackSlot(target.stack_state[i], source.stack_state[i]);
719   }
720 
721   // Full stack merging is only done for forward jumps, so we can just clear the
722   // cache registers at the target in case of mismatch.
723   if (source.cached_instance != target.cached_instance) {
724     target.ClearCachedInstanceRegister();
725   }
726   if (source.cached_mem_start != target.cached_mem_start) {
727     target.ClearCachedMemStartRegister();
728   }
729 }
730 
MergeStackWith(CacheState & target,uint32_t arity,JumpDirection jump_direction)731 void LiftoffAssembler::MergeStackWith(CacheState& target, uint32_t arity,
732                                       JumpDirection jump_direction) {
733   // Before: ----------------|----- (discarded) ----|--- arity ---|
734   //                         ^target_stack_height   ^stack_base   ^stack_height
735   // After:  ----|-- arity --|
736   //             ^           ^target_stack_height
737   //             ^target_stack_base
738   uint32_t stack_height = cache_state_.stack_height();
739   uint32_t target_stack_height = target.stack_height();
740   DCHECK_LE(target_stack_height, stack_height);
741   DCHECK_LE(arity, target_stack_height);
742   uint32_t stack_base = stack_height - arity;
743   uint32_t target_stack_base = target_stack_height - arity;
744   StackTransferRecipe transfers(this);
745   for (uint32_t i = 0; i < target_stack_base; ++i) {
746     transfers.TransferStackSlot(target.stack_state[i],
747                                 cache_state_.stack_state[i]);
748   }
749   for (uint32_t i = 0; i < arity; ++i) {
750     transfers.TransferStackSlot(target.stack_state[target_stack_base + i],
751                                 cache_state_.stack_state[stack_base + i]);
752   }
753 
754   // Check whether the cached instance and/or memory start need to be moved to
755   // another register. Register moves are executed as part of the
756   // {StackTransferRecipe}. Remember whether the register content has to be
757   // reloaded after executing the stack transfers.
758   bool reload_instance = false;
759   bool reload_mem_start = false;
760   for (auto tuple :
761        {std::make_tuple(&reload_instance, cache_state_.cached_instance,
762                         &target.cached_instance),
763         std::make_tuple(&reload_mem_start, cache_state_.cached_mem_start,
764                         &target.cached_mem_start)}) {
765     bool* reload = std::get<0>(tuple);
766     Register src_reg = std::get<1>(tuple);
767     Register* dst_reg = std::get<2>(tuple);
768     // If the registers match, or the destination has no cache register, nothing
769     // needs to be done.
770     if (src_reg == *dst_reg || *dst_reg == no_reg) continue;
771     // On forward jumps, just reset the cached register in the target state.
772     if (jump_direction == kForwardJump) {
773       target.ClearCacheRegister(dst_reg);
774     } else if (src_reg != no_reg) {
775       // If the source has the content but in the wrong register, execute a
776       // register move as part of the stack transfer.
777       transfers.MoveRegister(LiftoffRegister{*dst_reg},
778                              LiftoffRegister{src_reg}, kPointerKind);
779     } else {
780       // Otherwise (the source state has no cached content), we reload later.
781       *reload = true;
782     }
783   }
784 
785   // Now execute stack transfers and register moves/loads.
786   transfers.Execute();
787 
788   if (reload_instance) {
789     LoadInstanceFromFrame(target.cached_instance);
790   }
791   if (reload_mem_start) {
792     // {target.cached_instance} already got restored above, so we can use it
793     // if it exists.
794     Register instance = target.cached_instance;
795     if (instance == no_reg) {
796       // We don't have the instance available yet. Store it into the target
797       // mem_start, so that we can load the mem_start from there.
798       instance = target.cached_mem_start;
799       LoadInstanceFromFrame(instance);
800     }
801     LoadFromInstance(
802         target.cached_mem_start, instance,
803         ObjectAccess::ToTagged(WasmInstanceObject::kMemoryStartOffset),
804         sizeof(size_t));
805   }
806 }
807 
Spill(VarState * slot)808 void LiftoffAssembler::Spill(VarState* slot) {
809   switch (slot->loc()) {
810     case VarState::kStack:
811       return;
812     case VarState::kRegister:
813       Spill(slot->offset(), slot->reg(), slot->kind());
814       cache_state_.dec_used(slot->reg());
815       break;
816     case VarState::kIntConst:
817       Spill(slot->offset(), slot->constant());
818       break;
819   }
820   slot->MakeStack();
821 }
822 
SpillLocals()823 void LiftoffAssembler::SpillLocals() {
824   for (uint32_t i = 0; i < num_locals_; ++i) {
825     Spill(&cache_state_.stack_state[i]);
826   }
827 }
828 
SpillAllRegisters()829 void LiftoffAssembler::SpillAllRegisters() {
830   for (uint32_t i = 0, e = cache_state_.stack_height(); i < e; ++i) {
831     auto& slot = cache_state_.stack_state[i];
832     if (!slot.is_reg()) continue;
833     Spill(slot.offset(), slot.reg(), slot.kind());
834     slot.MakeStack();
835   }
836   cache_state_.ClearAllCacheRegisters();
837   cache_state_.reset_used_registers();
838 }
839 
ClearRegister(Register reg,std::initializer_list<Register * > possible_uses,LiftoffRegList pinned)840 void LiftoffAssembler::ClearRegister(
841     Register reg, std::initializer_list<Register*> possible_uses,
842     LiftoffRegList pinned) {
843   if (reg == cache_state()->cached_instance) {
844     cache_state()->ClearCachedInstanceRegister();
845     // We can return immediately. The instance is only used to load information
846     // at the beginning of an instruction when values don't have to be in
847     // specific registers yet. Therefore the instance should never be one of the
848     // {possible_uses}.
849     for (Register* use : possible_uses) {
850       USE(use);
851       DCHECK_NE(reg, *use);
852     }
853     return;
854   } else if (reg == cache_state()->cached_mem_start) {
855     cache_state()->ClearCachedMemStartRegister();
856     // The memory start may be among the {possible_uses}, e.g. for an atomic
857     // compare exchange. Therefore it is necessary to iterate over the
858     // {possible_uses} below, and we cannot return early.
859   } else if (cache_state()->is_used(LiftoffRegister(reg))) {
860     SpillRegister(LiftoffRegister(reg));
861   }
862   Register replacement = no_reg;
863   for (Register* use : possible_uses) {
864     if (reg != *use) continue;
865     if (replacement == no_reg) {
866       replacement = GetUnusedRegister(kGpReg, pinned).gp();
867       Move(replacement, reg, kPointerKind);
868     }
869     // We cannot leave this loop early. There may be multiple uses of {reg}.
870     *use = replacement;
871   }
872 }
873 
874 namespace {
PrepareStackTransfers(const ValueKindSig * sig,compiler::CallDescriptor * call_descriptor,const VarState * slots,LiftoffStackSlots * stack_slots,StackTransferRecipe * stack_transfers,LiftoffRegList * param_regs)875 void PrepareStackTransfers(const ValueKindSig* sig,
876                            compiler::CallDescriptor* call_descriptor,
877                            const VarState* slots,
878                            LiftoffStackSlots* stack_slots,
879                            StackTransferRecipe* stack_transfers,
880                            LiftoffRegList* param_regs) {
881   // Process parameters backwards, to reduce the amount of Slot sorting for
882   // the most common case - a normal Wasm Call. Slots will be mostly unsorted
883   // in the Builtin call case.
884   uint32_t call_desc_input_idx =
885       static_cast<uint32_t>(call_descriptor->InputCount());
886   uint32_t num_params = static_cast<uint32_t>(sig->parameter_count());
887   for (uint32_t i = num_params; i > 0; --i) {
888     const uint32_t param = i - 1;
889     ValueKind kind = sig->GetParam(param);
890     const bool is_gp_pair = kNeedI64RegPair && kind == kI64;
891     const int num_lowered_params = is_gp_pair ? 2 : 1;
892     const VarState& slot = slots[param];
893     const uint32_t stack_offset = slot.offset();
894     // Process both halfs of a register pair separately, because they are passed
895     // as separate parameters. One or both of them could end up on the stack.
896     for (int lowered_idx = 0; lowered_idx < num_lowered_params; ++lowered_idx) {
897       const RegPairHalf half =
898           is_gp_pair && lowered_idx == 0 ? kHighWord : kLowWord;
899       --call_desc_input_idx;
900       compiler::LinkageLocation loc =
901           call_descriptor->GetInputLocation(call_desc_input_idx);
902       if (loc.IsRegister()) {
903         DCHECK(!loc.IsAnyRegister());
904         RegClass rc = is_gp_pair ? kGpReg : reg_class_for(kind);
905         int reg_code = loc.AsRegister();
906         LiftoffRegister reg =
907             LiftoffRegister::from_external_code(rc, kind, reg_code);
908         param_regs->set(reg);
909         if (is_gp_pair) {
910           stack_transfers->LoadI64HalfIntoRegister(reg, slot, stack_offset,
911                                                    half);
912         } else {
913           stack_transfers->LoadIntoRegister(reg, slot, stack_offset);
914         }
915       } else {
916         DCHECK(loc.IsCallerFrameSlot());
917         int param_offset = -loc.GetLocation() - 1;
918         stack_slots->Add(slot, stack_offset, half, param_offset);
919       }
920     }
921   }
922 }
923 
924 }  // namespace
925 
PrepareBuiltinCall(const ValueKindSig * sig,compiler::CallDescriptor * call_descriptor,std::initializer_list<VarState> params)926 void LiftoffAssembler::PrepareBuiltinCall(
927     const ValueKindSig* sig, compiler::CallDescriptor* call_descriptor,
928     std::initializer_list<VarState> params) {
929   LiftoffStackSlots stack_slots(this);
930   StackTransferRecipe stack_transfers(this);
931   LiftoffRegList param_regs;
932   PrepareStackTransfers(sig, call_descriptor, params.begin(), &stack_slots,
933                         &stack_transfers, &param_regs);
934   SpillAllRegisters();
935   int param_slots = static_cast<int>(call_descriptor->ParameterSlotCount());
936   if (param_slots > 0) {
937     stack_slots.Construct(param_slots);
938   }
939   // Execute the stack transfers before filling the instance register.
940   stack_transfers.Execute();
941 
942   // Reset register use counters.
943   cache_state_.reset_used_registers();
944 }
945 
PrepareCall(const ValueKindSig * sig,compiler::CallDescriptor * call_descriptor,Register * target,Register * target_instance)946 void LiftoffAssembler::PrepareCall(const ValueKindSig* sig,
947                                    compiler::CallDescriptor* call_descriptor,
948                                    Register* target,
949                                    Register* target_instance) {
950   uint32_t num_params = static_cast<uint32_t>(sig->parameter_count());
951   // Input 0 is the call target.
952   constexpr size_t kInputShift = 1;
953 
954   // Spill all cache slots which are not being used as parameters.
955   cache_state_.ClearAllCacheRegisters();
956   for (VarState* it = cache_state_.stack_state.end() - 1 - num_params;
957        it >= cache_state_.stack_state.begin() &&
958        !cache_state_.used_registers.is_empty();
959        --it) {
960     if (!it->is_reg()) continue;
961     Spill(it->offset(), it->reg(), it->kind());
962     cache_state_.dec_used(it->reg());
963     it->MakeStack();
964   }
965 
966   LiftoffStackSlots stack_slots(this);
967   StackTransferRecipe stack_transfers(this);
968   LiftoffRegList param_regs;
969 
970   // Move the target instance (if supplied) into the correct instance register.
971   compiler::LinkageLocation instance_loc =
972       call_descriptor->GetInputLocation(kInputShift);
973   DCHECK(instance_loc.IsRegister() && !instance_loc.IsAnyRegister());
974   Register instance_reg = Register::from_code(instance_loc.AsRegister());
975   param_regs.set(instance_reg);
976   if (target_instance && *target_instance != instance_reg) {
977     stack_transfers.MoveRegister(LiftoffRegister(instance_reg),
978                                  LiftoffRegister(*target_instance),
979                                  kPointerKind);
980   }
981 
982   int param_slots = static_cast<int>(call_descriptor->ParameterSlotCount());
983   if (num_params) {
984     uint32_t param_base = cache_state_.stack_height() - num_params;
985     PrepareStackTransfers(sig, call_descriptor,
986                           &cache_state_.stack_state[param_base], &stack_slots,
987                           &stack_transfers, &param_regs);
988   }
989 
990   // If the target register overlaps with a parameter register, then move the
991   // target to another free register, or spill to the stack.
992   if (target && param_regs.has(LiftoffRegister(*target))) {
993     // Try to find another free register.
994     LiftoffRegList free_regs = kGpCacheRegList.MaskOut(param_regs);
995     if (!free_regs.is_empty()) {
996       LiftoffRegister new_target = free_regs.GetFirstRegSet();
997       stack_transfers.MoveRegister(new_target, LiftoffRegister(*target),
998                                    kPointerKind);
999       *target = new_target.gp();
1000     } else {
1001       stack_slots.Add(VarState(kPointerKind, LiftoffRegister(*target), 0),
1002                       param_slots);
1003       param_slots++;
1004       *target = no_reg;
1005     }
1006   }
1007 
1008   if (param_slots > 0) {
1009     stack_slots.Construct(param_slots);
1010   }
1011   // Execute the stack transfers before filling the instance register.
1012   stack_transfers.Execute();
1013   // Pop parameters from the value stack.
1014   cache_state_.stack_state.pop_back(num_params);
1015 
1016   // Reset register use counters.
1017   cache_state_.reset_used_registers();
1018 
1019   // Reload the instance from the stack.
1020   if (!target_instance) {
1021     FillInstanceInto(instance_reg);
1022   }
1023 }
1024 
FinishCall(const ValueKindSig * sig,compiler::CallDescriptor * call_descriptor)1025 void LiftoffAssembler::FinishCall(const ValueKindSig* sig,
1026                                   compiler::CallDescriptor* call_descriptor) {
1027   int call_desc_return_idx = 0;
1028   for (ValueKind return_kind : sig->returns()) {
1029     DCHECK_LT(call_desc_return_idx, call_descriptor->ReturnCount());
1030     const bool needs_gp_pair = needs_gp_reg_pair(return_kind);
1031     const int num_lowered_params = 1 + needs_gp_pair;
1032     const ValueKind lowered_kind = needs_gp_pair ? kI32 : return_kind;
1033     const RegClass rc = reg_class_for(lowered_kind);
1034     // Initialize to anything, will be set in the loop and used afterwards.
1035     LiftoffRegister reg_pair[2] = {kGpCacheRegList.GetFirstRegSet(),
1036                                    kGpCacheRegList.GetFirstRegSet()};
1037     LiftoffRegList pinned;
1038     for (int pair_idx = 0; pair_idx < num_lowered_params; ++pair_idx) {
1039       compiler::LinkageLocation loc =
1040           call_descriptor->GetReturnLocation(call_desc_return_idx++);
1041       if (loc.IsRegister()) {
1042         DCHECK(!loc.IsAnyRegister());
1043         reg_pair[pair_idx] = LiftoffRegister::from_external_code(
1044             rc, lowered_kind, loc.AsRegister());
1045       } else {
1046         DCHECK(loc.IsCallerFrameSlot());
1047         reg_pair[pair_idx] = GetUnusedRegister(rc, pinned);
1048         // Get slot offset relative to the stack pointer.
1049         int offset = call_descriptor->GetOffsetToReturns();
1050         int return_slot = -loc.GetLocation() - offset - 1;
1051         LoadReturnStackSlot(reg_pair[pair_idx],
1052                             return_slot * kSystemPointerSize, lowered_kind);
1053       }
1054       if (pair_idx == 0) {
1055         pinned.set(reg_pair[0]);
1056       }
1057     }
1058     if (num_lowered_params == 1) {
1059       PushRegister(return_kind, reg_pair[0]);
1060     } else {
1061       PushRegister(return_kind, LiftoffRegister::ForPair(reg_pair[0].gp(),
1062                                                          reg_pair[1].gp()));
1063     }
1064   }
1065   int return_slots = static_cast<int>(call_descriptor->ReturnSlotCount());
1066   RecordUsedSpillOffset(TopSpillOffset() + return_slots * kSystemPointerSize);
1067 }
1068 
Move(LiftoffRegister dst,LiftoffRegister src,ValueKind kind)1069 void LiftoffAssembler::Move(LiftoffRegister dst, LiftoffRegister src,
1070                             ValueKind kind) {
1071   DCHECK_EQ(dst.reg_class(), src.reg_class());
1072   DCHECK_NE(dst, src);
1073   if (kNeedI64RegPair && dst.is_gp_pair()) {
1074     // Use the {StackTransferRecipe} to move pairs, as the registers in the
1075     // pairs might overlap.
1076     StackTransferRecipe(this).MoveRegister(dst, src, kind);
1077   } else if (kNeedS128RegPair && dst.is_fp_pair()) {
1078     // Calling low_fp is fine, Move will automatically check the kind and
1079     // convert this FP to its SIMD register, and use a SIMD move.
1080     Move(dst.low_fp(), src.low_fp(), kind);
1081   } else if (dst.is_gp()) {
1082     Move(dst.gp(), src.gp(), kind);
1083   } else {
1084     Move(dst.fp(), src.fp(), kind);
1085   }
1086 }
1087 
ParallelRegisterMove(base::Vector<const ParallelRegisterMoveTuple> tuples)1088 void LiftoffAssembler::ParallelRegisterMove(
1089     base::Vector<const ParallelRegisterMoveTuple> tuples) {
1090   StackTransferRecipe stack_transfers(this);
1091   for (auto tuple : tuples) {
1092     if (tuple.dst == tuple.src) continue;
1093     stack_transfers.MoveRegister(tuple.dst, tuple.src, tuple.kind);
1094   }
1095 }
1096 
MoveToReturnLocations(const FunctionSig * sig,compiler::CallDescriptor * descriptor)1097 void LiftoffAssembler::MoveToReturnLocations(
1098     const FunctionSig* sig, compiler::CallDescriptor* descriptor) {
1099   StackTransferRecipe stack_transfers(this);
1100   if (sig->return_count() == 1) {
1101     ValueKind return_kind = sig->GetReturn(0).kind();
1102     // Defaults to a gp reg, will be set below if return kind is not gp.
1103     LiftoffRegister return_reg = LiftoffRegister(kGpReturnRegisters[0]);
1104 
1105     if (needs_gp_reg_pair(return_kind)) {
1106       return_reg = LiftoffRegister::ForPair(kGpReturnRegisters[0],
1107                                             kGpReturnRegisters[1]);
1108     } else if (needs_fp_reg_pair(return_kind)) {
1109       return_reg = LiftoffRegister::ForFpPair(kFpReturnRegisters[0]);
1110     } else if (reg_class_for(return_kind) == kFpReg) {
1111       return_reg = LiftoffRegister(kFpReturnRegisters[0]);
1112     } else {
1113       DCHECK_EQ(kGpReg, reg_class_for(return_kind));
1114     }
1115     stack_transfers.LoadIntoRegister(return_reg,
1116                                      cache_state_.stack_state.back(),
1117                                      cache_state_.stack_state.back().offset());
1118     return;
1119   }
1120 
1121   // Slow path for multi-return.
1122   int call_desc_return_idx = 0;
1123   DCHECK_LE(sig->return_count(), cache_state_.stack_height());
1124   VarState* slots = cache_state_.stack_state.end() - sig->return_count();
1125   // Fill return frame slots first to ensure that all potential spills happen
1126   // before we prepare the stack transfers.
1127   for (size_t i = 0; i < sig->return_count(); ++i) {
1128     ValueKind return_kind = sig->GetReturn(i).kind();
1129     bool needs_gp_pair = needs_gp_reg_pair(return_kind);
1130     int num_lowered_params = 1 + needs_gp_pair;
1131     for (int pair_idx = 0; pair_idx < num_lowered_params; ++pair_idx) {
1132       compiler::LinkageLocation loc =
1133           descriptor->GetReturnLocation(call_desc_return_idx++);
1134       if (loc.IsCallerFrameSlot()) {
1135         RegPairHalf half = pair_idx == 0 ? kLowWord : kHighWord;
1136         VarState& slot = slots[i];
1137         LiftoffRegister reg = needs_gp_pair
1138                                   ? LoadI64HalfIntoRegister(slot, half)
1139                                   : LoadToRegister(slot, {});
1140         ValueKind lowered_kind = needs_gp_pair ? kI32 : return_kind;
1141         StoreCallerFrameSlot(reg, -loc.AsCallerFrameSlot(), lowered_kind);
1142       }
1143     }
1144   }
1145   // Prepare and execute stack transfers.
1146   call_desc_return_idx = 0;
1147   for (size_t i = 0; i < sig->return_count(); ++i) {
1148     ValueKind return_kind = sig->GetReturn(i).kind();
1149     bool needs_gp_pair = needs_gp_reg_pair(return_kind);
1150     int num_lowered_params = 1 + needs_gp_pair;
1151     for (int pair_idx = 0; pair_idx < num_lowered_params; ++pair_idx) {
1152       RegPairHalf half = pair_idx == 0 ? kLowWord : kHighWord;
1153       compiler::LinkageLocation loc =
1154           descriptor->GetReturnLocation(call_desc_return_idx++);
1155       if (loc.IsRegister()) {
1156         DCHECK(!loc.IsAnyRegister());
1157         int reg_code = loc.AsRegister();
1158         ValueKind lowered_kind = needs_gp_pair ? kI32 : return_kind;
1159         RegClass rc = reg_class_for(lowered_kind);
1160         LiftoffRegister reg =
1161             LiftoffRegister::from_external_code(rc, return_kind, reg_code);
1162         VarState& slot = slots[i];
1163         if (needs_gp_pair) {
1164           stack_transfers.LoadI64HalfIntoRegister(reg, slot, slot.offset(),
1165                                                   half);
1166         } else {
1167           stack_transfers.LoadIntoRegister(reg, slot, slot.offset());
1168         }
1169       }
1170     }
1171   }
1172 }
1173 
1174 #ifdef ENABLE_SLOW_DCHECKS
ValidateCacheState() const1175 bool LiftoffAssembler::ValidateCacheState() const {
1176   uint32_t register_use_count[kAfterMaxLiftoffRegCode] = {0};
1177   LiftoffRegList used_regs;
1178   for (const VarState& var : cache_state_.stack_state) {
1179     if (!var.is_reg()) continue;
1180     LiftoffRegister reg = var.reg();
1181     if ((kNeedI64RegPair || kNeedS128RegPair) && reg.is_pair()) {
1182       ++register_use_count[reg.low().liftoff_code()];
1183       ++register_use_count[reg.high().liftoff_code()];
1184     } else {
1185       ++register_use_count[reg.liftoff_code()];
1186     }
1187     used_regs.set(reg);
1188   }
1189   for (Register cache_reg :
1190        {cache_state_.cached_instance, cache_state_.cached_mem_start}) {
1191     if (cache_reg != no_reg) {
1192       DCHECK(!used_regs.has(cache_reg));
1193       int liftoff_code = LiftoffRegister{cache_reg}.liftoff_code();
1194       used_regs.set(cache_reg);
1195       DCHECK_EQ(0, register_use_count[liftoff_code]);
1196       register_use_count[liftoff_code] = 1;
1197     }
1198   }
1199   bool valid = memcmp(register_use_count, cache_state_.register_use_count,
1200                       sizeof(register_use_count)) == 0 &&
1201                used_regs == cache_state_.used_registers;
1202   if (valid) return true;
1203   std::ostringstream os;
1204   os << "Error in LiftoffAssembler::ValidateCacheState().\n";
1205   os << "expected: used_regs " << used_regs << ", counts "
1206      << PrintCollection(register_use_count) << "\n";
1207   os << "found:    used_regs " << cache_state_.used_registers << ", counts "
1208      << PrintCollection(cache_state_.register_use_count) << "\n";
1209   os << "Use --trace-wasm-decoder and --trace-liftoff to debug.";
1210   FATAL("%s", os.str().c_str());
1211 }
1212 #endif
1213 
SpillOneRegister(LiftoffRegList candidates)1214 LiftoffRegister LiftoffAssembler::SpillOneRegister(LiftoffRegList candidates) {
1215   // Spill one cached value to free a register.
1216   LiftoffRegister spill_reg = cache_state_.GetNextSpillReg(candidates);
1217   SpillRegister(spill_reg);
1218   return spill_reg;
1219 }
1220 
SpillAdjacentFpRegisters(LiftoffRegList pinned)1221 LiftoffRegister LiftoffAssembler::SpillAdjacentFpRegisters(
1222     LiftoffRegList pinned) {
1223   // We end up in this call only when:
1224   // [1] kNeedS128RegPair, and
1225   // [2] there are no pair of adjacent FP registers that are free
1226   CHECK(kNeedS128RegPair);
1227   DCHECK(!kFpCacheRegList.MaskOut(pinned)
1228               .MaskOut(cache_state_.used_registers)
1229               .HasAdjacentFpRegsSet());
1230 
1231   // Special logic, if the top fp register is even, we might hit a case of an
1232   // invalid register in case 2.
1233   LiftoffRegister last_fp = kFpCacheRegList.GetLastRegSet();
1234   if (last_fp.fp().code() % 2 == 0) {
1235     pinned.set(last_fp);
1236   }
1237 
1238   // We can try to optimize the spilling here:
1239   // 1. Try to get a free fp register, either:
1240   //  a. This register is already free, or
1241   //  b. it had to be spilled.
1242   // 2. If 1a, the adjacent register is used (invariant [2]), spill it.
1243   // 3. If 1b, check the adjacent register:
1244   //  a. If free, done!
1245   //  b. If used, spill it.
1246   // We spill one register in 2 and 3a, and two registers in 3b.
1247 
1248   LiftoffRegister first_reg = GetUnusedRegister(kFpReg, pinned);
1249   LiftoffRegister second_reg = first_reg, low_reg = first_reg;
1250 
1251   if (first_reg.fp().code() % 2 == 0) {
1252     second_reg =
1253         LiftoffRegister::from_liftoff_code(first_reg.liftoff_code() + 1);
1254   } else {
1255     second_reg =
1256         LiftoffRegister::from_liftoff_code(first_reg.liftoff_code() - 1);
1257     low_reg = second_reg;
1258   }
1259 
1260   if (cache_state_.is_used(second_reg)) {
1261     SpillRegister(second_reg);
1262   }
1263 
1264   return low_reg;
1265 }
1266 
SpillRegister(LiftoffRegister reg)1267 void LiftoffAssembler::SpillRegister(LiftoffRegister reg) {
1268   int remaining_uses = cache_state_.get_use_count(reg);
1269   DCHECK_LT(0, remaining_uses);
1270   for (uint32_t idx = cache_state_.stack_height() - 1;; --idx) {
1271     DCHECK_GT(cache_state_.stack_height(), idx);
1272     auto* slot = &cache_state_.stack_state[idx];
1273     if (!slot->is_reg() || !slot->reg().overlaps(reg)) continue;
1274     if (slot->reg().is_pair()) {
1275       // Make sure to decrement *both* registers in a pair, because the
1276       // {clear_used} call below only clears one of them.
1277       cache_state_.dec_used(slot->reg().low());
1278       cache_state_.dec_used(slot->reg().high());
1279       cache_state_.last_spilled_regs.set(slot->reg().low());
1280       cache_state_.last_spilled_regs.set(slot->reg().high());
1281     }
1282     Spill(slot->offset(), slot->reg(), slot->kind());
1283     slot->MakeStack();
1284     if (--remaining_uses == 0) break;
1285   }
1286   cache_state_.clear_used(reg);
1287   cache_state_.last_spilled_regs.set(reg);
1288 }
1289 
set_num_locals(uint32_t num_locals)1290 void LiftoffAssembler::set_num_locals(uint32_t num_locals) {
1291   DCHECK_EQ(0, num_locals_);  // only call this once.
1292   num_locals_ = num_locals;
1293   if (num_locals > kInlineLocalKinds) {
1294     more_local_kinds_ = reinterpret_cast<ValueKind*>(
1295         base::Malloc(num_locals * sizeof(ValueKind)));
1296     DCHECK_NOT_NULL(more_local_kinds_);
1297   }
1298 }
1299 
operator <<(std::ostream & os,VarState slot)1300 std::ostream& operator<<(std::ostream& os, VarState slot) {
1301   os << name(slot.kind()) << ":";
1302   switch (slot.loc()) {
1303     case VarState::kStack:
1304       return os << "s";
1305     case VarState::kRegister:
1306       return os << slot.reg();
1307     case VarState::kIntConst:
1308       return os << "c" << slot.i32_const();
1309   }
1310   UNREACHABLE();
1311 }
1312 
1313 #if DEBUG
CheckCompatibleStackSlotTypes(ValueKind a,ValueKind b)1314 bool CheckCompatibleStackSlotTypes(ValueKind a, ValueKind b) {
1315   if (is_object_reference(a)) {
1316     // Since Liftoff doesn't do accurate type tracking (e.g. on loop back
1317     // edges), we only care that pointer types stay amongst pointer types.
1318     // It's fine if ref/optref overwrite each other.
1319     DCHECK(is_object_reference(b));
1320   } else if (is_rtt(a)) {
1321     // Same for rtt/rtt_with_depth.
1322     DCHECK(is_rtt(b));
1323   } else {
1324     // All other types (primitive numbers, bottom/stmt) must be equal.
1325     DCHECK_EQ(a, b);
1326   }
1327   return true;  // Dummy so this can be called via DCHECK.
1328 }
1329 #endif
1330 
1331 }  // namespace wasm
1332 }  // namespace internal
1333 }  // namespace v8
1334