• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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-compiler.h"
6 
7 #include "src/base/optional.h"
8 #include "src/codegen/assembler-inl.h"
9 // TODO(clemensb): Remove dependences on compiler stuff.
10 #include "src/codegen/external-reference.h"
11 #include "src/codegen/interface-descriptors.h"
12 #include "src/codegen/machine-type.h"
13 #include "src/codegen/macro-assembler-inl.h"
14 #include "src/compiler/linkage.h"
15 #include "src/compiler/wasm-compiler.h"
16 #include "src/logging/counters.h"
17 #include "src/logging/log.h"
18 #include "src/objects/smi.h"
19 #include "src/tracing/trace-event.h"
20 #include "src/utils/ostreams.h"
21 #include "src/utils/utils.h"
22 #include "src/wasm/baseline/liftoff-assembler.h"
23 #include "src/wasm/function-body-decoder-impl.h"
24 #include "src/wasm/function-compiler.h"
25 #include "src/wasm/memory-tracing.h"
26 #include "src/wasm/object-access.h"
27 #include "src/wasm/simd-shuffle.h"
28 #include "src/wasm/wasm-debug.h"
29 #include "src/wasm/wasm-engine.h"
30 #include "src/wasm/wasm-linkage.h"
31 #include "src/wasm/wasm-objects.h"
32 #include "src/wasm/wasm-opcodes-inl.h"
33 
34 namespace v8 {
35 namespace internal {
36 namespace wasm {
37 
38 constexpr auto kRegister = LiftoffAssembler::VarState::kRegister;
39 constexpr auto kIntConst = LiftoffAssembler::VarState::kIntConst;
40 constexpr auto kStack = LiftoffAssembler::VarState::kStack;
41 
42 namespace {
43 
44 #define __ asm_.
45 
46 #define TRACE(...)                                            \
47   do {                                                        \
48     if (FLAG_trace_liftoff) PrintF("[liftoff] " __VA_ARGS__); \
49   } while (false)
50 
51 #define WASM_INSTANCE_OBJECT_FIELD_OFFSET(name) \
52   ObjectAccess::ToTagged(WasmInstanceObject::k##name##Offset)
53 
54 template <int expected_size, int actual_size>
55 struct assert_field_size {
56   static_assert(expected_size == actual_size,
57                 "field in WasmInstance does not have the expected size");
58   static constexpr int size = actual_size;
59 };
60 
61 #define WASM_INSTANCE_OBJECT_FIELD_SIZE(name) \
62   FIELD_SIZE(WasmInstanceObject::k##name##Offset)
63 
64 #define LOAD_INSTANCE_FIELD(dst, name, load_size)                              \
65   __ LoadFromInstance(dst, WASM_INSTANCE_OBJECT_FIELD_OFFSET(name),            \
66                       assert_field_size<WASM_INSTANCE_OBJECT_FIELD_SIZE(name), \
67                                         load_size>::size);
68 
69 #define LOAD_TAGGED_PTR_INSTANCE_FIELD(dst, name)                         \
70   static_assert(WASM_INSTANCE_OBJECT_FIELD_SIZE(name) == kTaggedSize,     \
71                 "field in WasmInstance does not have the expected size"); \
72   __ LoadTaggedPointerFromInstance(dst,                                   \
73                                    WASM_INSTANCE_OBJECT_FIELD_OFFSET(name));
74 
75 #ifdef DEBUG
76 #define DEBUG_CODE_COMMENT(str) \
77   do {                          \
78     __ RecordComment(str);      \
79   } while (false)
80 #else
81 #define DEBUG_CODE_COMMENT(str) ((void)0)
82 #endif
83 
84 constexpr LoadType::LoadTypeValue kPointerLoadType =
85     kSystemPointerSize == 8 ? LoadType::kI64Load : LoadType::kI32Load;
86 
87 constexpr ValueType kPointerValueType =
88     kSystemPointerSize == 8 ? kWasmI64 : kWasmI32;
89 
90 #if V8_TARGET_ARCH_ARM64
91 // On ARM64, the Assembler keeps track of pointers to Labels to resolve
92 // branches to distant targets. Moving labels would confuse the Assembler,
93 // thus store the label on the heap and keep a unique_ptr.
94 class MovableLabel {
95  public:
96   MOVE_ONLY_NO_DEFAULT_CONSTRUCTOR(MovableLabel);
MovableLabel()97   MovableLabel() : label_(new Label()) {}
98 
get()99   Label* get() { return label_.get(); }
100 
101  private:
102   std::unique_ptr<Label> label_;
103 };
104 #else
105 // On all other platforms, just store the Label directly.
106 class MovableLabel {
107  public:
108   MOVE_ONLY_WITH_DEFAULT_CONSTRUCTORS(MovableLabel);
109 
get()110   Label* get() { return &label_; }
111 
112  private:
113   Label label_;
114 };
115 #endif
116 
GetLoweredCallDescriptor(Zone * zone,compiler::CallDescriptor * call_desc)117 compiler::CallDescriptor* GetLoweredCallDescriptor(
118     Zone* zone, compiler::CallDescriptor* call_desc) {
119   return kSystemPointerSize == 4
120              ? compiler::GetI32WasmCallDescriptor(zone, call_desc)
121              : call_desc;
122 }
123 
124 constexpr ValueType kSupportedTypesArr[] = {
125     kWasmI32,  kWasmI64,       kWasmF32,    kWasmF64,
126     kWasmS128, kWasmExternRef, kWasmFuncRef};
127 constexpr Vector<const ValueType> kSupportedTypes =
128     ArrayVector(kSupportedTypesArr);
129 
130 constexpr ValueType kSupportedTypesWithoutRefsArr[] = {
131     kWasmI32, kWasmI64, kWasmF32, kWasmF64, kWasmS128};
132 constexpr Vector<const ValueType> kSupportedTypesWithoutRefs =
133     ArrayVector(kSupportedTypesWithoutRefsArr);
134 
GetCompareCondition(WasmOpcode opcode)135 constexpr Condition GetCompareCondition(WasmOpcode opcode) {
136   switch (opcode) {
137     case kExprI32Eq:
138       return kEqual;
139     case kExprI32Ne:
140       return kUnequal;
141     case kExprI32LtS:
142       return kSignedLessThan;
143     case kExprI32LtU:
144       return kUnsignedLessThan;
145     case kExprI32GtS:
146       return kSignedGreaterThan;
147     case kExprI32GtU:
148       return kUnsignedGreaterThan;
149     case kExprI32LeS:
150       return kSignedLessEqual;
151     case kExprI32LeU:
152       return kUnsignedLessEqual;
153     case kExprI32GeS:
154       return kSignedGreaterEqual;
155     case kExprI32GeU:
156       return kUnsignedGreaterEqual;
157     default:
158 #if V8_HAS_CXX14_CONSTEXPR
159       UNREACHABLE();
160 #else
161       // We need to return something for old compilers here.
162       return kEqual;
163 #endif
164   }
165 }
166 
167 // Builds a {DebugSideTable}.
168 class DebugSideTableBuilder {
169  public:
170   enum AssumeSpilling {
171     // All register values will be spilled before the pc covered by the debug
172     // side table entry. Register slots will be marked as stack slots in the
173     // generated debug side table entry.
174     kAssumeSpilling,
175     // Register slots will be written out as they are.
176     kAllowRegisters,
177     // Register slots cannot appear since we already spilled.
178     kDidSpill
179   };
180 
181   class EntryBuilder {
182    public:
EntryBuilder(int pc_offset,std::vector<DebugSideTable::Entry::Value> values)183     explicit EntryBuilder(int pc_offset,
184                           std::vector<DebugSideTable::Entry::Value> values)
185         : pc_offset_(pc_offset), values_(std::move(values)) {}
186 
ToTableEntry()187     DebugSideTable::Entry ToTableEntry() {
188       return DebugSideTable::Entry{pc_offset_, std::move(values_)};
189     }
190 
pc_offset() const191     int pc_offset() const { return pc_offset_; }
set_pc_offset(int new_pc_offset)192     void set_pc_offset(int new_pc_offset) { pc_offset_ = new_pc_offset; }
193 
194    private:
195     int pc_offset_;
196     std::vector<DebugSideTable::Entry::Value> values_;
197   };
198 
199   // Adds a new entry, and returns a pointer to a builder for modifying that
200   // entry ({stack_height} includes {num_locals}).
NewEntry(int pc_offset,int num_locals,int stack_height,LiftoffAssembler::VarState * stack_state,AssumeSpilling assume_spilling)201   EntryBuilder* NewEntry(int pc_offset, int num_locals, int stack_height,
202                          LiftoffAssembler::VarState* stack_state,
203                          AssumeSpilling assume_spilling) {
204     DCHECK_LE(num_locals, stack_height);
205     // Record stack types.
206     std::vector<DebugSideTable::Entry::Value> values(stack_height);
207     for (int i = 0; i < stack_height; ++i) {
208       const auto& slot = stack_state[i];
209       values[i].type = slot.type();
210       values[i].stack_offset = slot.offset();
211       switch (slot.loc()) {
212         case kIntConst:
213           values[i].kind = DebugSideTable::Entry::kConstant;
214           values[i].i32_const = slot.i32_const();
215           break;
216         case kRegister:
217           DCHECK_NE(kDidSpill, assume_spilling);
218           if (assume_spilling == kAllowRegisters) {
219             values[i].kind = DebugSideTable::Entry::kRegister;
220             values[i].reg_code = slot.reg().liftoff_code();
221             break;
222           }
223           DCHECK_EQ(kAssumeSpilling, assume_spilling);
224           V8_FALLTHROUGH;
225         case kStack:
226           values[i].kind = DebugSideTable::Entry::kStack;
227           values[i].stack_offset = slot.offset();
228           break;
229       }
230     }
231     entries_.emplace_back(pc_offset, std::move(values));
232     return &entries_.back();
233   }
234 
SetNumLocals(int num_locals)235   void SetNumLocals(int num_locals) {
236     DCHECK_EQ(-1, num_locals_);
237     DCHECK_LE(0, num_locals);
238     num_locals_ = num_locals;
239   }
240 
GenerateDebugSideTable()241   std::unique_ptr<DebugSideTable> GenerateDebugSideTable() {
242     DCHECK_LE(0, num_locals_);
243     std::vector<DebugSideTable::Entry> entries;
244     entries.reserve(entries_.size());
245     for (auto& entry : entries_) entries.push_back(entry.ToTableEntry());
246     std::sort(entries.begin(), entries.end(),
247               [](DebugSideTable::Entry& a, DebugSideTable::Entry& b) {
248                 return a.pc_offset() < b.pc_offset();
249               });
250     return std::make_unique<DebugSideTable>(num_locals_, std::move(entries));
251   }
252 
253  private:
254   int num_locals_ = -1;
255   std::list<EntryBuilder> entries_;
256 };
257 
258 class LiftoffCompiler {
259  public:
260   // TODO(clemensb): Make this a template parameter.
261   static constexpr Decoder::ValidateFlag validate = Decoder::kBooleanValidation;
262 
263   using Value = ValueBase<validate>;
264 
265   static constexpr auto kI32 = ValueType::kI32;
266   static constexpr auto kI64 = ValueType::kI64;
267   static constexpr auto kF32 = ValueType::kF32;
268   static constexpr auto kF64 = ValueType::kF64;
269   static constexpr auto kS128 = ValueType::kS128;
270 
271   struct ElseState {
272     MovableLabel label;
273     LiftoffAssembler::CacheState state;
274   };
275 
276   struct Control : public ControlBase<Value, validate> {
277     std::unique_ptr<ElseState> else_state;
278     LiftoffAssembler::CacheState label_state;
279     MovableLabel label;
280 
281     MOVE_ONLY_NO_DEFAULT_CONSTRUCTOR(Control);
282 
283     template <typename... Args>
Controlv8::internal::wasm::__anon69d3db0f0111::LiftoffCompiler::Control284     explicit Control(Args&&... args) V8_NOEXCEPT
285         : ControlBase(std::forward<Args>(args)...) {}
286   };
287 
288   using FullDecoder = WasmFullDecoder<validate, LiftoffCompiler>;
289 
290   // For debugging, we need to spill registers before a trap or a stack check to
291   // be able to inspect them.
292   struct SpilledRegistersForInspection : public ZoneObject {
293     struct Entry {
294       int offset;
295       LiftoffRegister reg;
296       ValueType type;
297     };
298     ZoneVector<Entry> entries;
299 
SpilledRegistersForInspectionv8::internal::wasm::__anon69d3db0f0111::LiftoffCompiler::SpilledRegistersForInspection300     explicit SpilledRegistersForInspection(Zone* zone) : entries(zone) {}
301   };
302 
303   struct OutOfLineCode {
304     MovableLabel label;
305     MovableLabel continuation;
306     WasmCode::RuntimeStubId stub;
307     WasmCodePosition position;
308     LiftoffRegList regs_to_save;
309     uint32_t pc;  // for trap handler.
310     // These two pointers will only be used for debug code:
311     DebugSideTableBuilder::EntryBuilder* debug_sidetable_entry_builder;
312     SpilledRegistersForInspection* spilled_registers;
313 
314     // Named constructors:
Trapv8::internal::wasm::__anon69d3db0f0111::LiftoffCompiler::OutOfLineCode315     static OutOfLineCode Trap(
316         WasmCode::RuntimeStubId s, WasmCodePosition pos, uint32_t pc,
317         DebugSideTableBuilder::EntryBuilder* debug_sidetable_entry_builder,
318         SpilledRegistersForInspection* spilled_registers) {
319       DCHECK_LT(0, pos);
320       return {{},
321               {},
322               s,
323               pos,
324               {},
325               pc,
326               debug_sidetable_entry_builder,
327               spilled_registers};
328     }
StackCheckv8::internal::wasm::__anon69d3db0f0111::LiftoffCompiler::OutOfLineCode329     static OutOfLineCode StackCheck(
330         WasmCodePosition pos, LiftoffRegList regs_to_save,
331         SpilledRegistersForInspection* spilled_regs,
332         DebugSideTableBuilder::EntryBuilder* debug_sidetable_entry_builder) {
333       return {{},           {}, WasmCode::kWasmStackGuard,     pos,
334               regs_to_save, 0,  debug_sidetable_entry_builder, spilled_regs};
335     }
336   };
337 
LiftoffCompiler(compiler::CallDescriptor * call_descriptor,CompilationEnv * env,Zone * compilation_zone,std::unique_ptr<AssemblerBuffer> buffer,DebugSideTableBuilder * debug_sidetable_builder,ForDebugging for_debugging,int func_index,Vector<int> breakpoints={},int dead_breakpoint=0)338   LiftoffCompiler(compiler::CallDescriptor* call_descriptor,
339                   CompilationEnv* env, Zone* compilation_zone,
340                   std::unique_ptr<AssemblerBuffer> buffer,
341                   DebugSideTableBuilder* debug_sidetable_builder,
342                   ForDebugging for_debugging, int func_index,
343                   Vector<int> breakpoints = {}, int dead_breakpoint = 0)
344       : asm_(std::move(buffer)),
345         descriptor_(
346             GetLoweredCallDescriptor(compilation_zone, call_descriptor)),
347         env_(env),
348         debug_sidetable_builder_(debug_sidetable_builder),
349         for_debugging_(for_debugging),
350         func_index_(func_index),
351         out_of_line_code_(compilation_zone),
352         source_position_table_builder_(compilation_zone),
353         protected_instructions_(compilation_zone),
354         compilation_zone_(compilation_zone),
355         safepoint_table_builder_(compilation_zone_),
356         next_breakpoint_ptr_(breakpoints.begin()),
357         next_breakpoint_end_(breakpoints.end()),
358         dead_breakpoint_(dead_breakpoint) {
359     if (breakpoints.empty()) {
360       next_breakpoint_ptr_ = next_breakpoint_end_ = nullptr;
361     }
362   }
363 
did_bailout() const364   bool did_bailout() const { return bailout_reason_ != kSuccess; }
bailout_reason() const365   LiftoffBailoutReason bailout_reason() const { return bailout_reason_; }
366 
GetCode(CodeDesc * desc)367   void GetCode(CodeDesc* desc) {
368     asm_.GetCode(nullptr, desc, &safepoint_table_builder_,
369                  Assembler::kNoHandlerTable);
370   }
371 
GetSourcePositionTable()372   OwnedVector<uint8_t> GetSourcePositionTable() {
373     return source_position_table_builder_.ToSourcePositionTableVector();
374   }
375 
GetProtectedInstructionsData() const376   OwnedVector<uint8_t> GetProtectedInstructionsData() const {
377     return OwnedVector<uint8_t>::Of(
378         Vector<const uint8_t>::cast(VectorOf(protected_instructions_)));
379   }
380 
GetTotalFrameSlotCountForGC() const381   uint32_t GetTotalFrameSlotCountForGC() const {
382     return __ GetTotalFrameSlotCountForGC();
383   }
384 
unsupported(FullDecoder * decoder,LiftoffBailoutReason reason,const char * detail)385   void unsupported(FullDecoder* decoder, LiftoffBailoutReason reason,
386                    const char* detail) {
387     DCHECK_NE(kSuccess, reason);
388     if (did_bailout()) return;
389     bailout_reason_ = reason;
390     TRACE("unsupported: %s\n", detail);
391     decoder->errorf(decoder->pc_offset(), "unsupported liftoff operation: %s",
392                     detail);
393     UnuseLabels(decoder);
394   }
395 
DidAssemblerBailout(FullDecoder * decoder)396   bool DidAssemblerBailout(FullDecoder* decoder) {
397     if (decoder->failed() || !__ did_bailout()) return false;
398     unsupported(decoder, __ bailout_reason(), __ bailout_detail());
399     return true;
400   }
401 
BailoutReasonForType(ValueType type)402   LiftoffBailoutReason BailoutReasonForType(ValueType type) {
403     switch (type.kind()) {
404       case ValueType::kS128:
405         return kSimd;
406       case ValueType::kOptRef:
407       case ValueType::kRef:
408         if (type.is_reference_to(HeapType::kExn)) {
409           return kExceptionHandling;
410         } else {
411           return kRefTypes;
412         }
413       case ValueType::kBottom:
414         return kMultiValue;
415       default:
416         return kOtherReason;
417     }
418   }
419 
CheckSupportedType(FullDecoder * decoder,Vector<const ValueType> supported_types,ValueType type,const char * context)420   bool CheckSupportedType(FullDecoder* decoder,
421                           Vector<const ValueType> supported_types,
422                           ValueType type, const char* context) {
423     // Special case for kWasm128 which requires specific hardware support.
424     if (type == kWasmS128 && (!CpuFeatures::SupportsWasmSimd128())) {
425       unsupported(decoder, kSimd, "simd");
426       return false;
427     }
428     // Check supported types.
429     for (ValueType supported : supported_types) {
430       if (type == supported) return true;
431     }
432     LiftoffBailoutReason bailout_reason = BailoutReasonForType(type);
433     EmbeddedVector<char, 128> buffer;
434     SNPrintF(buffer, "%s %s", type.name().c_str(), context);
435     unsupported(decoder, bailout_reason, buffer.begin());
436     return false;
437   }
438 
GetSafepointTableOffset() const439   int GetSafepointTableOffset() const {
440     return safepoint_table_builder_.GetCodeOffset();
441   }
442 
UnuseLabels(FullDecoder * decoder)443   void UnuseLabels(FullDecoder* decoder) {
444 #ifdef DEBUG
445     auto Unuse = [](Label* label) {
446       label->Unuse();
447       label->UnuseNear();
448     };
449     // Unuse all labels now, otherwise their destructor will fire a DCHECK error
450     // if they where referenced before.
451     uint32_t control_depth = decoder ? decoder->control_depth() : 0;
452     for (uint32_t i = 0; i < control_depth; ++i) {
453       Control* c = decoder->control_at(i);
454       Unuse(c->label.get());
455       if (c->else_state) Unuse(c->else_state->label.get());
456     }
457     for (auto& ool : out_of_line_code_) Unuse(ool.label.get());
458 #endif
459   }
460 
StartFunction(FullDecoder * decoder)461   void StartFunction(FullDecoder* decoder) {
462     if (FLAG_trace_liftoff && !FLAG_trace_wasm_decoder) {
463       StdoutStream{} << "hint: add --trace-wasm-decoder to also see the wasm "
464                         "instructions being decoded\n";
465     }
466     int num_locals = decoder->num_locals();
467     __ set_num_locals(num_locals);
468     for (int i = 0; i < num_locals; ++i) {
469       ValueType type = decoder->local_type(i);
470       __ set_local_type(i, type);
471     }
472   }
473 
474   // Returns the number of inputs processed (1 or 2).
ProcessParameter(ValueType type,uint32_t input_idx)475   uint32_t ProcessParameter(ValueType type, uint32_t input_idx) {
476     const bool needs_pair = needs_gp_reg_pair(type);
477     const ValueType reg_type = needs_pair ? kWasmI32 : type;
478     const RegClass rc = reg_class_for(reg_type);
479 
480     auto LoadToReg = [this, reg_type, rc](compiler::LinkageLocation location,
481                                           LiftoffRegList pinned) {
482       if (location.IsRegister()) {
483         DCHECK(!location.IsAnyRegister());
484         return LiftoffRegister::from_external_code(rc, reg_type,
485                                                    location.AsRegister());
486       }
487       DCHECK(location.IsCallerFrameSlot());
488       LiftoffRegister reg = __ GetUnusedRegister(rc, pinned);
489       __ LoadCallerFrameSlot(reg, -location.AsCallerFrameSlot(), reg_type);
490       return reg;
491     };
492 
493     LiftoffRegister reg =
494         LoadToReg(descriptor_->GetInputLocation(input_idx), {});
495     if (needs_pair) {
496       LiftoffRegister reg2 =
497           LoadToReg(descriptor_->GetInputLocation(input_idx + 1),
498                     LiftoffRegList::ForRegs(reg));
499       reg = LiftoffRegister::ForPair(reg.gp(), reg2.gp());
500     }
501     __ PushRegister(type, reg);
502 
503     return needs_pair ? 2 : 1;
504   }
505 
StackCheck(WasmCodePosition position)506   void StackCheck(WasmCodePosition position) {
507     DEBUG_CODE_COMMENT("stack check");
508     if (!FLAG_wasm_stack_checks || !env_->runtime_exception_support) return;
509     LiftoffRegList regs_to_save = __ cache_state()->used_registers;
510     SpilledRegistersForInspection* spilled_regs = nullptr;
511     Register limit_address = __ GetUnusedRegister(kGpReg, {}).gp();
512     if (V8_UNLIKELY(for_debugging_)) {
513       regs_to_save = {};
514       spilled_regs = GetSpilledRegistersForInspection();
515     }
516     out_of_line_code_.push_back(OutOfLineCode::StackCheck(
517         position, regs_to_save, spilled_regs,
518         RegisterDebugSideTableEntry(DebugSideTableBuilder::kAssumeSpilling)));
519     OutOfLineCode& ool = out_of_line_code_.back();
520     LOAD_INSTANCE_FIELD(limit_address, StackLimitAddress, kSystemPointerSize);
521     __ StackCheck(ool.label.get(), limit_address);
522     __ bind(ool.continuation.get());
523   }
524 
SpillLocalsInitially(FullDecoder * decoder,uint32_t num_params)525   bool SpillLocalsInitially(FullDecoder* decoder, uint32_t num_params) {
526     int actual_locals = __ num_locals() - num_params;
527     DCHECK_LE(0, actual_locals);
528     constexpr int kNumCacheRegisters = NumRegs(kLiftoffAssemblerGpCacheRegs);
529     // If we have many locals, we put them on the stack initially. This avoids
530     // having to spill them on merge points. Use of these initial values should
531     // be rare anyway.
532     if (actual_locals > kNumCacheRegisters / 2) return true;
533     // If there are locals which are not i32 or i64, we also spill all locals,
534     // because other types cannot be initialized to constants.
535     for (uint32_t param_idx = num_params; param_idx < __ num_locals();
536          ++param_idx) {
537       ValueType type = decoder->local_type(param_idx);
538       if (type != kWasmI32 && type != kWasmI64) return true;
539     }
540     return false;
541   }
542 
TierUpFunction(FullDecoder * decoder)543   void TierUpFunction(FullDecoder* decoder) {
544     __ CallRuntimeStub(WasmCode::kWasmTriggerTierUp);
545     DefineSafepoint();
546   }
547 
TraceFunctionEntry(FullDecoder * decoder)548   void TraceFunctionEntry(FullDecoder* decoder) {
549     DEBUG_CODE_COMMENT("trace function entry");
550     __ SpillAllRegisters();
551     source_position_table_builder_.AddPosition(
552         __ pc_offset(), SourcePosition(decoder->position()), false);
553     __ CallRuntimeStub(WasmCode::kWasmTraceEnter);
554     DefineSafepoint();
555   }
556 
StartFunctionBody(FullDecoder * decoder,Control * block)557   void StartFunctionBody(FullDecoder* decoder, Control* block) {
558     for (uint32_t i = 0; i < __ num_locals(); ++i) {
559       if (!CheckSupportedType(decoder,
560                               FLAG_experimental_liftoff_extern_ref
561                                   ? kSupportedTypes
562                                   : kSupportedTypesWithoutRefs,
563                               __ local_type(i), "param"))
564         return;
565     }
566 
567     // Input 0 is the call target, the instance is at 1.
568     constexpr int kInstanceParameterIndex = 1;
569     // Store the instance parameter to a special stack slot.
570     compiler::LinkageLocation instance_loc =
571         descriptor_->GetInputLocation(kInstanceParameterIndex);
572     DCHECK(instance_loc.IsRegister());
573     DCHECK(!instance_loc.IsAnyRegister());
574     Register instance_reg = Register::from_code(instance_loc.AsRegister());
575     DCHECK_EQ(kWasmInstanceRegister, instance_reg);
576 
577     // Parameter 0 is the instance parameter.
578     uint32_t num_params =
579         static_cast<uint32_t>(decoder->sig_->parameter_count());
580 
581     __ CodeEntry();
582 
583     DEBUG_CODE_COMMENT("enter frame");
584     __ EnterFrame(StackFrame::WASM);
585     __ set_has_frame(true);
586     pc_offset_stack_frame_construction_ = __ PrepareStackFrame();
587     // {PrepareStackFrame} is the first platform-specific assembler method.
588     // If this failed, we can bail out immediately, avoiding runtime overhead
589     // and potential failures because of other unimplemented methods.
590     // A platform implementing {PrepareStackFrame} must ensure that we can
591     // finish compilation without errors even if we hit unimplemented
592     // LiftoffAssembler methods.
593     if (DidAssemblerBailout(decoder)) return;
594 
595     // Process parameters.
596     if (num_params) DEBUG_CODE_COMMENT("process parameters");
597     __ SpillInstance(instance_reg);
598     // Input 0 is the code target, 1 is the instance. First parameter at 2.
599     uint32_t input_idx = kInstanceParameterIndex + 1;
600     for (uint32_t param_idx = 0; param_idx < num_params; ++param_idx) {
601       input_idx += ProcessParameter(__ local_type(param_idx), input_idx);
602     }
603     int params_size = __ TopSpillOffset();
604     DCHECK_EQ(input_idx, descriptor_->InputCount());
605 
606     // Initialize locals beyond parameters.
607     if (num_params < __ num_locals()) DEBUG_CODE_COMMENT("init locals");
608     if (SpillLocalsInitially(decoder, num_params)) {
609       for (uint32_t param_idx = num_params; param_idx < __ num_locals();
610            ++param_idx) {
611         ValueType type = decoder->local_type(param_idx);
612         __ PushStack(type);
613       }
614       int spill_size = __ TopSpillOffset() - params_size;
615       __ FillStackSlotsWithZero(params_size, spill_size);
616     } else {
617       for (uint32_t param_idx = num_params; param_idx < __ num_locals();
618            ++param_idx) {
619         ValueType type = decoder->local_type(param_idx);
620         __ PushConstant(type, int32_t{0});
621       }
622     }
623 
624     if (FLAG_experimental_liftoff_extern_ref) {
625       // Initialize all reference type locals with ref.null.
626       for (uint32_t param_idx = num_params; param_idx < __ num_locals();
627            ++param_idx) {
628         ValueType type = decoder->local_type(param_idx);
629         if (type.is_reference_type()) {
630           Register isolate_root = __ GetUnusedRegister(kGpReg, {}).gp();
631           // We can re-use the isolate_root register as result register.
632           Register result = isolate_root;
633 
634           LOAD_INSTANCE_FIELD(isolate_root, IsolateRoot, kSystemPointerSize);
635           __ LoadTaggedPointer(
636               result, isolate_root, no_reg,
637               IsolateData::root_slot_offset(RootIndex::kNullValue), {});
638           __ Spill(__ cache_state()->stack_state.back().offset(),
639                    LiftoffRegister(result), type);
640         }
641       }
642     }
643     DCHECK_EQ(__ num_locals(), __ cache_state()->stack_height());
644 
645     if (V8_UNLIKELY(debug_sidetable_builder_)) {
646       debug_sidetable_builder_->SetNumLocals(__ num_locals());
647     }
648 
649     // The function-prologue stack check is associated with position 0, which
650     // is never a position of any instruction in the function.
651     StackCheck(0);
652 
653     if (FLAG_wasm_dynamic_tiering) {
654       // TODO(arobin): Avoid spilling registers unconditionally.
655       __ SpillAllRegisters();
656       DEBUG_CODE_COMMENT("dynamic tiering");
657       LiftoffRegList pinned;
658 
659       // Get the number of calls array address.
660       LiftoffRegister array_address =
661           pinned.set(__ GetUnusedRegister(kGpReg, pinned));
662       LOAD_INSTANCE_FIELD(array_address.gp(), NumLiftoffFunctionCallsArray,
663                           kSystemPointerSize);
664 
665       // Compute the correct offset in the array.
666       uint32_t offset =
667           kInt32Size * declared_function_index(env_->module, func_index_);
668 
669       // Get the number of calls and update it.
670       LiftoffRegister old_number_of_calls =
671           pinned.set(__ GetUnusedRegister(kGpReg, pinned));
672       LiftoffRegister new_number_of_calls =
673           pinned.set(__ GetUnusedRegister(kGpReg, pinned));
674       __ Load(old_number_of_calls, array_address.gp(), no_reg, offset,
675               LoadType::kI32Load, pinned);
676       __ emit_i32_addi(new_number_of_calls.gp(), old_number_of_calls.gp(), 1);
677       __ Store(array_address.gp(), no_reg, offset, new_number_of_calls,
678                StoreType::kI32Store, pinned);
679 
680       // Emit the runtime call if necessary.
681       Label no_tierup;
682       // Check if the number of calls is a power of 2.
683       __ emit_i32_and(old_number_of_calls.gp(), old_number_of_calls.gp(),
684                       new_number_of_calls.gp());
685       // Unary "unequal" means "different from zero".
686       __ emit_cond_jump(kUnequal, &no_tierup, kWasmI32,
687                         old_number_of_calls.gp());
688       TierUpFunction(decoder);
689       __ bind(&no_tierup);
690     }
691 
692     if (FLAG_trace_wasm) TraceFunctionEntry(decoder);
693   }
694 
GenerateOutOfLineCode(OutOfLineCode * ool)695   void GenerateOutOfLineCode(OutOfLineCode* ool) {
696     DEBUG_CODE_COMMENT(
697         (std::string("out of line: ") + GetRuntimeStubName(ool->stub)).c_str());
698     __ bind(ool->label.get());
699     const bool is_stack_check = ool->stub == WasmCode::kWasmStackGuard;
700     const bool is_mem_out_of_bounds =
701         ool->stub == WasmCode::kThrowWasmTrapMemOutOfBounds;
702 
703     if (is_mem_out_of_bounds && env_->use_trap_handler) {
704       uint32_t pc = static_cast<uint32_t>(__ pc_offset());
705       DCHECK_EQ(pc, __ pc_offset());
706       protected_instructions_.emplace_back(
707           trap_handler::ProtectedInstructionData{ool->pc, pc});
708     }
709 
710     if (!env_->runtime_exception_support) {
711       // We cannot test calls to the runtime in cctest/test-run-wasm.
712       // Therefore we emit a call to C here instead of a call to the runtime.
713       // In this mode, we never generate stack checks.
714       DCHECK(!is_stack_check);
715       __ CallTrapCallbackForTesting();
716       DEBUG_CODE_COMMENT("leave frame");
717       __ LeaveFrame(StackFrame::WASM);
718       __ DropStackSlotsAndRet(
719           static_cast<uint32_t>(descriptor_->StackParameterCount()));
720       return;
721     }
722 
723     // We cannot both push and spill registers.
724     DCHECK(ool->regs_to_save.is_empty() || ool->spilled_registers == nullptr);
725     if (!ool->regs_to_save.is_empty()) {
726       __ PushRegisters(ool->regs_to_save);
727     } else if (V8_UNLIKELY(ool->spilled_registers != nullptr)) {
728       for (auto& entry : ool->spilled_registers->entries) {
729         __ Spill(entry.offset, entry.reg, entry.type);
730       }
731     }
732 
733     source_position_table_builder_.AddPosition(
734         __ pc_offset(), SourcePosition(ool->position), true);
735     __ CallRuntimeStub(ool->stub);
736     // TODO(ahaas): Define a proper safepoint here.
737     safepoint_table_builder_.DefineSafepoint(&asm_, Safepoint::kNoLazyDeopt);
738     DCHECK_EQ(!debug_sidetable_builder_, !ool->debug_sidetable_entry_builder);
739     if (V8_UNLIKELY(ool->debug_sidetable_entry_builder)) {
740       ool->debug_sidetable_entry_builder->set_pc_offset(__ pc_offset());
741     }
742     DCHECK_EQ(ool->continuation.get()->is_bound(), is_stack_check);
743     if (!ool->regs_to_save.is_empty()) __ PopRegisters(ool->regs_to_save);
744     if (is_stack_check) {
745       if (V8_UNLIKELY(ool->spilled_registers != nullptr)) {
746         DCHECK(for_debugging_);
747         for (auto& entry : ool->spilled_registers->entries) {
748           __ Fill(entry.reg, entry.offset, entry.type);
749         }
750       }
751       __ emit_jump(ool->continuation.get());
752     } else {
753       __ AssertUnreachable(AbortReason::kUnexpectedReturnFromWasmTrap);
754     }
755   }
756 
FinishFunction(FullDecoder * decoder)757   void FinishFunction(FullDecoder* decoder) {
758     if (DidAssemblerBailout(decoder)) return;
759     for (OutOfLineCode& ool : out_of_line_code_) {
760       GenerateOutOfLineCode(&ool);
761     }
762     __ PatchPrepareStackFrame(pc_offset_stack_frame_construction_,
763                               __ GetTotalFrameSize());
764     __ FinishCode();
765     safepoint_table_builder_.Emit(&asm_, __ GetTotalFrameSlotCountForGC());
766     __ MaybeEmitOutOfLineConstantPool();
767     // The previous calls may have also generated a bailout.
768     DidAssemblerBailout(decoder);
769   }
770 
OnFirstError(FullDecoder * decoder)771   void OnFirstError(FullDecoder* decoder) {
772     if (!did_bailout()) bailout_reason_ = kDecodeError;
773     UnuseLabels(decoder);
774     asm_.AbortCompilation();
775   }
776 
EmitDebuggingInfo(FullDecoder * decoder,WasmOpcode opcode)777   V8_NOINLINE void EmitDebuggingInfo(FullDecoder* decoder, WasmOpcode opcode) {
778     DCHECK(for_debugging_);
779     if (!WasmOpcodes::IsBreakable(opcode)) return;
780     bool has_breakpoint = false;
781     if (next_breakpoint_ptr_) {
782       if (*next_breakpoint_ptr_ == 0) {
783         // A single breakpoint at offset 0 indicates stepping.
784         DCHECK_EQ(next_breakpoint_ptr_ + 1, next_breakpoint_end_);
785         has_breakpoint = true;
786       } else {
787         while (next_breakpoint_ptr_ != next_breakpoint_end_ &&
788                *next_breakpoint_ptr_ < decoder->position()) {
789           // Skip unreachable breakpoints.
790           ++next_breakpoint_ptr_;
791         }
792         if (next_breakpoint_ptr_ == next_breakpoint_end_) {
793           next_breakpoint_ptr_ = next_breakpoint_end_ = nullptr;
794         } else if (*next_breakpoint_ptr_ == decoder->position()) {
795           has_breakpoint = true;
796         }
797       }
798     }
799     if (has_breakpoint) {
800       EmitBreakpoint(decoder);
801       // Once we emitted a breakpoint, we don't need to check the "hook on
802       // function call" any more.
803       checked_hook_on_function_call_ = true;
804     } else if (!checked_hook_on_function_call_) {
805       checked_hook_on_function_call_ = true;
806       // Check the "hook on function call" flag. If set, trigger a break.
807       DEBUG_CODE_COMMENT("check hook on function call");
808       Register flag = __ GetUnusedRegister(kGpReg, {}).gp();
809       LOAD_INSTANCE_FIELD(flag, HookOnFunctionCallAddress, kSystemPointerSize);
810       Label no_break;
811       __ Load(LiftoffRegister{flag}, flag, no_reg, 0, LoadType::kI32Load8U, {});
812       // Unary "equal" means "equals zero".
813       __ emit_cond_jump(kEqual, &no_break, kWasmI32, flag);
814       EmitBreakpoint(decoder);
815       __ bind(&no_break);
816     } else if (dead_breakpoint_ == decoder->position()) {
817       DCHECK(!next_breakpoint_ptr_ ||
818              *next_breakpoint_ptr_ != dead_breakpoint_);
819       // The top frame is paused at this position, but the breakpoint was
820       // removed. Adding a dead breakpoint here ensures that the source
821       // position exists, and that the offset to the return address is the
822       // same as in the old code.
823       Label cont;
824       __ emit_jump(&cont);
825       EmitBreakpoint(decoder);
826       __ bind(&cont);
827     }
828   }
829 
NextInstruction(FullDecoder * decoder,WasmOpcode opcode)830   void NextInstruction(FullDecoder* decoder, WasmOpcode opcode) {
831     // Add a single check, so that the fast path can be inlined while
832     // {EmitDebuggingInfo} stays outlined.
833     if (V8_UNLIKELY(for_debugging_)) EmitDebuggingInfo(decoder, opcode);
834     TraceCacheState(decoder);
835 #ifdef DEBUG
836     SLOW_DCHECK(__ ValidateCacheState());
837     if (WasmOpcodes::IsPrefixOpcode(opcode)) {
838       opcode = decoder->read_prefixed_opcode<Decoder::kFullValidation>(
839           decoder->pc());
840     }
841     DEBUG_CODE_COMMENT(WasmOpcodes::OpcodeName(opcode));
842 #endif
843   }
844 
EmitBreakpoint(FullDecoder * decoder)845   void EmitBreakpoint(FullDecoder* decoder) {
846     DEBUG_CODE_COMMENT("breakpoint");
847     DCHECK(for_debugging_);
848     source_position_table_builder_.AddPosition(
849         __ pc_offset(), SourcePosition(decoder->position()), true);
850     __ CallRuntimeStub(WasmCode::kWasmDebugBreak);
851     // TODO(ahaas): Define a proper safepoint here.
852     safepoint_table_builder_.DefineSafepoint(&asm_, Safepoint::kNoLazyDeopt);
853     RegisterDebugSideTableEntry(DebugSideTableBuilder::kAllowRegisters);
854   }
855 
Block(FullDecoder * decoder,Control * block)856   void Block(FullDecoder* decoder, Control* block) {}
857 
Loop(FullDecoder * decoder,Control * loop)858   void Loop(FullDecoder* decoder, Control* loop) {
859     // Before entering a loop, spill all locals to the stack, in order to free
860     // the cache registers, and to avoid unnecessarily reloading stack values
861     // into registers at branches.
862     // TODO(clemensb): Come up with a better strategy here, involving
863     // pre-analysis of the function.
864     __ SpillLocals();
865 
866     __ PrepareLoopArgs(loop->start_merge.arity);
867 
868     // Loop labels bind at the beginning of the block.
869     __ bind(loop->label.get());
870 
871     // Save the current cache state for the merge when jumping to this loop.
872     loop->label_state.Split(*__ cache_state());
873 
874     // Execute a stack check in the loop header.
875     StackCheck(decoder->position());
876   }
877 
Try(FullDecoder * decoder,Control * block)878   void Try(FullDecoder* decoder, Control* block) {
879     unsupported(decoder, kExceptionHandling, "try");
880   }
881 
Catch(FullDecoder * decoder,Control * block,Value * exception)882   void Catch(FullDecoder* decoder, Control* block, Value* exception) {
883     unsupported(decoder, kExceptionHandling, "catch");
884   }
885 
If(FullDecoder * decoder,const Value & cond,Control * if_block)886   void If(FullDecoder* decoder, const Value& cond, Control* if_block) {
887     DCHECK_EQ(if_block, decoder->control_at(0));
888     DCHECK(if_block->is_if());
889 
890     // Allocate the else state.
891     if_block->else_state = std::make_unique<ElseState>();
892 
893     // Test the condition, jump to else if zero.
894     Register value = __ PopToRegister().gp();
895     __ emit_cond_jump(kEqual, if_block->else_state->label.get(), kWasmI32,
896                       value);
897 
898     // Store the state (after popping the value) for executing the else branch.
899     if_block->else_state->state.Split(*__ cache_state());
900   }
901 
FallThruTo(FullDecoder * decoder,Control * c)902   void FallThruTo(FullDecoder* decoder, Control* c) {
903     if (c->end_merge.reached) {
904       __ MergeFullStackWith(c->label_state, *__ cache_state());
905     } else {
906       c->label_state.Split(*__ cache_state());
907     }
908     TraceCacheState(decoder);
909   }
910 
FinishOneArmedIf(FullDecoder * decoder,Control * c)911   void FinishOneArmedIf(FullDecoder* decoder, Control* c) {
912     DCHECK(c->is_onearmed_if());
913     if (c->end_merge.reached) {
914       // Someone already merged to the end of the if. Merge both arms into that.
915       if (c->reachable()) {
916         // Merge the if state into the end state.
917         __ MergeFullStackWith(c->label_state, *__ cache_state());
918         __ emit_jump(c->label.get());
919       }
920       // Merge the else state into the end state.
921       __ bind(c->else_state->label.get());
922       __ MergeFullStackWith(c->label_state, c->else_state->state);
923       __ cache_state()->Steal(c->label_state);
924     } else if (c->reachable()) {
925       // No merge yet at the end of the if, but we need to create a merge for
926       // the both arms of this if. Thus init the merge point from the else
927       // state, then merge the if state into that.
928       DCHECK_EQ(c->start_merge.arity, c->end_merge.arity);
929       c->label_state.InitMerge(c->else_state->state, __ num_locals(),
930                                c->start_merge.arity, c->stack_depth);
931       __ MergeFullStackWith(c->label_state, *__ cache_state());
932       __ emit_jump(c->label.get());
933       // Merge the else state into the end state.
934       __ bind(c->else_state->label.get());
935       __ MergeFullStackWith(c->label_state, c->else_state->state);
936       __ cache_state()->Steal(c->label_state);
937     } else {
938       // No merge needed, just continue with the else state.
939       __ bind(c->else_state->label.get());
940       __ cache_state()->Steal(c->else_state->state);
941     }
942   }
943 
PopControl(FullDecoder * decoder,Control * c)944   void PopControl(FullDecoder* decoder, Control* c) {
945     if (c->is_loop()) return;  // A loop just falls through.
946     if (c->is_onearmed_if()) {
947       // Special handling for one-armed ifs.
948       FinishOneArmedIf(decoder, c);
949     } else if (c->end_merge.reached) {
950       // There is a merge already. Merge our state into that, then continue with
951       // that state.
952       if (c->reachable()) {
953         __ MergeFullStackWith(c->label_state, *__ cache_state());
954       }
955       __ cache_state()->Steal(c->label_state);
956     } else {
957       // No merge, just continue with our current state.
958     }
959 
960     if (!c->label.get()->is_bound()) __ bind(c->label.get());
961   }
962 
EndControl(FullDecoder * decoder,Control * c)963   void EndControl(FullDecoder* decoder, Control* c) {}
964 
GenerateCCall(const LiftoffRegister * result_regs,const FunctionSig * sig,ValueType out_argument_type,const LiftoffRegister * arg_regs,ExternalReference ext_ref)965   void GenerateCCall(const LiftoffRegister* result_regs, const FunctionSig* sig,
966                      ValueType out_argument_type,
967                      const LiftoffRegister* arg_regs,
968                      ExternalReference ext_ref) {
969     // Before making a call, spill all cache registers.
970     __ SpillAllRegisters();
971 
972     // Store arguments on our stack, then align the stack for calling to C.
973     int param_bytes = 0;
974     for (ValueType param_type : sig->parameters()) {
975       param_bytes += param_type.element_size_bytes();
976     }
977     int out_arg_bytes = out_argument_type == kWasmStmt
978                             ? 0
979                             : out_argument_type.element_size_bytes();
980     int stack_bytes = std::max(param_bytes, out_arg_bytes);
981     __ CallC(sig, arg_regs, result_regs, out_argument_type, stack_bytes,
982              ext_ref);
983   }
984 
985   template <typename EmitFn, typename... Args>
986   typename std::enable_if<!std::is_member_function_pointer<EmitFn>::value>::type
CallEmitFn(EmitFn fn,Args...args)987   CallEmitFn(EmitFn fn, Args... args) {
988     fn(args...);
989   }
990 
991   template <typename EmitFn, typename... Args>
992   typename std::enable_if<std::is_member_function_pointer<EmitFn>::value>::type
CallEmitFn(EmitFn fn,Args...args)993   CallEmitFn(EmitFn fn, Args... args) {
994     (asm_.*fn)(ConvertAssemblerArg(args)...);
995   }
996 
997   // Wrap a {LiftoffRegister} with implicit conversions to {Register} and
998   // {DoubleRegister}.
999   struct AssemblerRegisterConverter {
1000     LiftoffRegister reg;
operator LiftoffRegisterv8::internal::wasm::__anon69d3db0f0111::LiftoffCompiler::AssemblerRegisterConverter1001     operator LiftoffRegister() { return reg; }
operator Registerv8::internal::wasm::__anon69d3db0f0111::LiftoffCompiler::AssemblerRegisterConverter1002     operator Register() { return reg.gp(); }
operator DoubleRegisterv8::internal::wasm::__anon69d3db0f0111::LiftoffCompiler::AssemblerRegisterConverter1003     operator DoubleRegister() { return reg.fp(); }
1004   };
1005 
1006   // Convert {LiftoffRegister} to {AssemblerRegisterConverter}, other types stay
1007   // unchanged.
1008   template <typename T>
1009   typename std::conditional<std::is_same<LiftoffRegister, T>::value,
1010                             AssemblerRegisterConverter, T>::type
ConvertAssemblerArg(T t)1011   ConvertAssemblerArg(T t) {
1012     return {t};
1013   }
1014 
1015   template <typename EmitFn, typename ArgType>
1016   struct EmitFnWithFirstArg {
1017     EmitFn fn;
1018     ArgType first_arg;
1019   };
1020 
1021   template <typename EmitFn, typename ArgType>
BindFirst(EmitFn fn,ArgType arg)1022   EmitFnWithFirstArg<EmitFn, ArgType> BindFirst(EmitFn fn, ArgType arg) {
1023     return {fn, arg};
1024   }
1025 
1026   template <typename EmitFn, typename T, typename... Args>
CallEmitFn(EmitFnWithFirstArg<EmitFn,T> bound_fn,Args...args)1027   void CallEmitFn(EmitFnWithFirstArg<EmitFn, T> bound_fn, Args... args) {
1028     CallEmitFn(bound_fn.fn, bound_fn.first_arg, ConvertAssemblerArg(args)...);
1029   }
1030 
1031   template <ValueType::Kind src_type, ValueType::Kind result_type, class EmitFn>
EmitUnOp(EmitFn fn)1032   void EmitUnOp(EmitFn fn) {
1033     constexpr RegClass src_rc = reg_class_for(src_type);
1034     constexpr RegClass result_rc = reg_class_for(result_type);
1035     LiftoffRegister src = __ PopToRegister();
1036     LiftoffRegister dst = src_rc == result_rc
1037                               ? __ GetUnusedRegister(result_rc, {src}, {})
1038                               : __ GetUnusedRegister(result_rc, {});
1039     CallEmitFn(fn, dst, src);
1040     __ PushRegister(ValueType::Primitive(result_type), dst);
1041   }
1042 
1043   template <ValueType::Kind type>
EmitFloatUnOpWithCFallback(bool (LiftoffAssembler::* emit_fn)(DoubleRegister,DoubleRegister),ExternalReference (* fallback_fn)())1044   void EmitFloatUnOpWithCFallback(
1045       bool (LiftoffAssembler::*emit_fn)(DoubleRegister, DoubleRegister),
1046       ExternalReference (*fallback_fn)()) {
1047     auto emit_with_c_fallback = [=](LiftoffRegister dst, LiftoffRegister src) {
1048       if ((asm_.*emit_fn)(dst.fp(), src.fp())) return;
1049       ExternalReference ext_ref = fallback_fn();
1050       ValueType sig_reps[] = {ValueType::Primitive(type)};
1051       FunctionSig sig(0, 1, sig_reps);
1052       GenerateCCall(&dst, &sig, ValueType::Primitive(type), &src, ext_ref);
1053     };
1054     EmitUnOp<type, type>(emit_with_c_fallback);
1055   }
1056 
1057   enum TypeConversionTrapping : bool { kCanTrap = true, kNoTrap = false };
1058   template <ValueType::Kind dst_type, ValueType::Kind src_type,
1059             TypeConversionTrapping can_trap>
EmitTypeConversion(WasmOpcode opcode,ExternalReference (* fallback_fn)(),WasmCodePosition trap_position)1060   void EmitTypeConversion(WasmOpcode opcode, ExternalReference (*fallback_fn)(),
1061                           WasmCodePosition trap_position) {
1062     static constexpr RegClass src_rc = reg_class_for(src_type);
1063     static constexpr RegClass dst_rc = reg_class_for(dst_type);
1064     LiftoffRegister src = __ PopToRegister();
1065     LiftoffRegister dst = src_rc == dst_rc
1066                               ? __ GetUnusedRegister(dst_rc, {src}, {})
1067                               : __ GetUnusedRegister(dst_rc, {});
1068     DCHECK_EQ(!!can_trap, trap_position > 0);
1069     Label* trap = can_trap ? AddOutOfLineTrap(
1070                                  trap_position,
1071                                  WasmCode::kThrowWasmTrapFloatUnrepresentable)
1072                            : nullptr;
1073     if (!__ emit_type_conversion(opcode, dst, src, trap)) {
1074       DCHECK_NOT_NULL(fallback_fn);
1075       ExternalReference ext_ref = fallback_fn();
1076       if (can_trap) {
1077         // External references for potentially trapping conversions return int.
1078         ValueType sig_reps[] = {kWasmI32, ValueType::Primitive(src_type)};
1079         FunctionSig sig(1, 1, sig_reps);
1080         LiftoffRegister ret_reg =
1081             __ GetUnusedRegister(kGpReg, LiftoffRegList::ForRegs(dst));
1082         LiftoffRegister dst_regs[] = {ret_reg, dst};
1083         GenerateCCall(dst_regs, &sig, ValueType::Primitive(dst_type), &src,
1084                       ext_ref);
1085         __ emit_cond_jump(kEqual, trap, kWasmI32, ret_reg.gp());
1086       } else {
1087         ValueType sig_reps[] = {ValueType::Primitive(src_type)};
1088         FunctionSig sig(0, 1, sig_reps);
1089         GenerateCCall(&dst, &sig, ValueType::Primitive(dst_type), &src,
1090                       ext_ref);
1091       }
1092     }
1093     __ PushRegister(ValueType::Primitive(dst_type), dst);
1094   }
1095 
UnOp(FullDecoder * decoder,WasmOpcode opcode,const Value & value,Value * result)1096   void UnOp(FullDecoder* decoder, WasmOpcode opcode, const Value& value,
1097             Value* result) {
1098 #define CASE_I32_UNOP(opcode, fn) \
1099   case kExpr##opcode:             \
1100     return EmitUnOp<kI32, kI32>(&LiftoffAssembler::emit_##fn);
1101 #define CASE_I64_UNOP(opcode, fn) \
1102   case kExpr##opcode:             \
1103     return EmitUnOp<kI64, kI64>(&LiftoffAssembler::emit_##fn);
1104 #define CASE_FLOAT_UNOP(opcode, type, fn) \
1105   case kExpr##opcode:                     \
1106     return EmitUnOp<k##type, k##type>(&LiftoffAssembler::emit_##fn);
1107 #define CASE_FLOAT_UNOP_WITH_CFALLBACK(opcode, type, fn)                     \
1108   case kExpr##opcode:                                                        \
1109     return EmitFloatUnOpWithCFallback<k##type>(&LiftoffAssembler::emit_##fn, \
1110                                                &ExternalReference::wasm_##fn);
1111 #define CASE_TYPE_CONVERSION(opcode, dst_type, src_type, ext_ref, can_trap) \
1112   case kExpr##opcode:                                                       \
1113     return EmitTypeConversion<k##dst_type, k##src_type, can_trap>(          \
1114         kExpr##opcode, ext_ref, can_trap ? decoder->position() : 0);
1115     switch (opcode) {
1116       CASE_I32_UNOP(I32Clz, i32_clz)
1117       CASE_I32_UNOP(I32Ctz, i32_ctz)
1118       CASE_FLOAT_UNOP(F32Abs, F32, f32_abs)
1119       CASE_FLOAT_UNOP(F32Neg, F32, f32_neg)
1120       CASE_FLOAT_UNOP_WITH_CFALLBACK(F32Ceil, F32, f32_ceil)
1121       CASE_FLOAT_UNOP_WITH_CFALLBACK(F32Floor, F32, f32_floor)
1122       CASE_FLOAT_UNOP_WITH_CFALLBACK(F32Trunc, F32, f32_trunc)
1123       CASE_FLOAT_UNOP_WITH_CFALLBACK(F32NearestInt, F32, f32_nearest_int)
1124       CASE_FLOAT_UNOP(F32Sqrt, F32, f32_sqrt)
1125       CASE_FLOAT_UNOP(F64Abs, F64, f64_abs)
1126       CASE_FLOAT_UNOP(F64Neg, F64, f64_neg)
1127       CASE_FLOAT_UNOP_WITH_CFALLBACK(F64Ceil, F64, f64_ceil)
1128       CASE_FLOAT_UNOP_WITH_CFALLBACK(F64Floor, F64, f64_floor)
1129       CASE_FLOAT_UNOP_WITH_CFALLBACK(F64Trunc, F64, f64_trunc)
1130       CASE_FLOAT_UNOP_WITH_CFALLBACK(F64NearestInt, F64, f64_nearest_int)
1131       CASE_FLOAT_UNOP(F64Sqrt, F64, f64_sqrt)
1132       CASE_TYPE_CONVERSION(I32ConvertI64, I32, I64, nullptr, kNoTrap)
1133       CASE_TYPE_CONVERSION(I32SConvertF32, I32, F32, nullptr, kCanTrap)
1134       CASE_TYPE_CONVERSION(I32UConvertF32, I32, F32, nullptr, kCanTrap)
1135       CASE_TYPE_CONVERSION(I32SConvertF64, I32, F64, nullptr, kCanTrap)
1136       CASE_TYPE_CONVERSION(I32UConvertF64, I32, F64, nullptr, kCanTrap)
1137       CASE_TYPE_CONVERSION(I32ReinterpretF32, I32, F32, nullptr, kNoTrap)
1138       CASE_TYPE_CONVERSION(I64SConvertI32, I64, I32, nullptr, kNoTrap)
1139       CASE_TYPE_CONVERSION(I64UConvertI32, I64, I32, nullptr, kNoTrap)
1140       CASE_TYPE_CONVERSION(I64SConvertF32, I64, F32,
1141                            &ExternalReference::wasm_float32_to_int64, kCanTrap)
1142       CASE_TYPE_CONVERSION(I64UConvertF32, I64, F32,
1143                            &ExternalReference::wasm_float32_to_uint64, kCanTrap)
1144       CASE_TYPE_CONVERSION(I64SConvertF64, I64, F64,
1145                            &ExternalReference::wasm_float64_to_int64, kCanTrap)
1146       CASE_TYPE_CONVERSION(I64UConvertF64, I64, F64,
1147                            &ExternalReference::wasm_float64_to_uint64, kCanTrap)
1148       CASE_TYPE_CONVERSION(I64ReinterpretF64, I64, F64, nullptr, kNoTrap)
1149       CASE_TYPE_CONVERSION(F32SConvertI32, F32, I32, nullptr, kNoTrap)
1150       CASE_TYPE_CONVERSION(F32UConvertI32, F32, I32, nullptr, kNoTrap)
1151       CASE_TYPE_CONVERSION(F32SConvertI64, F32, I64,
1152                            &ExternalReference::wasm_int64_to_float32, kNoTrap)
1153       CASE_TYPE_CONVERSION(F32UConvertI64, F32, I64,
1154                            &ExternalReference::wasm_uint64_to_float32, kNoTrap)
1155       CASE_TYPE_CONVERSION(F32ConvertF64, F32, F64, nullptr, kNoTrap)
1156       CASE_TYPE_CONVERSION(F32ReinterpretI32, F32, I32, nullptr, kNoTrap)
1157       CASE_TYPE_CONVERSION(F64SConvertI32, F64, I32, nullptr, kNoTrap)
1158       CASE_TYPE_CONVERSION(F64UConvertI32, F64, I32, nullptr, kNoTrap)
1159       CASE_TYPE_CONVERSION(F64SConvertI64, F64, I64,
1160                            &ExternalReference::wasm_int64_to_float64, kNoTrap)
1161       CASE_TYPE_CONVERSION(F64UConvertI64, F64, I64,
1162                            &ExternalReference::wasm_uint64_to_float64, kNoTrap)
1163       CASE_TYPE_CONVERSION(F64ConvertF32, F64, F32, nullptr, kNoTrap)
1164       CASE_TYPE_CONVERSION(F64ReinterpretI64, F64, I64, nullptr, kNoTrap)
1165       CASE_I32_UNOP(I32SExtendI8, i32_signextend_i8)
1166       CASE_I32_UNOP(I32SExtendI16, i32_signextend_i16)
1167       CASE_I64_UNOP(I64SExtendI8, i64_signextend_i8)
1168       CASE_I64_UNOP(I64SExtendI16, i64_signextend_i16)
1169       CASE_I64_UNOP(I64SExtendI32, i64_signextend_i32)
1170       CASE_I64_UNOP(I64Clz, i64_clz)
1171       CASE_I64_UNOP(I64Ctz, i64_ctz)
1172       CASE_TYPE_CONVERSION(I32SConvertSatF32, I32, F32, nullptr, kNoTrap)
1173       CASE_TYPE_CONVERSION(I32UConvertSatF32, I32, F32, nullptr, kNoTrap)
1174       CASE_TYPE_CONVERSION(I32SConvertSatF64, I32, F64, nullptr, kNoTrap)
1175       CASE_TYPE_CONVERSION(I32UConvertSatF64, I32, F64, nullptr, kNoTrap)
1176       CASE_TYPE_CONVERSION(I64SConvertSatF32, I64, F32,
1177                            &ExternalReference::wasm_float32_to_int64_sat,
1178                            kNoTrap)
1179       CASE_TYPE_CONVERSION(I64UConvertSatF32, I64, F32,
1180                            &ExternalReference::wasm_float32_to_uint64_sat,
1181                            kNoTrap)
1182       CASE_TYPE_CONVERSION(I64SConvertSatF64, I64, F64,
1183                            &ExternalReference::wasm_float64_to_int64_sat,
1184                            kNoTrap)
1185       CASE_TYPE_CONVERSION(I64UConvertSatF64, I64, F64,
1186                            &ExternalReference::wasm_float64_to_uint64_sat,
1187                            kNoTrap)
1188       case kExprI32Eqz:
1189         DCHECK(decoder->lookahead(0, kExprI32Eqz));
1190         if (decoder->lookahead(1, kExprBrIf) && !for_debugging_) {
1191           DCHECK(!has_outstanding_op());
1192           outstanding_op_ = kExprI32Eqz;
1193           break;
1194         }
1195         return EmitUnOp<kI32, kI32>(&LiftoffAssembler::emit_i32_eqz);
1196       case kExprI64Eqz:
1197         return EmitUnOp<kI64, kI32>(&LiftoffAssembler::emit_i64_eqz);
1198       case kExprI32Popcnt:
1199         return EmitUnOp<kI32, kI32>(
1200             [=](LiftoffRegister dst, LiftoffRegister src) {
1201               if (__ emit_i32_popcnt(dst.gp(), src.gp())) return;
1202               ValueType sig_i_i_reps[] = {kWasmI32, kWasmI32};
1203               FunctionSig sig_i_i(1, 1, sig_i_i_reps);
1204               GenerateCCall(&dst, &sig_i_i, kWasmStmt, &src,
1205                             ExternalReference::wasm_word32_popcnt());
1206             });
1207       case kExprI64Popcnt:
1208         return EmitUnOp<kI64, kI64>(
1209             [=](LiftoffRegister dst, LiftoffRegister src) {
1210               if (__ emit_i64_popcnt(dst, src)) return;
1211               // The c function returns i32. We will zero-extend later.
1212               ValueType sig_i_l_reps[] = {kWasmI32, kWasmI64};
1213               FunctionSig sig_i_l(1, 1, sig_i_l_reps);
1214               LiftoffRegister c_call_dst = kNeedI64RegPair ? dst.low() : dst;
1215               GenerateCCall(&c_call_dst, &sig_i_l, kWasmStmt, &src,
1216                             ExternalReference::wasm_word64_popcnt());
1217               // Now zero-extend the result to i64.
1218               __ emit_type_conversion(kExprI64UConvertI32, dst, c_call_dst,
1219                                       nullptr);
1220             });
1221       case kExprRefIsNull:
1222         unsupported(decoder, kRefTypes, "ref_is_null");
1223         return;
1224       default:
1225         UNREACHABLE();
1226     }
1227 #undef CASE_I32_UNOP
1228 #undef CASE_I64_UNOP
1229 #undef CASE_FLOAT_UNOP
1230 #undef CASE_FLOAT_UNOP_WITH_CFALLBACK
1231 #undef CASE_TYPE_CONVERSION
1232   }
1233 
1234   template <ValueType::Kind src_type, ValueType::Kind result_type,
1235             typename EmitFn, typename EmitFnImm>
EmitBinOpImm(EmitFn fn,EmitFnImm fnImm)1236   void EmitBinOpImm(EmitFn fn, EmitFnImm fnImm) {
1237     static constexpr RegClass src_rc = reg_class_for(src_type);
1238     static constexpr RegClass result_rc = reg_class_for(result_type);
1239 
1240     LiftoffAssembler::VarState rhs_slot = __ cache_state()->stack_state.back();
1241     // Check if the RHS is an immediate.
1242     if (rhs_slot.is_const()) {
1243       __ cache_state()->stack_state.pop_back();
1244       int32_t imm = rhs_slot.i32_const();
1245 
1246       LiftoffRegister lhs = __ PopToRegister();
1247       // Either reuse {lhs} for {dst}, or choose a register (pair) which does
1248       // not overlap, for easier code generation.
1249       LiftoffRegList pinned = LiftoffRegList::ForRegs(lhs);
1250       LiftoffRegister dst = src_rc == result_rc
1251                                 ? __ GetUnusedRegister(result_rc, {lhs}, pinned)
1252                                 : __ GetUnusedRegister(result_rc, pinned);
1253 
1254       CallEmitFn(fnImm, dst, lhs, imm);
1255       __ PushRegister(ValueType::Primitive(result_type), dst);
1256     } else {
1257       // The RHS was not an immediate.
1258       EmitBinOp<src_type, result_type>(fn);
1259     }
1260   }
1261 
1262   template <ValueType::Kind src_type, ValueType::Kind result_type,
1263             bool swap_lhs_rhs = false, typename EmitFn>
EmitBinOp(EmitFn fn)1264   void EmitBinOp(EmitFn fn) {
1265     static constexpr RegClass src_rc = reg_class_for(src_type);
1266     static constexpr RegClass result_rc = reg_class_for(result_type);
1267     LiftoffRegister rhs = __ PopToRegister();
1268     LiftoffRegister lhs = __ PopToRegister(LiftoffRegList::ForRegs(rhs));
1269     LiftoffRegister dst = src_rc == result_rc
1270                               ? __ GetUnusedRegister(result_rc, {lhs, rhs}, {})
1271                               : __ GetUnusedRegister(result_rc, {});
1272 
1273     if (swap_lhs_rhs) std::swap(lhs, rhs);
1274 
1275     CallEmitFn(fn, dst, lhs, rhs);
1276     __ PushRegister(ValueType::Primitive(result_type), dst);
1277   }
1278 
EmitDivOrRem64CCall(LiftoffRegister dst,LiftoffRegister lhs,LiftoffRegister rhs,ExternalReference ext_ref,Label * trap_by_zero,Label * trap_unrepresentable=nullptr)1279   void EmitDivOrRem64CCall(LiftoffRegister dst, LiftoffRegister lhs,
1280                            LiftoffRegister rhs, ExternalReference ext_ref,
1281                            Label* trap_by_zero,
1282                            Label* trap_unrepresentable = nullptr) {
1283     // Cannot emit native instructions, build C call.
1284     LiftoffRegister ret =
1285         __ GetUnusedRegister(kGpReg, LiftoffRegList::ForRegs(dst));
1286     LiftoffRegister tmp =
1287         __ GetUnusedRegister(kGpReg, LiftoffRegList::ForRegs(dst, ret));
1288     LiftoffRegister arg_regs[] = {lhs, rhs};
1289     LiftoffRegister result_regs[] = {ret, dst};
1290     ValueType sig_types[] = {kWasmI32, kWasmI64, kWasmI64};
1291     // <i64, i64> -> i32 (with i64 output argument)
1292     FunctionSig sig(1, 2, sig_types);
1293     GenerateCCall(result_regs, &sig, kWasmI64, arg_regs, ext_ref);
1294     __ LoadConstant(tmp, WasmValue(int32_t{0}));
1295     __ emit_cond_jump(kEqual, trap_by_zero, kWasmI32, ret.gp(), tmp.gp());
1296     if (trap_unrepresentable) {
1297       __ LoadConstant(tmp, WasmValue(int32_t{-1}));
1298       __ emit_cond_jump(kEqual, trap_unrepresentable, kWasmI32, ret.gp(),
1299                         tmp.gp());
1300     }
1301   }
1302 
1303   template <WasmOpcode opcode>
EmitI32CmpOp(FullDecoder * decoder)1304   void EmitI32CmpOp(FullDecoder* decoder) {
1305     DCHECK(decoder->lookahead(0, opcode));
1306     if (decoder->lookahead(1, kExprBrIf) && !for_debugging_) {
1307       DCHECK(!has_outstanding_op());
1308       outstanding_op_ = opcode;
1309       return;
1310     }
1311     return EmitBinOp<kI32, kI32>(BindFirst(&LiftoffAssembler::emit_i32_set_cond,
1312                                            GetCompareCondition(opcode)));
1313   }
1314 
BinOp(FullDecoder * decoder,WasmOpcode opcode,const Value & lhs,const Value & rhs,Value * result)1315   void BinOp(FullDecoder* decoder, WasmOpcode opcode, const Value& lhs,
1316              const Value& rhs, Value* result) {
1317 #define CASE_I64_SHIFTOP(opcode, fn)                                         \
1318   case kExpr##opcode:                                                        \
1319     return EmitBinOpImm<kI64, kI64>(                                         \
1320         [=](LiftoffRegister dst, LiftoffRegister src,                        \
1321             LiftoffRegister amount) {                                        \
1322           __ emit_##fn(dst, src,                                             \
1323                        amount.is_gp_pair() ? amount.low_gp() : amount.gp()); \
1324         },                                                                   \
1325         &LiftoffAssembler::emit_##fn##i);
1326 #define CASE_CCALL_BINOP(opcode, type, ext_ref_fn)                           \
1327   case kExpr##opcode:                                                        \
1328     return EmitBinOp<k##type, k##type>(                                      \
1329         [=](LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs) { \
1330           LiftoffRegister args[] = {lhs, rhs};                               \
1331           auto ext_ref = ExternalReference::ext_ref_fn();                    \
1332           ValueType sig_reps[] = {kWasm##type, kWasm##type, kWasm##type};    \
1333           const bool out_via_stack = kWasm##type == kWasmI64;                \
1334           FunctionSig sig(out_via_stack ? 0 : 1, 2, sig_reps);               \
1335           ValueType out_arg_type = out_via_stack ? kWasmI64 : kWasmStmt;     \
1336           GenerateCCall(&dst, &sig, out_arg_type, args, ext_ref);            \
1337         });
1338     switch (opcode) {
1339       case kExprI32Add:
1340         return EmitBinOpImm<kI32, kI32>(&LiftoffAssembler::emit_i32_add,
1341                                         &LiftoffAssembler::emit_i32_addi);
1342       case kExprI32Sub:
1343         return EmitBinOp<kI32, kI32>(&LiftoffAssembler::emit_i32_sub);
1344       case kExprI32Mul:
1345         return EmitBinOp<kI32, kI32>(&LiftoffAssembler::emit_i32_mul);
1346       case kExprI32And:
1347         return EmitBinOpImm<kI32, kI32>(&LiftoffAssembler::emit_i32_and,
1348                                         &LiftoffAssembler::emit_i32_andi);
1349       case kExprI32Ior:
1350         return EmitBinOpImm<kI32, kI32>(&LiftoffAssembler::emit_i32_or,
1351                                         &LiftoffAssembler::emit_i32_ori);
1352       case kExprI32Xor:
1353         return EmitBinOpImm<kI32, kI32>(&LiftoffAssembler::emit_i32_xor,
1354                                         &LiftoffAssembler::emit_i32_xori);
1355       case kExprI32Eq:
1356         return EmitI32CmpOp<kExprI32Eq>(decoder);
1357       case kExprI32Ne:
1358         return EmitI32CmpOp<kExprI32Ne>(decoder);
1359       case kExprI32LtS:
1360         return EmitI32CmpOp<kExprI32LtS>(decoder);
1361       case kExprI32LtU:
1362         return EmitI32CmpOp<kExprI32LtU>(decoder);
1363       case kExprI32GtS:
1364         return EmitI32CmpOp<kExprI32GtS>(decoder);
1365       case kExprI32GtU:
1366         return EmitI32CmpOp<kExprI32GtU>(decoder);
1367       case kExprI32LeS:
1368         return EmitI32CmpOp<kExprI32LeS>(decoder);
1369       case kExprI32LeU:
1370         return EmitI32CmpOp<kExprI32LeU>(decoder);
1371       case kExprI32GeS:
1372         return EmitI32CmpOp<kExprI32GeS>(decoder);
1373       case kExprI32GeU:
1374         return EmitI32CmpOp<kExprI32GeU>(decoder);
1375       case kExprI64Add:
1376         return EmitBinOpImm<kI64, kI64>(&LiftoffAssembler::emit_i64_add,
1377                                         &LiftoffAssembler::emit_i64_addi);
1378       case kExprI64Sub:
1379         return EmitBinOp<kI64, kI64>(&LiftoffAssembler::emit_i64_sub);
1380       case kExprI64Mul:
1381         return EmitBinOp<kI64, kI64>(&LiftoffAssembler::emit_i64_mul);
1382       case kExprI64And:
1383         return EmitBinOpImm<kI64, kI64>(&LiftoffAssembler::emit_i64_and,
1384                                         &LiftoffAssembler::emit_i64_andi);
1385       case kExprI64Ior:
1386         return EmitBinOpImm<kI64, kI64>(&LiftoffAssembler::emit_i64_or,
1387                                         &LiftoffAssembler::emit_i64_ori);
1388       case kExprI64Xor:
1389         return EmitBinOpImm<kI64, kI64>(&LiftoffAssembler::emit_i64_xor,
1390                                         &LiftoffAssembler::emit_i64_xori);
1391       case kExprI64Eq:
1392         return EmitBinOp<kI64, kI32>(
1393             BindFirst(&LiftoffAssembler::emit_i64_set_cond, kEqual));
1394       case kExprI64Ne:
1395         return EmitBinOp<kI64, kI32>(
1396             BindFirst(&LiftoffAssembler::emit_i64_set_cond, kUnequal));
1397       case kExprI64LtS:
1398         return EmitBinOp<kI64, kI32>(
1399             BindFirst(&LiftoffAssembler::emit_i64_set_cond, kSignedLessThan));
1400       case kExprI64LtU:
1401         return EmitBinOp<kI64, kI32>(
1402             BindFirst(&LiftoffAssembler::emit_i64_set_cond, kUnsignedLessThan));
1403       case kExprI64GtS:
1404         return EmitBinOp<kI64, kI32>(BindFirst(
1405             &LiftoffAssembler::emit_i64_set_cond, kSignedGreaterThan));
1406       case kExprI64GtU:
1407         return EmitBinOp<kI64, kI32>(BindFirst(
1408             &LiftoffAssembler::emit_i64_set_cond, kUnsignedGreaterThan));
1409       case kExprI64LeS:
1410         return EmitBinOp<kI64, kI32>(
1411             BindFirst(&LiftoffAssembler::emit_i64_set_cond, kSignedLessEqual));
1412       case kExprI64LeU:
1413         return EmitBinOp<kI64, kI32>(BindFirst(
1414             &LiftoffAssembler::emit_i64_set_cond, kUnsignedLessEqual));
1415       case kExprI64GeS:
1416         return EmitBinOp<kI64, kI32>(BindFirst(
1417             &LiftoffAssembler::emit_i64_set_cond, kSignedGreaterEqual));
1418       case kExprI64GeU:
1419         return EmitBinOp<kI64, kI32>(BindFirst(
1420             &LiftoffAssembler::emit_i64_set_cond, kUnsignedGreaterEqual));
1421       case kExprF32Eq:
1422         return EmitBinOp<kF32, kI32>(
1423             BindFirst(&LiftoffAssembler::emit_f32_set_cond, kEqual));
1424       case kExprF32Ne:
1425         return EmitBinOp<kF32, kI32>(
1426             BindFirst(&LiftoffAssembler::emit_f32_set_cond, kUnequal));
1427       case kExprF32Lt:
1428         return EmitBinOp<kF32, kI32>(
1429             BindFirst(&LiftoffAssembler::emit_f32_set_cond, kUnsignedLessThan));
1430       case kExprF32Gt:
1431         return EmitBinOp<kF32, kI32>(BindFirst(
1432             &LiftoffAssembler::emit_f32_set_cond, kUnsignedGreaterThan));
1433       case kExprF32Le:
1434         return EmitBinOp<kF32, kI32>(BindFirst(
1435             &LiftoffAssembler::emit_f32_set_cond, kUnsignedLessEqual));
1436       case kExprF32Ge:
1437         return EmitBinOp<kF32, kI32>(BindFirst(
1438             &LiftoffAssembler::emit_f32_set_cond, kUnsignedGreaterEqual));
1439       case kExprF64Eq:
1440         return EmitBinOp<kF64, kI32>(
1441             BindFirst(&LiftoffAssembler::emit_f64_set_cond, kEqual));
1442       case kExprF64Ne:
1443         return EmitBinOp<kF64, kI32>(
1444             BindFirst(&LiftoffAssembler::emit_f64_set_cond, kUnequal));
1445       case kExprF64Lt:
1446         return EmitBinOp<kF64, kI32>(
1447             BindFirst(&LiftoffAssembler::emit_f64_set_cond, kUnsignedLessThan));
1448       case kExprF64Gt:
1449         return EmitBinOp<kF64, kI32>(BindFirst(
1450             &LiftoffAssembler::emit_f64_set_cond, kUnsignedGreaterThan));
1451       case kExprF64Le:
1452         return EmitBinOp<kF64, kI32>(BindFirst(
1453             &LiftoffAssembler::emit_f64_set_cond, kUnsignedLessEqual));
1454       case kExprF64Ge:
1455         return EmitBinOp<kF64, kI32>(BindFirst(
1456             &LiftoffAssembler::emit_f64_set_cond, kUnsignedGreaterEqual));
1457       case kExprI32Shl:
1458         return EmitBinOpImm<kI32, kI32>(&LiftoffAssembler::emit_i32_shl,
1459                                         &LiftoffAssembler::emit_i32_shli);
1460       case kExprI32ShrS:
1461         return EmitBinOpImm<kI32, kI32>(&LiftoffAssembler::emit_i32_sar,
1462                                         &LiftoffAssembler::emit_i32_sari);
1463       case kExprI32ShrU:
1464         return EmitBinOpImm<kI32, kI32>(&LiftoffAssembler::emit_i32_shr,
1465                                         &LiftoffAssembler::emit_i32_shri);
1466         CASE_CCALL_BINOP(I32Rol, I32, wasm_word32_rol)
1467         CASE_CCALL_BINOP(I32Ror, I32, wasm_word32_ror)
1468         CASE_I64_SHIFTOP(I64Shl, i64_shl)
1469         CASE_I64_SHIFTOP(I64ShrS, i64_sar)
1470         CASE_I64_SHIFTOP(I64ShrU, i64_shr)
1471         CASE_CCALL_BINOP(I64Rol, I64, wasm_word64_rol)
1472         CASE_CCALL_BINOP(I64Ror, I64, wasm_word64_ror)
1473       case kExprF32Add:
1474         return EmitBinOp<kF32, kF32>(&LiftoffAssembler::emit_f32_add);
1475       case kExprF32Sub:
1476         return EmitBinOp<kF32, kF32>(&LiftoffAssembler::emit_f32_sub);
1477       case kExprF32Mul:
1478         return EmitBinOp<kF32, kF32>(&LiftoffAssembler::emit_f32_mul);
1479       case kExprF32Div:
1480         return EmitBinOp<kF32, kF32>(&LiftoffAssembler::emit_f32_div);
1481       case kExprF32Min:
1482         return EmitBinOp<kF32, kF32>(&LiftoffAssembler::emit_f32_min);
1483       case kExprF32Max:
1484         return EmitBinOp<kF32, kF32>(&LiftoffAssembler::emit_f32_max);
1485       case kExprF32CopySign:
1486         return EmitBinOp<kF32, kF32>(&LiftoffAssembler::emit_f32_copysign);
1487       case kExprF64Add:
1488         return EmitBinOp<kF64, kF64>(&LiftoffAssembler::emit_f64_add);
1489       case kExprF64Sub:
1490         return EmitBinOp<kF64, kF64>(&LiftoffAssembler::emit_f64_sub);
1491       case kExprF64Mul:
1492         return EmitBinOp<kF64, kF64>(&LiftoffAssembler::emit_f64_mul);
1493       case kExprF64Div:
1494         return EmitBinOp<kF64, kF64>(&LiftoffAssembler::emit_f64_div);
1495       case kExprF64Min:
1496         return EmitBinOp<kF64, kF64>(&LiftoffAssembler::emit_f64_min);
1497       case kExprF64Max:
1498         return EmitBinOp<kF64, kF64>(&LiftoffAssembler::emit_f64_max);
1499       case kExprF64CopySign:
1500         return EmitBinOp<kF64, kF64>(&LiftoffAssembler::emit_f64_copysign);
1501       case kExprI32DivS:
1502         return EmitBinOp<kI32, kI32>([this, decoder](LiftoffRegister dst,
1503                                                      LiftoffRegister lhs,
1504                                                      LiftoffRegister rhs) {
1505           WasmCodePosition position = decoder->position();
1506           AddOutOfLineTrap(position, WasmCode::kThrowWasmTrapDivByZero);
1507           // Adding the second trap might invalidate the pointer returned for
1508           // the first one, thus get both pointers afterwards.
1509           AddOutOfLineTrap(position,
1510                            WasmCode::kThrowWasmTrapDivUnrepresentable);
1511           Label* div_by_zero = out_of_line_code_.end()[-2].label.get();
1512           Label* div_unrepresentable = out_of_line_code_.end()[-1].label.get();
1513           __ emit_i32_divs(dst.gp(), lhs.gp(), rhs.gp(), div_by_zero,
1514                            div_unrepresentable);
1515         });
1516       case kExprI32DivU:
1517         return EmitBinOp<kI32, kI32>(
1518             [this, decoder](LiftoffRegister dst, LiftoffRegister lhs,
1519                             LiftoffRegister rhs) {
1520               Label* div_by_zero = AddOutOfLineTrap(
1521                   decoder->position(), WasmCode::kThrowWasmTrapDivByZero);
1522               __ emit_i32_divu(dst.gp(), lhs.gp(), rhs.gp(), div_by_zero);
1523             });
1524       case kExprI32RemS:
1525         return EmitBinOp<kI32, kI32>(
1526             [this, decoder](LiftoffRegister dst, LiftoffRegister lhs,
1527                             LiftoffRegister rhs) {
1528               Label* rem_by_zero = AddOutOfLineTrap(
1529                   decoder->position(), WasmCode::kThrowWasmTrapRemByZero);
1530               __ emit_i32_rems(dst.gp(), lhs.gp(), rhs.gp(), rem_by_zero);
1531             });
1532       case kExprI32RemU:
1533         return EmitBinOp<kI32, kI32>(
1534             [this, decoder](LiftoffRegister dst, LiftoffRegister lhs,
1535                             LiftoffRegister rhs) {
1536               Label* rem_by_zero = AddOutOfLineTrap(
1537                   decoder->position(), WasmCode::kThrowWasmTrapRemByZero);
1538               __ emit_i32_remu(dst.gp(), lhs.gp(), rhs.gp(), rem_by_zero);
1539             });
1540       case kExprI64DivS:
1541         return EmitBinOp<kI64, kI64>([this, decoder](LiftoffRegister dst,
1542                                                      LiftoffRegister lhs,
1543                                                      LiftoffRegister rhs) {
1544           WasmCodePosition position = decoder->position();
1545           AddOutOfLineTrap(position, WasmCode::kThrowWasmTrapDivByZero);
1546           // Adding the second trap might invalidate the pointer returned for
1547           // the first one, thus get both pointers afterwards.
1548           AddOutOfLineTrap(position,
1549                            WasmCode::kThrowWasmTrapDivUnrepresentable);
1550           Label* div_by_zero = out_of_line_code_.end()[-2].label.get();
1551           Label* div_unrepresentable = out_of_line_code_.end()[-1].label.get();
1552           if (!__ emit_i64_divs(dst, lhs, rhs, div_by_zero,
1553                                 div_unrepresentable)) {
1554             ExternalReference ext_ref = ExternalReference::wasm_int64_div();
1555             EmitDivOrRem64CCall(dst, lhs, rhs, ext_ref, div_by_zero,
1556                                 div_unrepresentable);
1557           }
1558         });
1559       case kExprI64DivU:
1560         return EmitBinOp<kI64, kI64>([this, decoder](LiftoffRegister dst,
1561                                                      LiftoffRegister lhs,
1562                                                      LiftoffRegister rhs) {
1563           Label* div_by_zero = AddOutOfLineTrap(
1564               decoder->position(), WasmCode::kThrowWasmTrapDivByZero);
1565           if (!__ emit_i64_divu(dst, lhs, rhs, div_by_zero)) {
1566             ExternalReference ext_ref = ExternalReference::wasm_uint64_div();
1567             EmitDivOrRem64CCall(dst, lhs, rhs, ext_ref, div_by_zero);
1568           }
1569         });
1570       case kExprI64RemS:
1571         return EmitBinOp<kI64, kI64>(
1572             [this, decoder](LiftoffRegister dst, LiftoffRegister lhs,
1573                             LiftoffRegister rhs) {
1574               Label* rem_by_zero = AddOutOfLineTrap(
1575                   decoder->position(), WasmCode::kThrowWasmTrapRemByZero);
1576               if (!__ emit_i64_rems(dst, lhs, rhs, rem_by_zero)) {
1577                 ExternalReference ext_ref = ExternalReference::wasm_int64_mod();
1578                 EmitDivOrRem64CCall(dst, lhs, rhs, ext_ref, rem_by_zero);
1579               }
1580             });
1581       case kExprI64RemU:
1582         return EmitBinOp<kI64, kI64>([this, decoder](LiftoffRegister dst,
1583                                                      LiftoffRegister lhs,
1584                                                      LiftoffRegister rhs) {
1585           Label* rem_by_zero = AddOutOfLineTrap(
1586               decoder->position(), WasmCode::kThrowWasmTrapRemByZero);
1587           if (!__ emit_i64_remu(dst, lhs, rhs, rem_by_zero)) {
1588             ExternalReference ext_ref = ExternalReference::wasm_uint64_mod();
1589             EmitDivOrRem64CCall(dst, lhs, rhs, ext_ref, rem_by_zero);
1590           }
1591         });
1592       default:
1593         UNREACHABLE();
1594     }
1595 #undef CASE_I64_SHIFTOP
1596 #undef CASE_CCALL_BINOP
1597   }
1598 
I32Const(FullDecoder * decoder,Value * result,int32_t value)1599   void I32Const(FullDecoder* decoder, Value* result, int32_t value) {
1600     __ PushConstant(kWasmI32, value);
1601   }
1602 
I64Const(FullDecoder * decoder,Value * result,int64_t value)1603   void I64Const(FullDecoder* decoder, Value* result, int64_t value) {
1604     // The {VarState} stores constant values as int32_t, thus we only store
1605     // 64-bit constants in this field if it fits in an int32_t. Larger values
1606     // cannot be used as immediate value anyway, so we can also just put them in
1607     // a register immediately.
1608     int32_t value_i32 = static_cast<int32_t>(value);
1609     if (value_i32 == value) {
1610       __ PushConstant(kWasmI64, value_i32);
1611     } else {
1612       LiftoffRegister reg = __ GetUnusedRegister(reg_class_for(kWasmI64), {});
1613       __ LoadConstant(reg, WasmValue(value));
1614       __ PushRegister(kWasmI64, reg);
1615     }
1616   }
1617 
F32Const(FullDecoder * decoder,Value * result,float value)1618   void F32Const(FullDecoder* decoder, Value* result, float value) {
1619     LiftoffRegister reg = __ GetUnusedRegister(kFpReg, {});
1620     __ LoadConstant(reg, WasmValue(value));
1621     __ PushRegister(kWasmF32, reg);
1622   }
1623 
F64Const(FullDecoder * decoder,Value * result,double value)1624   void F64Const(FullDecoder* decoder, Value* result, double value) {
1625     LiftoffRegister reg = __ GetUnusedRegister(kFpReg, {});
1626     __ LoadConstant(reg, WasmValue(value));
1627     __ PushRegister(kWasmF64, reg);
1628   }
1629 
RefNull(FullDecoder * decoder,ValueType type,Value *)1630   void RefNull(FullDecoder* decoder, ValueType type, Value*) {
1631     if (!FLAG_experimental_liftoff_extern_ref) {
1632       unsupported(decoder, kRefTypes, "ref_null");
1633       return;
1634     }
1635     Register isolate_root = __ GetUnusedRegister(kGpReg, {}).gp();
1636     // We can re-use the isolate_root register as result register.
1637     Register result = isolate_root;
1638 
1639     LOAD_INSTANCE_FIELD(isolate_root, IsolateRoot, kSystemPointerSize);
1640     __ LoadTaggedPointer(result, isolate_root, no_reg,
1641                          IsolateData::root_slot_offset(RootIndex::kNullValue),
1642                          {});
1643     __ PushRegister(type, LiftoffRegister(result));
1644   }
1645 
RefFunc(FullDecoder * decoder,uint32_t function_index,Value * result)1646   void RefFunc(FullDecoder* decoder, uint32_t function_index, Value* result) {
1647     unsupported(decoder, kRefTypes, "func");
1648   }
1649 
RefAsNonNull(FullDecoder * decoder,const Value & arg,Value * result)1650   void RefAsNonNull(FullDecoder* decoder, const Value& arg, Value* result) {
1651     unsupported(decoder, kRefTypes, "ref.as_non_null");
1652   }
1653 
Drop(FullDecoder * decoder,const Value & value)1654   void Drop(FullDecoder* decoder, const Value& value) {
1655     auto& slot = __ cache_state()->stack_state.back();
1656     // If the dropped slot contains a register, decrement it's use count.
1657     if (slot.is_reg()) __ cache_state()->dec_used(slot.reg());
1658     __ cache_state()->stack_state.pop_back();
1659   }
1660 
TraceFunctionExit(FullDecoder * decoder)1661   void TraceFunctionExit(FullDecoder* decoder) {
1662     DEBUG_CODE_COMMENT("trace function exit");
1663     // Before making the runtime call, spill all cache registers.
1664     __ SpillAllRegisters();
1665     LiftoffRegList pinned;
1666     // Get a register to hold the stack slot for the return value.
1667     LiftoffRegister info = pinned.set(__ GetUnusedRegister(kGpReg, pinned));
1668     __ AllocateStackSlot(info.gp(), sizeof(int64_t));
1669 
1670     // Store the return value if there is exactly one. Multiple return values
1671     // are not handled yet.
1672     size_t num_returns = decoder->sig_->return_count();
1673     if (num_returns == 1) {
1674       ValueType return_type = decoder->sig_->GetReturn(0);
1675       LiftoffRegister return_reg =
1676           __ LoadToRegister(__ cache_state()->stack_state.back(), pinned);
1677       __ Store(info.gp(), no_reg, 0, return_reg,
1678                StoreType::ForValueType(return_type), pinned);
1679     }
1680     // Put the parameter in its place.
1681     WasmTraceExitDescriptor descriptor;
1682     DCHECK_EQ(0, descriptor.GetStackParameterCount());
1683     DCHECK_EQ(1, descriptor.GetRegisterParameterCount());
1684     Register param_reg = descriptor.GetRegisterParameter(0);
1685     if (info.gp() != param_reg) {
1686       __ Move(param_reg, info.gp(), LiftoffAssembler::kWasmIntPtr);
1687     }
1688 
1689     source_position_table_builder_.AddPosition(
1690         __ pc_offset(), SourcePosition(decoder->position()), false);
1691     __ CallRuntimeStub(WasmCode::kWasmTraceExit);
1692     DefineSafepoint();
1693 
1694     __ DeallocateStackSlot(sizeof(int64_t));
1695   }
1696 
ReturnImpl(FullDecoder * decoder)1697   void ReturnImpl(FullDecoder* decoder) {
1698     if (FLAG_trace_wasm) TraceFunctionExit(decoder);
1699     size_t num_returns = decoder->sig_->return_count();
1700     if (num_returns > 0) __ MoveToReturnLocations(decoder->sig_, descriptor_);
1701     DEBUG_CODE_COMMENT("leave frame");
1702     __ LeaveFrame(StackFrame::WASM);
1703     __ DropStackSlotsAndRet(
1704         static_cast<uint32_t>(descriptor_->StackParameterCount()));
1705   }
1706 
DoReturn(FullDecoder * decoder,Vector<Value>)1707   void DoReturn(FullDecoder* decoder, Vector<Value> /*values*/) {
1708     ReturnImpl(decoder);
1709   }
1710 
LocalGet(FullDecoder * decoder,Value * result,const LocalIndexImmediate<validate> & imm)1711   void LocalGet(FullDecoder* decoder, Value* result,
1712                 const LocalIndexImmediate<validate>& imm) {
1713     auto local_slot = __ cache_state()->stack_state[imm.index];
1714     __ cache_state()->stack_state.emplace_back(
1715         local_slot.type(), __ NextSpillOffset(local_slot.type()));
1716     auto* slot = &__ cache_state()->stack_state.back();
1717     if (local_slot.is_reg()) {
1718       __ cache_state()->inc_used(local_slot.reg());
1719       slot->MakeRegister(local_slot.reg());
1720     } else if (local_slot.is_const()) {
1721       slot->MakeConstant(local_slot.i32_const());
1722     } else {
1723       DCHECK(local_slot.is_stack());
1724       auto rc = reg_class_for(local_slot.type());
1725       LiftoffRegister reg = __ GetUnusedRegister(rc, {});
1726       __ cache_state()->inc_used(reg);
1727       slot->MakeRegister(reg);
1728       __ Fill(reg, local_slot.offset(), local_slot.type());
1729     }
1730   }
1731 
LocalSetFromStackSlot(LiftoffAssembler::VarState * dst_slot,uint32_t local_index)1732   void LocalSetFromStackSlot(LiftoffAssembler::VarState* dst_slot,
1733                              uint32_t local_index) {
1734     auto& state = *__ cache_state();
1735     auto& src_slot = state.stack_state.back();
1736     ValueType type = dst_slot->type();
1737     if (dst_slot->is_reg()) {
1738       LiftoffRegister slot_reg = dst_slot->reg();
1739       if (state.get_use_count(slot_reg) == 1) {
1740         __ Fill(dst_slot->reg(), src_slot.offset(), type);
1741         return;
1742       }
1743       state.dec_used(slot_reg);
1744       dst_slot->MakeStack();
1745     }
1746     DCHECK_EQ(type, __ local_type(local_index));
1747     RegClass rc = reg_class_for(type);
1748     LiftoffRegister dst_reg = __ GetUnusedRegister(rc, {});
1749     __ Fill(dst_reg, src_slot.offset(), type);
1750     *dst_slot = LiftoffAssembler::VarState(type, dst_reg, dst_slot->offset());
1751     __ cache_state()->inc_used(dst_reg);
1752   }
1753 
LocalSet(uint32_t local_index,bool is_tee)1754   void LocalSet(uint32_t local_index, bool is_tee) {
1755     auto& state = *__ cache_state();
1756     auto& source_slot = state.stack_state.back();
1757     auto& target_slot = state.stack_state[local_index];
1758     switch (source_slot.loc()) {
1759       case kRegister:
1760         if (target_slot.is_reg()) state.dec_used(target_slot.reg());
1761         target_slot.Copy(source_slot);
1762         if (is_tee) state.inc_used(target_slot.reg());
1763         break;
1764       case kIntConst:
1765         if (target_slot.is_reg()) state.dec_used(target_slot.reg());
1766         target_slot.Copy(source_slot);
1767         break;
1768       case kStack:
1769         LocalSetFromStackSlot(&target_slot, local_index);
1770         break;
1771     }
1772     if (!is_tee) __ cache_state()->stack_state.pop_back();
1773   }
1774 
LocalSet(FullDecoder * decoder,const Value & value,const LocalIndexImmediate<validate> & imm)1775   void LocalSet(FullDecoder* decoder, const Value& value,
1776                 const LocalIndexImmediate<validate>& imm) {
1777     LocalSet(imm.index, false);
1778   }
1779 
LocalTee(FullDecoder * decoder,const Value & value,Value * result,const LocalIndexImmediate<validate> & imm)1780   void LocalTee(FullDecoder* decoder, const Value& value, Value* result,
1781                 const LocalIndexImmediate<validate>& imm) {
1782     LocalSet(imm.index, true);
1783   }
1784 
AllocateLocals(FullDecoder * decoder,Vector<Value> local_values)1785   void AllocateLocals(FullDecoder* decoder, Vector<Value> local_values) {
1786     // TODO(7748): Introduce typed functions bailout reason
1787     unsupported(decoder, kGC, "let");
1788   }
1789 
DeallocateLocals(FullDecoder * decoder,uint32_t count)1790   void DeallocateLocals(FullDecoder* decoder, uint32_t count) {
1791     // TODO(7748): Introduce typed functions bailout reason
1792     unsupported(decoder, kGC, "let");
1793   }
1794 
GetGlobalBaseAndOffset(const WasmGlobal * global,LiftoffRegList * pinned,uint32_t * offset)1795   Register GetGlobalBaseAndOffset(const WasmGlobal* global,
1796                                   LiftoffRegList* pinned, uint32_t* offset) {
1797     Register addr = pinned->set(__ GetUnusedRegister(kGpReg, {})).gp();
1798     if (global->mutability && global->imported) {
1799       LOAD_INSTANCE_FIELD(addr, ImportedMutableGlobals, kSystemPointerSize);
1800       __ Load(LiftoffRegister(addr), addr, no_reg,
1801               global->index * sizeof(Address), kPointerLoadType, *pinned);
1802       *offset = 0;
1803     } else {
1804       LOAD_INSTANCE_FIELD(addr, GlobalsStart, kSystemPointerSize);
1805       *offset = global->offset;
1806     }
1807     return addr;
1808   }
1809 
GlobalGet(FullDecoder * decoder,Value * result,const GlobalIndexImmediate<validate> & imm)1810   void GlobalGet(FullDecoder* decoder, Value* result,
1811                  const GlobalIndexImmediate<validate>& imm) {
1812     const auto* global = &env_->module->globals[imm.index];
1813     if (!CheckSupportedType(decoder,
1814                             FLAG_experimental_liftoff_extern_ref
1815                                 ? kSupportedTypes
1816                                 : kSupportedTypesWithoutRefs,
1817                             global->type, "global")) {
1818       return;
1819     }
1820 
1821     if (global->type.is_reference_type()) {
1822       if (global->mutability && global->imported) {
1823         unsupported(decoder, kRefTypes, "imported mutable globals");
1824         return;
1825       }
1826 
1827       LiftoffRegList pinned;
1828       Register globals_buffer =
1829           pinned.set(__ GetUnusedRegister(kGpReg, pinned)).gp();
1830       LOAD_TAGGED_PTR_INSTANCE_FIELD(globals_buffer, TaggedGlobalsBuffer);
1831       Register value = pinned.set(__ GetUnusedRegister(kGpReg, pinned)).gp();
1832       __ LoadTaggedPointer(value, globals_buffer, no_reg,
1833                            wasm::ObjectAccess::ElementOffsetInTaggedFixedArray(
1834                                imm.global->offset),
1835                            pinned);
1836       __ PushRegister(global->type, LiftoffRegister(value));
1837       return;
1838     }
1839     LiftoffRegList pinned;
1840     uint32_t offset = 0;
1841     Register addr = GetGlobalBaseAndOffset(global, &pinned, &offset);
1842     LiftoffRegister value =
1843         pinned.set(__ GetUnusedRegister(reg_class_for(global->type), pinned));
1844     LoadType type = LoadType::ForValueType(global->type);
1845     __ Load(value, addr, no_reg, offset, type, pinned, nullptr, true);
1846     __ PushRegister(global->type, value);
1847   }
1848 
GlobalSet(FullDecoder * decoder,const Value & value,const GlobalIndexImmediate<validate> & imm)1849   void GlobalSet(FullDecoder* decoder, const Value& value,
1850                  const GlobalIndexImmediate<validate>& imm) {
1851     auto* global = &env_->module->globals[imm.index];
1852     if (!CheckSupportedType(decoder,
1853                             FLAG_experimental_liftoff_extern_ref
1854                                 ? kSupportedTypes
1855                                 : kSupportedTypesWithoutRefs,
1856                             global->type, "global")) {
1857       return;
1858     }
1859 
1860     if (global->type.is_reference_type()) {
1861       if (global->mutability && global->imported) {
1862         unsupported(decoder, kRefTypes, "imported mutable globals");
1863         return;
1864       }
1865 
1866       LiftoffRegList pinned;
1867       Register globals_buffer =
1868           pinned.set(__ GetUnusedRegister(kGpReg, pinned)).gp();
1869       LOAD_TAGGED_PTR_INSTANCE_FIELD(globals_buffer, TaggedGlobalsBuffer);
1870       LiftoffRegister value = pinned.set(__ PopToRegister(pinned));
1871       __ StoreTaggedPointer(globals_buffer,
1872                             wasm::ObjectAccess::ElementOffsetInTaggedFixedArray(
1873                                 imm.global->offset),
1874                             value, pinned);
1875       return;
1876     }
1877     LiftoffRegList pinned;
1878     uint32_t offset = 0;
1879     Register addr = GetGlobalBaseAndOffset(global, &pinned, &offset);
1880     LiftoffRegister reg = pinned.set(__ PopToRegister(pinned));
1881     StoreType type = StoreType::ForValueType(global->type);
1882     __ Store(addr, no_reg, offset, reg, type, {}, nullptr, true);
1883   }
1884 
TableGet(FullDecoder * decoder,const Value & index,Value * result,const TableIndexImmediate<validate> & imm)1885   void TableGet(FullDecoder* decoder, const Value& index, Value* result,
1886                 const TableIndexImmediate<validate>& imm) {
1887     unsupported(decoder, kRefTypes, "table_get");
1888   }
1889 
TableSet(FullDecoder * decoder,const Value & index,const Value & value,const TableIndexImmediate<validate> & imm)1890   void TableSet(FullDecoder* decoder, const Value& index, const Value& value,
1891                 const TableIndexImmediate<validate>& imm) {
1892     unsupported(decoder, kRefTypes, "table_set");
1893   }
1894 
Unreachable(FullDecoder * decoder)1895   void Unreachable(FullDecoder* decoder) {
1896     Label* unreachable_label = AddOutOfLineTrap(
1897         decoder->position(), WasmCode::kThrowWasmTrapUnreachable);
1898     __ emit_jump(unreachable_label);
1899     __ AssertUnreachable(AbortReason::kUnexpectedReturnFromWasmTrap);
1900   }
1901 
Select(FullDecoder * decoder,const Value & cond,const Value & fval,const Value & tval,Value * result)1902   void Select(FullDecoder* decoder, const Value& cond, const Value& fval,
1903               const Value& tval, Value* result) {
1904     LiftoffRegList pinned;
1905     Register condition = pinned.set(__ PopToRegister()).gp();
1906     ValueType type = __ cache_state()->stack_state.end()[-1].type();
1907     DCHECK_EQ(type, __ cache_state()->stack_state.end()[-2].type());
1908     LiftoffRegister false_value = pinned.set(__ PopToRegister(pinned));
1909     LiftoffRegister true_value = __ PopToRegister(pinned);
1910     LiftoffRegister dst = __ GetUnusedRegister(true_value.reg_class(),
1911                                                {true_value, false_value}, {});
1912     if (!__ emit_select(dst, condition, true_value, false_value, type)) {
1913       // Emit generic code (using branches) instead.
1914       Label cont;
1915       Label case_false;
1916       __ emit_cond_jump(kEqual, &case_false, kWasmI32, condition);
1917       if (dst != true_value) __ Move(dst, true_value, type);
1918       __ emit_jump(&cont);
1919 
1920       __ bind(&case_false);
1921       if (dst != false_value) __ Move(dst, false_value, type);
1922       __ bind(&cont);
1923     }
1924     __ PushRegister(type, dst);
1925   }
1926 
BrImpl(Control * target)1927   void BrImpl(Control* target) {
1928     if (!target->br_merge()->reached) {
1929       target->label_state.InitMerge(*__ cache_state(), __ num_locals(),
1930                                     target->br_merge()->arity,
1931                                     target->stack_depth);
1932     }
1933     __ MergeStackWith(target->label_state, target->br_merge()->arity);
1934     __ jmp(target->label.get());
1935   }
1936 
Br(FullDecoder * decoder,Control * target)1937   void Br(FullDecoder* decoder, Control* target) { BrImpl(target); }
1938 
BrOrRet(FullDecoder * decoder,uint32_t depth)1939   void BrOrRet(FullDecoder* decoder, uint32_t depth) {
1940     if (depth == decoder->control_depth() - 1) {
1941       ReturnImpl(decoder);
1942     } else {
1943       BrImpl(decoder->control_at(depth));
1944     }
1945   }
1946 
BrIf(FullDecoder * decoder,const Value &,uint32_t depth)1947   void BrIf(FullDecoder* decoder, const Value& /* cond */, uint32_t depth) {
1948     // Before branching, materialize all constants. This avoids repeatedly
1949     // materializing them for each conditional branch.
1950     // TODO(clemensb): Do the same for br_table.
1951     if (depth != decoder->control_depth() - 1) {
1952       __ MaterializeMergedConstants(
1953           decoder->control_at(depth)->br_merge()->arity);
1954     }
1955 
1956     Label cont_false;
1957     Register value = __ PopToRegister().gp();
1958 
1959     if (!has_outstanding_op()) {
1960       // Unary "equal" means "equals zero".
1961       __ emit_cond_jump(kEqual, &cont_false, kWasmI32, value);
1962     } else if (outstanding_op_ == kExprI32Eqz) {
1963       // Unary "unequal" means "not equals zero".
1964       __ emit_cond_jump(kUnequal, &cont_false, kWasmI32, value);
1965       outstanding_op_ = kNoOutstandingOp;
1966     } else {
1967       // Otherwise, it's an i32 compare opcode.
1968       Condition cond = NegateCondition(GetCompareCondition(outstanding_op_));
1969       Register rhs = value;
1970       Register lhs = __ PopToRegister(LiftoffRegList::ForRegs(rhs)).gp();
1971       __ emit_cond_jump(cond, &cont_false, kWasmI32, lhs, rhs);
1972       outstanding_op_ = kNoOutstandingOp;
1973     }
1974 
1975     BrOrRet(decoder, depth);
1976     __ bind(&cont_false);
1977   }
1978 
1979   // Generate a branch table case, potentially reusing previously generated
1980   // stack transfer code.
GenerateBrCase(FullDecoder * decoder,uint32_t br_depth,std::map<uint32_t,MovableLabel> * br_targets)1981   void GenerateBrCase(FullDecoder* decoder, uint32_t br_depth,
1982                       std::map<uint32_t, MovableLabel>* br_targets) {
1983     MovableLabel& label = (*br_targets)[br_depth];
1984     if (label.get()->is_bound()) {
1985       __ jmp(label.get());
1986     } else {
1987       __ bind(label.get());
1988       BrOrRet(decoder, br_depth);
1989     }
1990   }
1991 
1992   // Generate a branch table for input in [min, max).
1993   // TODO(wasm): Generate a real branch table (like TF TableSwitch).
GenerateBrTable(FullDecoder * decoder,LiftoffRegister tmp,LiftoffRegister value,uint32_t min,uint32_t max,BranchTableIterator<validate> * table_iterator,std::map<uint32_t,MovableLabel> * br_targets)1994   void GenerateBrTable(FullDecoder* decoder, LiftoffRegister tmp,
1995                        LiftoffRegister value, uint32_t min, uint32_t max,
1996                        BranchTableIterator<validate>* table_iterator,
1997                        std::map<uint32_t, MovableLabel>* br_targets) {
1998     DCHECK_LT(min, max);
1999     // Check base case.
2000     if (max == min + 1) {
2001       DCHECK_EQ(min, table_iterator->cur_index());
2002       GenerateBrCase(decoder, table_iterator->next(), br_targets);
2003       return;
2004     }
2005 
2006     uint32_t split = min + (max - min) / 2;
2007     Label upper_half;
2008     __ LoadConstant(tmp, WasmValue(split));
2009     __ emit_cond_jump(kUnsignedGreaterEqual, &upper_half, kWasmI32, value.gp(),
2010                       tmp.gp());
2011     // Emit br table for lower half:
2012     GenerateBrTable(decoder, tmp, value, min, split, table_iterator,
2013                     br_targets);
2014     __ bind(&upper_half);
2015     // table_iterator will trigger a DCHECK if we don't stop decoding now.
2016     if (did_bailout()) return;
2017     // Emit br table for upper half:
2018     GenerateBrTable(decoder, tmp, value, split, max, table_iterator,
2019                     br_targets);
2020   }
2021 
BrTable(FullDecoder * decoder,const BranchTableImmediate<validate> & imm,const Value & key)2022   void BrTable(FullDecoder* decoder, const BranchTableImmediate<validate>& imm,
2023                const Value& key) {
2024     LiftoffRegList pinned;
2025     LiftoffRegister value = pinned.set(__ PopToRegister());
2026     BranchTableIterator<validate> table_iterator(decoder, imm);
2027     std::map<uint32_t, MovableLabel> br_targets;
2028 
2029     if (imm.table_count > 0) {
2030       LiftoffRegister tmp = __ GetUnusedRegister(kGpReg, pinned);
2031       __ LoadConstant(tmp, WasmValue(uint32_t{imm.table_count}));
2032       Label case_default;
2033       __ emit_cond_jump(kUnsignedGreaterEqual, &case_default, kWasmI32,
2034                         value.gp(), tmp.gp());
2035 
2036       GenerateBrTable(decoder, tmp, value, 0, imm.table_count, &table_iterator,
2037                       &br_targets);
2038 
2039       __ bind(&case_default);
2040       // table_iterator will trigger a DCHECK if we don't stop decoding now.
2041       if (did_bailout()) return;
2042     }
2043 
2044     // Generate the default case.
2045     GenerateBrCase(decoder, table_iterator.next(), &br_targets);
2046     DCHECK(!table_iterator.has_next());
2047   }
2048 
Else(FullDecoder * decoder,Control * c)2049   void Else(FullDecoder* decoder, Control* c) {
2050     if (c->reachable()) {
2051       if (!c->end_merge.reached) {
2052         c->label_state.InitMerge(*__ cache_state(), __ num_locals(),
2053                                  c->end_merge.arity, c->stack_depth);
2054       }
2055       __ MergeFullStackWith(c->label_state, *__ cache_state());
2056       __ emit_jump(c->label.get());
2057     }
2058     __ bind(c->else_state->label.get());
2059     __ cache_state()->Steal(c->else_state->state);
2060   }
2061 
GetSpilledRegistersForInspection()2062   SpilledRegistersForInspection* GetSpilledRegistersForInspection() {
2063     DCHECK(for_debugging_);
2064     // If we are generating debugging code, we really need to spill all
2065     // registers to make them inspectable when stopping at the trap.
2066     auto* spilled = compilation_zone_->New<SpilledRegistersForInspection>(
2067         compilation_zone_);
2068     for (uint32_t i = 0, e = __ cache_state()->stack_height(); i < e; ++i) {
2069       auto& slot = __ cache_state()->stack_state[i];
2070       if (!slot.is_reg()) continue;
2071       spilled->entries.push_back(SpilledRegistersForInspection::Entry{
2072           slot.offset(), slot.reg(), slot.type()});
2073     }
2074     return spilled;
2075   }
2076 
AddOutOfLineTrap(WasmCodePosition position,WasmCode::RuntimeStubId stub,uint32_t pc=0)2077   Label* AddOutOfLineTrap(WasmCodePosition position,
2078                           WasmCode::RuntimeStubId stub, uint32_t pc = 0) {
2079     DCHECK(FLAG_wasm_bounds_checks);
2080 
2081     out_of_line_code_.push_back(OutOfLineCode::Trap(
2082         stub, position, pc,
2083         RegisterDebugSideTableEntry(DebugSideTableBuilder::kAssumeSpilling),
2084         V8_UNLIKELY(for_debugging_) ? GetSpilledRegistersForInspection()
2085                                     : nullptr));
2086     return out_of_line_code_.back().label.get();
2087   }
2088 
2089   enum ForceCheck : bool { kDoForceCheck = true, kDontForceCheck = false };
2090 
2091   // Returns true if the memory access is statically known to be out of bounds
2092   // (a jump to the trap was generated then); return false otherwise.
BoundsCheckMem(FullDecoder * decoder,uint32_t access_size,uint64_t offset,Register index,LiftoffRegList pinned,ForceCheck force_check)2093   bool BoundsCheckMem(FullDecoder* decoder, uint32_t access_size,
2094                       uint64_t offset, Register index, LiftoffRegList pinned,
2095                       ForceCheck force_check) {
2096     // If the offset does not fit in a uintptr_t, this can never succeed on this
2097     // machine.
2098     const bool statically_oob =
2099         offset > std::numeric_limits<uintptr_t>::max() ||
2100         !base::IsInBounds<uintptr_t>(offset, access_size,
2101                                      env_->max_memory_size);
2102 
2103     if (!force_check && !statically_oob &&
2104         (!FLAG_wasm_bounds_checks || env_->use_trap_handler)) {
2105       return false;
2106     }
2107 
2108     // TODO(wasm): This adds protected instruction information for the jump
2109     // instruction we are about to generate. It would be better to just not add
2110     // protected instruction info when the pc is 0.
2111     Label* trap_label = AddOutOfLineTrap(
2112         decoder->position(), WasmCode::kThrowWasmTrapMemOutOfBounds,
2113         env_->use_trap_handler ? __ pc_offset() : 0);
2114 
2115     if (statically_oob) {
2116       __ emit_jump(trap_label);
2117       decoder->SetSucceedingCodeDynamicallyUnreachable();
2118       return true;
2119     }
2120 
2121     uintptr_t end_offset = offset + access_size - 1u;
2122 
2123     // If the end offset is larger than the smallest memory, dynamically check
2124     // the end offset against the actual memory size, which is not known at
2125     // compile time. Otherwise, only one check is required (see below).
2126     LiftoffRegister end_offset_reg =
2127         pinned.set(__ GetUnusedRegister(kGpReg, pinned));
2128     Register mem_size = __ GetUnusedRegister(kGpReg, pinned).gp();
2129     LOAD_INSTANCE_FIELD(mem_size, MemorySize, kSystemPointerSize);
2130 
2131     __ LoadConstant(end_offset_reg, WasmValue::ForUintPtr(end_offset));
2132 
2133     if (end_offset >= env_->min_memory_size) {
2134       __ emit_cond_jump(kUnsignedGreaterEqual, trap_label,
2135                         LiftoffAssembler::kWasmIntPtr, end_offset_reg.gp(),
2136                         mem_size);
2137     }
2138 
2139     // Just reuse the end_offset register for computing the effective size.
2140     LiftoffRegister effective_size_reg = end_offset_reg;
2141     __ emit_ptrsize_sub(effective_size_reg.gp(), mem_size, end_offset_reg.gp());
2142 
2143     __ emit_u32_to_intptr(index, index);
2144 
2145     __ emit_cond_jump(kUnsignedGreaterEqual, trap_label,
2146                       LiftoffAssembler::kWasmIntPtr, index,
2147                       effective_size_reg.gp());
2148     return false;
2149   }
2150 
AlignmentCheckMem(FullDecoder * decoder,uint32_t access_size,uint32_t offset,Register index,LiftoffRegList pinned)2151   void AlignmentCheckMem(FullDecoder* decoder, uint32_t access_size,
2152                          uint32_t offset, Register index,
2153                          LiftoffRegList pinned) {
2154     Label* trap_label = AddOutOfLineTrap(
2155         decoder->position(), WasmCode::kThrowWasmTrapUnalignedAccess, 0);
2156     Register address = __ GetUnusedRegister(kGpReg, pinned).gp();
2157 
2158     const uint32_t align_mask = access_size - 1;
2159     if ((offset & align_mask) == 0) {
2160       // If {offset} is aligned, we can produce faster code.
2161 
2162       // TODO(ahaas): On Intel, the "test" instruction implicitly computes the
2163       // AND of two operands. We could introduce a new variant of
2164       // {emit_cond_jump} to use the "test" instruction without the "and" here.
2165       // Then we can also avoid using the temp register here.
2166       __ emit_i32_andi(address, index, align_mask);
2167       __ emit_cond_jump(kUnequal, trap_label, kWasmI32, address);
2168       return;
2169     }
2170     __ emit_i32_addi(address, index, offset);
2171     __ emit_i32_andi(address, address, align_mask);
2172 
2173     __ emit_cond_jump(kUnequal, trap_label, kWasmI32, address);
2174   }
2175 
TraceMemoryOperation(bool is_store,MachineRepresentation rep,Register index,uint32_t offset,WasmCodePosition position)2176   void TraceMemoryOperation(bool is_store, MachineRepresentation rep,
2177                             Register index, uint32_t offset,
2178                             WasmCodePosition position) {
2179     // Before making the runtime call, spill all cache registers.
2180     __ SpillAllRegisters();
2181 
2182     LiftoffRegList pinned = LiftoffRegList::ForRegs(index);
2183     // Get one register for computing the effective offset (offset + index).
2184     LiftoffRegister effective_offset =
2185         pinned.set(__ GetUnusedRegister(kGpReg, pinned));
2186     __ LoadConstant(effective_offset, WasmValue(offset));
2187     __ emit_i32_add(effective_offset.gp(), effective_offset.gp(), index);
2188 
2189     // Get a register to hold the stack slot for MemoryTracingInfo.
2190     LiftoffRegister info = pinned.set(__ GetUnusedRegister(kGpReg, pinned));
2191     // Allocate stack slot for MemoryTracingInfo.
2192     __ AllocateStackSlot(info.gp(), sizeof(MemoryTracingInfo));
2193 
2194     // Reuse the {effective_offset} register for all information to be stored in
2195     // the MemoryTracingInfo struct.
2196     LiftoffRegister data = effective_offset;
2197 
2198     // Now store all information into the MemoryTracingInfo struct.
2199     if (kSystemPointerSize == 8) {
2200       // Zero-extend the effective offset to u64.
2201       CHECK(__ emit_type_conversion(kExprI64UConvertI32, data, effective_offset,
2202                                     nullptr));
2203     }
2204     __ Store(
2205         info.gp(), no_reg, offsetof(MemoryTracingInfo, offset), data,
2206         kSystemPointerSize == 8 ? StoreType::kI64Store : StoreType::kI32Store,
2207         pinned);
2208     __ LoadConstant(data, WasmValue(is_store ? 1 : 0));
2209     __ Store(info.gp(), no_reg, offsetof(MemoryTracingInfo, is_store), data,
2210              StoreType::kI32Store8, pinned);
2211     __ LoadConstant(data, WasmValue(static_cast<int>(rep)));
2212     __ Store(info.gp(), no_reg, offsetof(MemoryTracingInfo, mem_rep), data,
2213              StoreType::kI32Store8, pinned);
2214 
2215     WasmTraceMemoryDescriptor descriptor;
2216     DCHECK_EQ(0, descriptor.GetStackParameterCount());
2217     DCHECK_EQ(1, descriptor.GetRegisterParameterCount());
2218     Register param_reg = descriptor.GetRegisterParameter(0);
2219     if (info.gp() != param_reg) {
2220       __ Move(param_reg, info.gp(), LiftoffAssembler::kWasmIntPtr);
2221     }
2222 
2223     source_position_table_builder_.AddPosition(__ pc_offset(),
2224                                                SourcePosition(position), false);
2225     __ CallRuntimeStub(WasmCode::kWasmTraceMemory);
2226     DefineSafepoint();
2227 
2228     __ DeallocateStackSlot(sizeof(MemoryTracingInfo));
2229   }
2230 
AddMemoryMasking(Register index,uint32_t * offset,LiftoffRegList * pinned)2231   Register AddMemoryMasking(Register index, uint32_t* offset,
2232                             LiftoffRegList* pinned) {
2233     if (!FLAG_untrusted_code_mitigations || env_->use_trap_handler) {
2234       return index;
2235     }
2236     DEBUG_CODE_COMMENT("mask memory index");
2237     // Make sure that we can overwrite {index}.
2238     if (__ cache_state()->is_used(LiftoffRegister(index))) {
2239       Register old_index = index;
2240       pinned->clear(LiftoffRegister(old_index));
2241       index = pinned->set(__ GetUnusedRegister(kGpReg, *pinned)).gp();
2242       if (index != old_index) __ Move(index, old_index, kWasmI32);
2243     }
2244     Register tmp = __ GetUnusedRegister(kGpReg, *pinned).gp();
2245     __ emit_ptrsize_addi(index, index, *offset);
2246     LOAD_INSTANCE_FIELD(tmp, MemoryMask, kSystemPointerSize);
2247     __ emit_ptrsize_and(index, index, tmp);
2248     *offset = 0;
2249     return index;
2250   }
2251 
LoadMem(FullDecoder * decoder,LoadType type,const MemoryAccessImmediate<validate> & imm,const Value & index_val,Value * result)2252   void LoadMem(FullDecoder* decoder, LoadType type,
2253                const MemoryAccessImmediate<validate>& imm,
2254                const Value& index_val, Value* result) {
2255     ValueType value_type = type.value_type();
2256     if (!CheckSupportedType(decoder, kSupportedTypes, value_type, "load"))
2257       return;
2258     LiftoffRegList pinned;
2259     Register index = pinned.set(__ PopToRegister()).gp();
2260     if (BoundsCheckMem(decoder, type.size(), imm.offset, index, pinned,
2261                        kDontForceCheck)) {
2262       return;
2263     }
2264     uint32_t offset = imm.offset;
2265     index = AddMemoryMasking(index, &offset, &pinned);
2266     DEBUG_CODE_COMMENT("load from memory");
2267     Register addr = pinned.set(__ GetUnusedRegister(kGpReg, pinned)).gp();
2268     LOAD_INSTANCE_FIELD(addr, MemoryStart, kSystemPointerSize);
2269     RegClass rc = reg_class_for(value_type);
2270     LiftoffRegister value = pinned.set(__ GetUnusedRegister(rc, pinned));
2271     uint32_t protected_load_pc = 0;
2272     __ Load(value, addr, index, offset, type, pinned, &protected_load_pc, true);
2273     if (env_->use_trap_handler) {
2274       AddOutOfLineTrap(decoder->position(),
2275                        WasmCode::kThrowWasmTrapMemOutOfBounds,
2276                        protected_load_pc);
2277     }
2278     __ PushRegister(value_type, value);
2279 
2280     if (FLAG_trace_wasm_memory) {
2281       TraceMemoryOperation(false, type.mem_type().representation(), index,
2282                            offset, decoder->position());
2283     }
2284   }
2285 
LoadTransform(FullDecoder * decoder,LoadType type,LoadTransformationKind transform,const MemoryAccessImmediate<validate> & imm,const Value & index_val,Value * result)2286   void LoadTransform(FullDecoder* decoder, LoadType type,
2287                      LoadTransformationKind transform,
2288                      const MemoryAccessImmediate<validate>& imm,
2289                      const Value& index_val, Value* result) {
2290     // LoadTransform requires SIMD support, so check for it here. If
2291     // unsupported, bailout and let TurboFan lower the code.
2292     if (!CheckSupportedType(decoder, kSupportedTypes, kWasmS128,
2293                             "LoadTransform")) {
2294       return;
2295     }
2296 
2297     LiftoffRegList pinned;
2298     Register index = pinned.set(__ PopToRegister()).gp();
2299     // For load splats and load zero, LoadType is the size of the load, and for
2300     // load extends, LoadType is the size of the lane, and it always loads 8
2301     // bytes.
2302     uint32_t access_size =
2303         transform == LoadTransformationKind::kExtend ? 8 : type.size();
2304     if (BoundsCheckMem(decoder, access_size, imm.offset, index, pinned,
2305                        kDontForceCheck)) {
2306       return;
2307     }
2308 
2309     uint32_t offset = imm.offset;
2310     index = AddMemoryMasking(index, &offset, &pinned);
2311     DEBUG_CODE_COMMENT("load with transformation");
2312     Register addr = __ GetUnusedRegister(kGpReg, pinned).gp();
2313     LOAD_INSTANCE_FIELD(addr, MemoryStart, kSystemPointerSize);
2314     LiftoffRegister value = __ GetUnusedRegister(reg_class_for(kS128), {});
2315     uint32_t protected_load_pc = 0;
2316     __ LoadTransform(value, addr, index, offset, type, transform,
2317                      &protected_load_pc);
2318 
2319     if (env_->use_trap_handler) {
2320       AddOutOfLineTrap(decoder->position(),
2321                        WasmCode::kThrowWasmTrapMemOutOfBounds,
2322                        protected_load_pc);
2323     }
2324     __ PushRegister(ValueType::Primitive(kS128), value);
2325 
2326     if (FLAG_trace_wasm_memory) {
2327       // Again load extend is different.
2328       MachineRepresentation mem_rep =
2329           transform == LoadTransformationKind::kExtend
2330               ? MachineRepresentation::kWord64
2331               : type.mem_type().representation();
2332       TraceMemoryOperation(false, mem_rep, index, offset, decoder->position());
2333     }
2334   }
2335 
LoadLane(FullDecoder * decoder,LoadType type,const Value & value,const Value & index,const MemoryAccessImmediate<validate> & imm,const uint8_t laneidx,Value * result)2336   void LoadLane(FullDecoder* decoder, LoadType type, const Value& value,
2337                 const Value& index, const MemoryAccessImmediate<validate>& imm,
2338                 const uint8_t laneidx, Value* result) {
2339     unsupported(decoder, kSimd, "simd load lane");
2340   }
2341 
StoreMem(FullDecoder * decoder,StoreType type,const MemoryAccessImmediate<validate> & imm,const Value & index_val,const Value & value_val)2342   void StoreMem(FullDecoder* decoder, StoreType type,
2343                 const MemoryAccessImmediate<validate>& imm,
2344                 const Value& index_val, const Value& value_val) {
2345     ValueType value_type = type.value_type();
2346     if (!CheckSupportedType(decoder, kSupportedTypes, value_type, "store"))
2347       return;
2348     LiftoffRegList pinned;
2349     LiftoffRegister value = pinned.set(__ PopToRegister());
2350     Register index = pinned.set(__ PopToRegister(pinned)).gp();
2351     if (BoundsCheckMem(decoder, type.size(), imm.offset, index, pinned,
2352                        kDontForceCheck)) {
2353       return;
2354     }
2355     uint32_t offset = imm.offset;
2356     index = AddMemoryMasking(index, &offset, &pinned);
2357     DEBUG_CODE_COMMENT("store to memory");
2358     Register addr = pinned.set(__ GetUnusedRegister(kGpReg, pinned)).gp();
2359     LOAD_INSTANCE_FIELD(addr, MemoryStart, kSystemPointerSize);
2360     uint32_t protected_store_pc = 0;
2361     LiftoffRegList outer_pinned;
2362     if (FLAG_trace_wasm_memory) outer_pinned.set(index);
2363     __ Store(addr, index, offset, value, type, outer_pinned,
2364              &protected_store_pc, true);
2365     if (env_->use_trap_handler) {
2366       AddOutOfLineTrap(decoder->position(),
2367                        WasmCode::kThrowWasmTrapMemOutOfBounds,
2368                        protected_store_pc);
2369     }
2370     if (FLAG_trace_wasm_memory) {
2371       TraceMemoryOperation(true, type.mem_rep(), index, offset,
2372                            decoder->position());
2373     }
2374   }
2375 
StoreLane(FullDecoder * decoder,StoreType type,const MemoryAccessImmediate<validate> & imm,const Value & index,const Value & value,const uint8_t laneidx)2376   void StoreLane(FullDecoder* decoder, StoreType type,
2377                  const MemoryAccessImmediate<validate>& imm, const Value& index,
2378                  const Value& value, const uint8_t laneidx) {
2379     unsupported(decoder, kSimd, "simd load lane");
2380   }
2381 
CurrentMemoryPages(FullDecoder * decoder,Value * result)2382   void CurrentMemoryPages(FullDecoder* decoder, Value* result) {
2383     Register mem_size = __ GetUnusedRegister(kGpReg, {}).gp();
2384     LOAD_INSTANCE_FIELD(mem_size, MemorySize, kSystemPointerSize);
2385     __ emit_ptrsize_shri(mem_size, mem_size, kWasmPageSizeLog2);
2386     __ PushRegister(kWasmI32, LiftoffRegister(mem_size));
2387   }
2388 
MemoryGrow(FullDecoder * decoder,const Value & value,Value * result_val)2389   void MemoryGrow(FullDecoder* decoder, const Value& value, Value* result_val) {
2390     // Pop the input, then spill all cache registers to make the runtime call.
2391     LiftoffRegList pinned;
2392     LiftoffRegister input = pinned.set(__ PopToRegister());
2393     __ SpillAllRegisters();
2394 
2395     constexpr Register kGpReturnReg = kGpReturnRegisters[0];
2396     static_assert(kLiftoffAssemblerGpCacheRegs & kGpReturnReg.bit(),
2397                   "first return register is a cache register (needs more "
2398                   "complex code here otherwise)");
2399     LiftoffRegister result = pinned.set(LiftoffRegister(kGpReturnReg));
2400 
2401     WasmMemoryGrowDescriptor descriptor;
2402     DCHECK_EQ(0, descriptor.GetStackParameterCount());
2403     DCHECK_EQ(1, descriptor.GetRegisterParameterCount());
2404     DCHECK_EQ(kWasmI32.machine_type(), descriptor.GetParameterType(0));
2405 
2406     Register param_reg = descriptor.GetRegisterParameter(0);
2407     if (input.gp() != param_reg) __ Move(param_reg, input.gp(), kWasmI32);
2408 
2409     __ CallRuntimeStub(WasmCode::kWasmMemoryGrow);
2410     DefineSafepoint();
2411     RegisterDebugSideTableEntry(DebugSideTableBuilder::kDidSpill);
2412 
2413     if (kReturnRegister0 != result.gp()) {
2414       __ Move(result.gp(), kReturnRegister0, kWasmI32);
2415     }
2416 
2417     __ PushRegister(kWasmI32, result);
2418   }
2419 
RegisterDebugSideTableEntry(DebugSideTableBuilder::AssumeSpilling assume_spilling)2420   DebugSideTableBuilder::EntryBuilder* RegisterDebugSideTableEntry(
2421       DebugSideTableBuilder::AssumeSpilling assume_spilling) {
2422     if (V8_LIKELY(!debug_sidetable_builder_)) return nullptr;
2423     int stack_height = static_cast<int>(__ cache_state()->stack_height());
2424     return debug_sidetable_builder_->NewEntry(
2425         __ pc_offset(), __ num_locals(), stack_height,
2426         __ cache_state()->stack_state.begin(), assume_spilling);
2427   }
2428 
2429   enum CallKind : bool { kReturnCall = true, kNoReturnCall = false };
2430 
CallDirect(FullDecoder * decoder,const CallFunctionImmediate<validate> & imm,const Value args[],Value[])2431   void CallDirect(FullDecoder* decoder,
2432                   const CallFunctionImmediate<validate>& imm,
2433                   const Value args[], Value[]) {
2434     CallDirect(decoder, imm, args, nullptr, kNoReturnCall);
2435   }
2436 
CallIndirect(FullDecoder * decoder,const Value & index_val,const CallIndirectImmediate<validate> & imm,const Value args[],Value returns[])2437   void CallIndirect(FullDecoder* decoder, const Value& index_val,
2438                     const CallIndirectImmediate<validate>& imm,
2439                     const Value args[], Value returns[]) {
2440     CallIndirect(decoder, index_val, imm, kNoReturnCall);
2441   }
2442 
CallRef(FullDecoder * decoder,const Value & func_ref,const FunctionSig * sig,uint32_t sig_index,const Value args[],Value returns[])2443   void CallRef(FullDecoder* decoder, const Value& func_ref,
2444                const FunctionSig* sig, uint32_t sig_index, const Value args[],
2445                Value returns[]) {
2446     unsupported(decoder, kRefTypes, "call_ref");
2447   }
2448 
ReturnCall(FullDecoder * decoder,const CallFunctionImmediate<validate> & imm,const Value args[])2449   void ReturnCall(FullDecoder* decoder,
2450                   const CallFunctionImmediate<validate>& imm,
2451                   const Value args[]) {
2452     CallDirect(decoder, imm, args, nullptr, kReturnCall);
2453   }
2454 
ReturnCallIndirect(FullDecoder * decoder,const Value & index_val,const CallIndirectImmediate<validate> & imm,const Value args[])2455   void ReturnCallIndirect(FullDecoder* decoder, const Value& index_val,
2456                           const CallIndirectImmediate<validate>& imm,
2457                           const Value args[]) {
2458     CallIndirect(decoder, index_val, imm, kReturnCall);
2459   }
2460 
ReturnCallRef(FullDecoder * decoder,const Value & func_ref,const FunctionSig * sig,uint32_t sig_index,const Value args[])2461   void ReturnCallRef(FullDecoder* decoder, const Value& func_ref,
2462                      const FunctionSig* sig, uint32_t sig_index,
2463                      const Value args[]) {
2464     unsupported(decoder, kRefTypes, "call_ref");
2465   }
2466 
BrOnNull(FullDecoder * decoder,const Value & ref_object,uint32_t depth)2467   void BrOnNull(FullDecoder* decoder, const Value& ref_object, uint32_t depth) {
2468     unsupported(decoder, kRefTypes, "br_on_null");
2469   }
2470 
2471   template <ValueType::Kind src_type, ValueType::Kind result_type,
2472             typename EmitFn>
EmitTerOp(EmitFn fn)2473   void EmitTerOp(EmitFn fn) {
2474     static constexpr RegClass src_rc = reg_class_for(src_type);
2475     static constexpr RegClass result_rc = reg_class_for(result_type);
2476     LiftoffRegister src3 = __ PopToRegister();
2477     LiftoffRegister src2 = __ PopToRegister(LiftoffRegList::ForRegs(src3));
2478     LiftoffRegister src1 =
2479         __ PopToRegister(LiftoffRegList::ForRegs(src3, src2));
2480     // Reusing src1 and src2 will complicate codegen for select for some
2481     // backend, so we allow only reusing src3 (the mask), and pin src1 and src2.
2482     LiftoffRegister dst =
2483         src_rc == result_rc
2484             ? __ GetUnusedRegister(result_rc, {src3},
2485                                    LiftoffRegList::ForRegs(src1, src2))
2486             : __ GetUnusedRegister(result_rc, {});
2487     CallEmitFn(fn, dst, src1, src2, src3);
2488     __ PushRegister(ValueType::Primitive(result_type), dst);
2489   }
2490 
2491   template <typename EmitFn, typename EmitFnImm>
EmitSimdShiftOp(EmitFn fn,EmitFnImm fnImm)2492   void EmitSimdShiftOp(EmitFn fn, EmitFnImm fnImm) {
2493     static constexpr RegClass result_rc = reg_class_for(ValueType::kS128);
2494 
2495     LiftoffAssembler::VarState rhs_slot = __ cache_state()->stack_state.back();
2496     // Check if the RHS is an immediate.
2497     if (rhs_slot.is_const()) {
2498       __ cache_state()->stack_state.pop_back();
2499       int32_t imm = rhs_slot.i32_const();
2500 
2501       LiftoffRegister operand = __ PopToRegister();
2502       LiftoffRegister dst = __ GetUnusedRegister(result_rc, {operand}, {});
2503 
2504       CallEmitFn(fnImm, dst, operand, imm);
2505       __ PushRegister(kWasmS128, dst);
2506     } else {
2507       LiftoffRegister count = __ PopToRegister();
2508       LiftoffRegister operand = __ PopToRegister();
2509       LiftoffRegister dst = __ GetUnusedRegister(result_rc, {operand}, {});
2510 
2511       CallEmitFn(fn, dst, operand, count);
2512       __ PushRegister(kWasmS128, dst);
2513     }
2514   }
2515 
EmitSimdFloatRoundingOpWithCFallback(bool (LiftoffAssembler::* emit_fn)(LiftoffRegister,LiftoffRegister),ExternalReference (* ext_ref)())2516   void EmitSimdFloatRoundingOpWithCFallback(
2517       bool (LiftoffAssembler::*emit_fn)(LiftoffRegister, LiftoffRegister),
2518       ExternalReference (*ext_ref)()) {
2519     static constexpr RegClass rc = reg_class_for(kWasmS128);
2520     LiftoffRegister src = __ PopToRegister();
2521     LiftoffRegister dst = __ GetUnusedRegister(rc, {src}, {});
2522     if (!(asm_.*emit_fn)(dst, src)) {
2523       // Return v128 via stack for ARM.
2524       ValueType sig_v_s_reps[] = {kWasmS128};
2525       FunctionSig sig_v_s(0, 1, sig_v_s_reps);
2526       GenerateCCall(&dst, &sig_v_s, kWasmS128, &src, ext_ref());
2527     }
2528     __ PushRegister(kWasmS128, dst);
2529   }
2530 
SimdOp(FullDecoder * decoder,WasmOpcode opcode,Vector<Value> args,Value * result)2531   void SimdOp(FullDecoder* decoder, WasmOpcode opcode, Vector<Value> args,
2532               Value* result) {
2533     if (!CpuFeatures::SupportsWasmSimd128()) {
2534       return unsupported(decoder, kSimd, "simd");
2535     }
2536     switch (opcode) {
2537       case wasm::kExprI8x16Swizzle:
2538         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_i8x16_swizzle);
2539       case wasm::kExprI8x16Splat:
2540         return EmitUnOp<kI32, kS128>(&LiftoffAssembler::emit_i8x16_splat);
2541       case wasm::kExprI16x8Splat:
2542         return EmitUnOp<kI32, kS128>(&LiftoffAssembler::emit_i16x8_splat);
2543       case wasm::kExprI32x4Splat:
2544         return EmitUnOp<kI32, kS128>(&LiftoffAssembler::emit_i32x4_splat);
2545       case wasm::kExprI64x2Splat:
2546         return EmitUnOp<kI64, kS128>(&LiftoffAssembler::emit_i64x2_splat);
2547       case wasm::kExprF32x4Splat:
2548         return EmitUnOp<kF32, kS128>(&LiftoffAssembler::emit_f32x4_splat);
2549       case wasm::kExprF64x2Splat:
2550         return EmitUnOp<kF64, kS128>(&LiftoffAssembler::emit_f64x2_splat);
2551       case wasm::kExprI8x16Eq:
2552         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_i8x16_eq);
2553       case wasm::kExprI8x16Ne:
2554         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_i8x16_ne);
2555       case wasm::kExprI8x16LtS:
2556         return EmitBinOp<kS128, kS128, true>(
2557             &LiftoffAssembler::emit_i8x16_gt_s);
2558       case wasm::kExprI8x16LtU:
2559         return EmitBinOp<kS128, kS128, true>(
2560             &LiftoffAssembler::emit_i8x16_gt_u);
2561       case wasm::kExprI8x16GtS:
2562         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_i8x16_gt_s);
2563       case wasm::kExprI8x16GtU:
2564         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_i8x16_gt_u);
2565       case wasm::kExprI8x16LeS:
2566         return EmitBinOp<kS128, kS128, true>(
2567             &LiftoffAssembler::emit_i8x16_ge_s);
2568       case wasm::kExprI8x16LeU:
2569         return EmitBinOp<kS128, kS128, true>(
2570             &LiftoffAssembler::emit_i8x16_ge_u);
2571       case wasm::kExprI8x16GeS:
2572         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_i8x16_ge_s);
2573       case wasm::kExprI8x16GeU:
2574         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_i8x16_ge_u);
2575       case wasm::kExprI16x8Eq:
2576         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_i16x8_eq);
2577       case wasm::kExprI16x8Ne:
2578         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_i16x8_ne);
2579       case wasm::kExprI16x8LtS:
2580         return EmitBinOp<kS128, kS128, true>(
2581             &LiftoffAssembler::emit_i16x8_gt_s);
2582       case wasm::kExprI16x8LtU:
2583         return EmitBinOp<kS128, kS128, true>(
2584             &LiftoffAssembler::emit_i16x8_gt_u);
2585       case wasm::kExprI16x8GtS:
2586         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_i16x8_gt_s);
2587       case wasm::kExprI16x8GtU:
2588         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_i16x8_gt_u);
2589       case wasm::kExprI16x8LeS:
2590         return EmitBinOp<kS128, kS128, true>(
2591             &LiftoffAssembler::emit_i16x8_ge_s);
2592       case wasm::kExprI16x8LeU:
2593         return EmitBinOp<kS128, kS128, true>(
2594             &LiftoffAssembler::emit_i16x8_ge_u);
2595       case wasm::kExprI16x8GeS:
2596         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_i16x8_ge_s);
2597       case wasm::kExprI16x8GeU:
2598         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_i16x8_ge_u);
2599       case wasm::kExprI32x4Eq:
2600         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_i32x4_eq);
2601       case wasm::kExprI32x4Ne:
2602         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_i32x4_ne);
2603       case wasm::kExprI32x4LtS:
2604         return EmitBinOp<kS128, kS128, true>(
2605             &LiftoffAssembler::emit_i32x4_gt_s);
2606       case wasm::kExprI32x4LtU:
2607         return EmitBinOp<kS128, kS128, true>(
2608             &LiftoffAssembler::emit_i32x4_gt_u);
2609       case wasm::kExprI32x4GtS:
2610         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_i32x4_gt_s);
2611       case wasm::kExprI32x4GtU:
2612         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_i32x4_gt_u);
2613       case wasm::kExprI32x4LeS:
2614         return EmitBinOp<kS128, kS128, true>(
2615             &LiftoffAssembler::emit_i32x4_ge_s);
2616       case wasm::kExprI32x4LeU:
2617         return EmitBinOp<kS128, kS128, true>(
2618             &LiftoffAssembler::emit_i32x4_ge_u);
2619       case wasm::kExprI32x4GeS:
2620         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_i32x4_ge_s);
2621       case wasm::kExprI32x4GeU:
2622         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_i32x4_ge_u);
2623       case wasm::kExprF32x4Eq:
2624         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_f32x4_eq);
2625       case wasm::kExprF32x4Ne:
2626         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_f32x4_ne);
2627       case wasm::kExprF32x4Lt:
2628         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_f32x4_lt);
2629       case wasm::kExprF32x4Gt:
2630         return EmitBinOp<kS128, kS128, true>(&LiftoffAssembler::emit_f32x4_lt);
2631       case wasm::kExprF32x4Le:
2632         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_f32x4_le);
2633       case wasm::kExprF32x4Ge:
2634         return EmitBinOp<kS128, kS128, true>(&LiftoffAssembler::emit_f32x4_le);
2635       case wasm::kExprF64x2Eq:
2636         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_f64x2_eq);
2637       case wasm::kExprF64x2Ne:
2638         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_f64x2_ne);
2639       case wasm::kExprF64x2Lt:
2640         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_f64x2_lt);
2641       case wasm::kExprF64x2Gt:
2642         return EmitBinOp<kS128, kS128, true>(&LiftoffAssembler::emit_f64x2_lt);
2643       case wasm::kExprF64x2Le:
2644         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_f64x2_le);
2645       case wasm::kExprF64x2Ge:
2646         return EmitBinOp<kS128, kS128, true>(&LiftoffAssembler::emit_f64x2_le);
2647       case wasm::kExprS128Not:
2648         return EmitUnOp<kS128, kS128>(&LiftoffAssembler::emit_s128_not);
2649       case wasm::kExprS128And:
2650         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_s128_and);
2651       case wasm::kExprS128Or:
2652         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_s128_or);
2653       case wasm::kExprS128Xor:
2654         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_s128_xor);
2655       case wasm::kExprS128Select:
2656         return EmitTerOp<kS128, kS128>(&LiftoffAssembler::emit_s128_select);
2657       case wasm::kExprI8x16Neg:
2658         return EmitUnOp<kS128, kS128>(&LiftoffAssembler::emit_i8x16_neg);
2659       case wasm::kExprV8x16AnyTrue:
2660         return EmitUnOp<kS128, kI32>(&LiftoffAssembler::emit_v8x16_anytrue);
2661       case wasm::kExprV8x16AllTrue:
2662         return EmitUnOp<kS128, kI32>(&LiftoffAssembler::emit_v8x16_alltrue);
2663       case wasm::kExprI8x16BitMask:
2664         return EmitUnOp<kS128, kI32>(&LiftoffAssembler::emit_i8x16_bitmask);
2665       case wasm::kExprI8x16Shl:
2666         return EmitSimdShiftOp(&LiftoffAssembler::emit_i8x16_shl,
2667                                &LiftoffAssembler::emit_i8x16_shli);
2668       case wasm::kExprI8x16ShrS:
2669         return EmitSimdShiftOp(&LiftoffAssembler::emit_i8x16_shr_s,
2670                                &LiftoffAssembler::emit_i8x16_shri_s);
2671       case wasm::kExprI8x16ShrU:
2672         return EmitSimdShiftOp(&LiftoffAssembler::emit_i8x16_shr_u,
2673                                &LiftoffAssembler::emit_i8x16_shri_u);
2674       case wasm::kExprI8x16Add:
2675         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_i8x16_add);
2676       case wasm::kExprI8x16AddSatS:
2677         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_i8x16_add_sat_s);
2678       case wasm::kExprI8x16AddSatU:
2679         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_i8x16_add_sat_u);
2680       case wasm::kExprI8x16Sub:
2681         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_i8x16_sub);
2682       case wasm::kExprI8x16SubSatS:
2683         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_i8x16_sub_sat_s);
2684       case wasm::kExprI8x16SubSatU:
2685         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_i8x16_sub_sat_u);
2686       case wasm::kExprI8x16Mul:
2687         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_i8x16_mul);
2688       case wasm::kExprI8x16MinS:
2689         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_i8x16_min_s);
2690       case wasm::kExprI8x16MinU:
2691         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_i8x16_min_u);
2692       case wasm::kExprI8x16MaxS:
2693         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_i8x16_max_s);
2694       case wasm::kExprI8x16MaxU:
2695         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_i8x16_max_u);
2696       case wasm::kExprI16x8Neg:
2697         return EmitUnOp<kS128, kS128>(&LiftoffAssembler::emit_i16x8_neg);
2698       case wasm::kExprV16x8AnyTrue:
2699         return EmitUnOp<kS128, kI32>(&LiftoffAssembler::emit_v16x8_anytrue);
2700       case wasm::kExprV16x8AllTrue:
2701         return EmitUnOp<kS128, kI32>(&LiftoffAssembler::emit_v16x8_alltrue);
2702       case wasm::kExprI16x8BitMask:
2703         return EmitUnOp<kS128, kI32>(&LiftoffAssembler::emit_i16x8_bitmask);
2704       case wasm::kExprI16x8Shl:
2705         return EmitSimdShiftOp(&LiftoffAssembler::emit_i16x8_shl,
2706                                &LiftoffAssembler::emit_i16x8_shli);
2707       case wasm::kExprI16x8ShrS:
2708         return EmitSimdShiftOp(&LiftoffAssembler::emit_i16x8_shr_s,
2709                                &LiftoffAssembler::emit_i16x8_shri_s);
2710       case wasm::kExprI16x8ShrU:
2711         return EmitSimdShiftOp(&LiftoffAssembler::emit_i16x8_shr_u,
2712                                &LiftoffAssembler::emit_i16x8_shri_u);
2713       case wasm::kExprI16x8Add:
2714         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_i16x8_add);
2715       case wasm::kExprI16x8AddSatS:
2716         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_i16x8_add_sat_s);
2717       case wasm::kExprI16x8AddSatU:
2718         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_i16x8_add_sat_u);
2719       case wasm::kExprI16x8Sub:
2720         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_i16x8_sub);
2721       case wasm::kExprI16x8SubSatS:
2722         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_i16x8_sub_sat_s);
2723       case wasm::kExprI16x8SubSatU:
2724         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_i16x8_sub_sat_u);
2725       case wasm::kExprI16x8Mul:
2726         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_i16x8_mul);
2727       case wasm::kExprI16x8MinS:
2728         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_i16x8_min_s);
2729       case wasm::kExprI16x8MinU:
2730         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_i16x8_min_u);
2731       case wasm::kExprI16x8MaxS:
2732         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_i16x8_max_s);
2733       case wasm::kExprI16x8MaxU:
2734         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_i16x8_max_u);
2735       case wasm::kExprI32x4Neg:
2736         return EmitUnOp<kS128, kS128>(&LiftoffAssembler::emit_i32x4_neg);
2737       case wasm::kExprV32x4AnyTrue:
2738         return EmitUnOp<kS128, kI32>(&LiftoffAssembler::emit_v32x4_anytrue);
2739       case wasm::kExprV32x4AllTrue:
2740         return EmitUnOp<kS128, kI32>(&LiftoffAssembler::emit_v32x4_alltrue);
2741       case wasm::kExprI32x4BitMask:
2742         return EmitUnOp<kS128, kI32>(&LiftoffAssembler::emit_i32x4_bitmask);
2743       case wasm::kExprI32x4Shl:
2744         return EmitSimdShiftOp(&LiftoffAssembler::emit_i32x4_shl,
2745                                &LiftoffAssembler::emit_i32x4_shli);
2746       case wasm::kExprI32x4ShrS:
2747         return EmitSimdShiftOp(&LiftoffAssembler::emit_i32x4_shr_s,
2748                                &LiftoffAssembler::emit_i32x4_shri_s);
2749       case wasm::kExprI32x4ShrU:
2750         return EmitSimdShiftOp(&LiftoffAssembler::emit_i32x4_shr_u,
2751                                &LiftoffAssembler::emit_i32x4_shri_u);
2752       case wasm::kExprI32x4Add:
2753         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_i32x4_add);
2754       case wasm::kExprI32x4Sub:
2755         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_i32x4_sub);
2756       case wasm::kExprI32x4Mul:
2757         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_i32x4_mul);
2758       case wasm::kExprI32x4MinS:
2759         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_i32x4_min_s);
2760       case wasm::kExprI32x4MinU:
2761         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_i32x4_min_u);
2762       case wasm::kExprI32x4MaxS:
2763         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_i32x4_max_s);
2764       case wasm::kExprI32x4MaxU:
2765         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_i32x4_max_u);
2766       case wasm::kExprI32x4DotI16x8S:
2767         return EmitBinOp<kS128, kS128>(
2768             &LiftoffAssembler::emit_i32x4_dot_i16x8_s);
2769       case wasm::kExprI64x2Neg:
2770         return EmitUnOp<kS128, kS128>(&LiftoffAssembler::emit_i64x2_neg);
2771       case wasm::kExprI64x2Shl:
2772         return EmitSimdShiftOp(&LiftoffAssembler::emit_i64x2_shl,
2773                                &LiftoffAssembler::emit_i64x2_shli);
2774       case wasm::kExprI64x2ShrS:
2775         return EmitSimdShiftOp(&LiftoffAssembler::emit_i64x2_shr_s,
2776                                &LiftoffAssembler::emit_i64x2_shri_s);
2777       case wasm::kExprI64x2ShrU:
2778         return EmitSimdShiftOp(&LiftoffAssembler::emit_i64x2_shr_u,
2779                                &LiftoffAssembler::emit_i64x2_shri_u);
2780       case wasm::kExprI64x2Add:
2781         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_i64x2_add);
2782       case wasm::kExprI64x2Sub:
2783         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_i64x2_sub);
2784       case wasm::kExprI64x2Mul:
2785         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_i64x2_mul);
2786       case wasm::kExprF32x4Abs:
2787         return EmitUnOp<kS128, kS128>(&LiftoffAssembler::emit_f32x4_abs);
2788       case wasm::kExprF32x4Neg:
2789         return EmitUnOp<kS128, kS128>(&LiftoffAssembler::emit_f32x4_neg);
2790       case wasm::kExprF32x4Sqrt:
2791         return EmitUnOp<kS128, kS128>(&LiftoffAssembler::emit_f32x4_sqrt);
2792       case wasm::kExprF32x4Ceil:
2793         return EmitSimdFloatRoundingOpWithCFallback(
2794             &LiftoffAssembler::emit_f32x4_ceil,
2795             &ExternalReference::wasm_f32x4_ceil);
2796       case wasm::kExprF32x4Floor:
2797         return EmitSimdFloatRoundingOpWithCFallback(
2798             &LiftoffAssembler::emit_f32x4_floor,
2799             ExternalReference::wasm_f32x4_floor);
2800       case wasm::kExprF32x4Trunc:
2801         return EmitSimdFloatRoundingOpWithCFallback(
2802             &LiftoffAssembler::emit_f32x4_trunc,
2803             ExternalReference::wasm_f32x4_trunc);
2804       case wasm::kExprF32x4NearestInt:
2805         return EmitSimdFloatRoundingOpWithCFallback(
2806             &LiftoffAssembler::emit_f32x4_nearest_int,
2807             ExternalReference::wasm_f32x4_nearest_int);
2808       case wasm::kExprF32x4Add:
2809         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_f32x4_add);
2810       case wasm::kExprF32x4Sub:
2811         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_f32x4_sub);
2812       case wasm::kExprF32x4Mul:
2813         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_f32x4_mul);
2814       case wasm::kExprF32x4Div:
2815         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_f32x4_div);
2816       case wasm::kExprF32x4Min:
2817         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_f32x4_min);
2818       case wasm::kExprF32x4Max:
2819         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_f32x4_max);
2820       case wasm::kExprF32x4Pmin:
2821         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_f32x4_pmin);
2822       case wasm::kExprF32x4Pmax:
2823         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_f32x4_pmax);
2824       case wasm::kExprF64x2Abs:
2825         return EmitUnOp<kS128, kS128>(&LiftoffAssembler::emit_f64x2_abs);
2826       case wasm::kExprF64x2Neg:
2827         return EmitUnOp<kS128, kS128>(&LiftoffAssembler::emit_f64x2_neg);
2828       case wasm::kExprF64x2Sqrt:
2829         return EmitUnOp<kS128, kS128>(&LiftoffAssembler::emit_f64x2_sqrt);
2830       case wasm::kExprF64x2Ceil:
2831         return EmitSimdFloatRoundingOpWithCFallback(
2832             &LiftoffAssembler::emit_f64x2_ceil,
2833             &ExternalReference::wasm_f64x2_ceil);
2834       case wasm::kExprF64x2Floor:
2835         return EmitSimdFloatRoundingOpWithCFallback(
2836             &LiftoffAssembler::emit_f64x2_floor,
2837             ExternalReference::wasm_f64x2_floor);
2838       case wasm::kExprF64x2Trunc:
2839         return EmitSimdFloatRoundingOpWithCFallback(
2840             &LiftoffAssembler::emit_f64x2_trunc,
2841             ExternalReference::wasm_f64x2_trunc);
2842       case wasm::kExprF64x2NearestInt:
2843         return EmitSimdFloatRoundingOpWithCFallback(
2844             &LiftoffAssembler::emit_f64x2_nearest_int,
2845             ExternalReference::wasm_f64x2_nearest_int);
2846       case wasm::kExprF64x2Add:
2847         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_f64x2_add);
2848       case wasm::kExprF64x2Sub:
2849         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_f64x2_sub);
2850       case wasm::kExprF64x2Mul:
2851         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_f64x2_mul);
2852       case wasm::kExprF64x2Div:
2853         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_f64x2_div);
2854       case wasm::kExprF64x2Min:
2855         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_f64x2_min);
2856       case wasm::kExprF64x2Max:
2857         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_f64x2_max);
2858       case wasm::kExprF64x2Pmin:
2859         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_f64x2_pmin);
2860       case wasm::kExprF64x2Pmax:
2861         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_f64x2_pmax);
2862       case wasm::kExprI32x4SConvertF32x4:
2863         return EmitUnOp<kS128, kS128>(
2864             &LiftoffAssembler::emit_i32x4_sconvert_f32x4);
2865       case wasm::kExprI32x4UConvertF32x4:
2866         return EmitUnOp<kS128, kS128>(
2867             &LiftoffAssembler::emit_i32x4_uconvert_f32x4);
2868       case wasm::kExprF32x4SConvertI32x4:
2869         return EmitUnOp<kS128, kS128>(
2870             &LiftoffAssembler::emit_f32x4_sconvert_i32x4);
2871       case wasm::kExprF32x4UConvertI32x4:
2872         return EmitUnOp<kS128, kS128>(
2873             &LiftoffAssembler::emit_f32x4_uconvert_i32x4);
2874       case wasm::kExprI8x16SConvertI16x8:
2875         return EmitBinOp<kS128, kS128>(
2876             &LiftoffAssembler::emit_i8x16_sconvert_i16x8);
2877       case wasm::kExprI8x16UConvertI16x8:
2878         return EmitBinOp<kS128, kS128>(
2879             &LiftoffAssembler::emit_i8x16_uconvert_i16x8);
2880       case wasm::kExprI16x8SConvertI32x4:
2881         return EmitBinOp<kS128, kS128>(
2882             &LiftoffAssembler::emit_i16x8_sconvert_i32x4);
2883       case wasm::kExprI16x8UConvertI32x4:
2884         return EmitBinOp<kS128, kS128>(
2885             &LiftoffAssembler::emit_i16x8_uconvert_i32x4);
2886       case wasm::kExprI16x8SConvertI8x16Low:
2887         return EmitUnOp<kS128, kS128>(
2888             &LiftoffAssembler::emit_i16x8_sconvert_i8x16_low);
2889       case wasm::kExprI16x8SConvertI8x16High:
2890         return EmitUnOp<kS128, kS128>(
2891             &LiftoffAssembler::emit_i16x8_sconvert_i8x16_high);
2892       case wasm::kExprI16x8UConvertI8x16Low:
2893         return EmitUnOp<kS128, kS128>(
2894             &LiftoffAssembler::emit_i16x8_uconvert_i8x16_low);
2895       case wasm::kExprI16x8UConvertI8x16High:
2896         return EmitUnOp<kS128, kS128>(
2897             &LiftoffAssembler::emit_i16x8_uconvert_i8x16_high);
2898       case wasm::kExprI32x4SConvertI16x8Low:
2899         return EmitUnOp<kS128, kS128>(
2900             &LiftoffAssembler::emit_i32x4_sconvert_i16x8_low);
2901       case wasm::kExprI32x4SConvertI16x8High:
2902         return EmitUnOp<kS128, kS128>(
2903             &LiftoffAssembler::emit_i32x4_sconvert_i16x8_high);
2904       case wasm::kExprI32x4UConvertI16x8Low:
2905         return EmitUnOp<kS128, kS128>(
2906             &LiftoffAssembler::emit_i32x4_uconvert_i16x8_low);
2907       case wasm::kExprI32x4UConvertI16x8High:
2908         return EmitUnOp<kS128, kS128>(
2909             &LiftoffAssembler::emit_i32x4_uconvert_i16x8_high);
2910       case wasm::kExprS128AndNot:
2911         return EmitBinOp<kS128, kS128>(&LiftoffAssembler::emit_s128_and_not);
2912       case wasm::kExprI8x16RoundingAverageU:
2913         return EmitBinOp<kS128, kS128>(
2914             &LiftoffAssembler::emit_i8x16_rounding_average_u);
2915       case wasm::kExprI16x8RoundingAverageU:
2916         return EmitBinOp<kS128, kS128>(
2917             &LiftoffAssembler::emit_i16x8_rounding_average_u);
2918       case wasm::kExprI8x16Abs:
2919         return EmitUnOp<kS128, kS128>(&LiftoffAssembler::emit_i8x16_abs);
2920       case wasm::kExprI16x8Abs:
2921         return EmitUnOp<kS128, kS128>(&LiftoffAssembler::emit_i16x8_abs);
2922       case wasm::kExprI32x4Abs:
2923         return EmitUnOp<kS128, kS128>(&LiftoffAssembler::emit_i32x4_abs);
2924       default:
2925         unsupported(decoder, kSimd, "simd");
2926     }
2927   }
2928 
2929   template <ValueType::Kind src_type, ValueType::Kind result_type,
2930             typename EmitFn>
EmitSimdExtractLaneOp(EmitFn fn,const SimdLaneImmediate<validate> & imm)2931   void EmitSimdExtractLaneOp(EmitFn fn,
2932                              const SimdLaneImmediate<validate>& imm) {
2933     static constexpr RegClass src_rc = reg_class_for(src_type);
2934     static constexpr RegClass result_rc = reg_class_for(result_type);
2935     LiftoffRegister lhs = __ PopToRegister();
2936     LiftoffRegister dst = src_rc == result_rc
2937                               ? __ GetUnusedRegister(result_rc, {lhs}, {})
2938                               : __ GetUnusedRegister(result_rc, {});
2939     fn(dst, lhs, imm.lane);
2940     __ PushRegister(ValueType::Primitive(result_type), dst);
2941   }
2942 
2943   template <ValueType::Kind src2_type, typename EmitFn>
EmitSimdReplaceLaneOp(EmitFn fn,const SimdLaneImmediate<validate> & imm)2944   void EmitSimdReplaceLaneOp(EmitFn fn,
2945                              const SimdLaneImmediate<validate>& imm) {
2946     static constexpr RegClass src1_rc = reg_class_for(kS128);
2947     static constexpr RegClass src2_rc = reg_class_for(src2_type);
2948     static constexpr RegClass result_rc = reg_class_for(kS128);
2949     // On backends which need fp pair, src1_rc and result_rc end up being
2950     // kFpRegPair, which is != kFpReg, but we still want to pin src2 when it is
2951     // kFpReg, since it can overlap with those pairs.
2952     static constexpr bool pin_src2 = kNeedS128RegPair && src2_rc == kFpReg;
2953 
2954     // Does not work for arm
2955     LiftoffRegister src2 = __ PopToRegister();
2956     LiftoffRegister src1 = (src1_rc == src2_rc || pin_src2)
2957                                ? __ PopToRegister(LiftoffRegList::ForRegs(src2))
2958                                : __
2959                                  PopToRegister();
2960     LiftoffRegister dst =
2961         (src2_rc == result_rc || pin_src2)
2962             ? __ GetUnusedRegister(result_rc, {src1},
2963                                    LiftoffRegList::ForRegs(src2))
2964             : __ GetUnusedRegister(result_rc, {src1}, {});
2965     fn(dst, src1, src2, imm.lane);
2966     __ PushRegister(kWasmS128, dst);
2967   }
2968 
SimdLaneOp(FullDecoder * decoder,WasmOpcode opcode,const SimdLaneImmediate<validate> & imm,const Vector<Value> inputs,Value * result)2969   void SimdLaneOp(FullDecoder* decoder, WasmOpcode opcode,
2970                   const SimdLaneImmediate<validate>& imm,
2971                   const Vector<Value> inputs, Value* result) {
2972     if (!CpuFeatures::SupportsWasmSimd128()) {
2973       return unsupported(decoder, kSimd, "simd");
2974     }
2975     switch (opcode) {
2976 #define CASE_SIMD_EXTRACT_LANE_OP(opcode, type, fn)                           \
2977   case wasm::kExpr##opcode:                                                   \
2978     EmitSimdExtractLaneOp<kS128, k##type>(                                    \
2979         [=](LiftoffRegister dst, LiftoffRegister lhs, uint8_t imm_lane_idx) { \
2980           __ emit_##fn(dst, lhs, imm_lane_idx);                               \
2981         },                                                                    \
2982         imm);                                                                 \
2983     break;
2984       CASE_SIMD_EXTRACT_LANE_OP(I8x16ExtractLaneS, I32, i8x16_extract_lane_s)
2985       CASE_SIMD_EXTRACT_LANE_OP(I8x16ExtractLaneU, I32, i8x16_extract_lane_u)
2986       CASE_SIMD_EXTRACT_LANE_OP(I16x8ExtractLaneS, I32, i16x8_extract_lane_s)
2987       CASE_SIMD_EXTRACT_LANE_OP(I16x8ExtractLaneU, I32, i16x8_extract_lane_u)
2988       CASE_SIMD_EXTRACT_LANE_OP(I32x4ExtractLane, I32, i32x4_extract_lane)
2989       CASE_SIMD_EXTRACT_LANE_OP(I64x2ExtractLane, I64, i64x2_extract_lane)
2990       CASE_SIMD_EXTRACT_LANE_OP(F32x4ExtractLane, F32, f32x4_extract_lane)
2991       CASE_SIMD_EXTRACT_LANE_OP(F64x2ExtractLane, F64, f64x2_extract_lane)
2992 #undef CASE_SIMD_EXTRACT_LANE_OP
2993 #define CASE_SIMD_REPLACE_LANE_OP(opcode, type, fn)                          \
2994   case wasm::kExpr##opcode:                                                  \
2995     EmitSimdReplaceLaneOp<k##type>(                                          \
2996         [=](LiftoffRegister dst, LiftoffRegister src1, LiftoffRegister src2, \
2997             uint8_t imm_lane_idx) {                                          \
2998           __ emit_##fn(dst, src1, src2, imm_lane_idx);                       \
2999         },                                                                   \
3000         imm);                                                                \
3001     break;
3002       CASE_SIMD_REPLACE_LANE_OP(I8x16ReplaceLane, I32, i8x16_replace_lane)
3003       CASE_SIMD_REPLACE_LANE_OP(I16x8ReplaceLane, I32, i16x8_replace_lane)
3004       CASE_SIMD_REPLACE_LANE_OP(I32x4ReplaceLane, I32, i32x4_replace_lane)
3005       CASE_SIMD_REPLACE_LANE_OP(I64x2ReplaceLane, I64, i64x2_replace_lane)
3006       CASE_SIMD_REPLACE_LANE_OP(F32x4ReplaceLane, F32, f32x4_replace_lane)
3007       CASE_SIMD_REPLACE_LANE_OP(F64x2ReplaceLane, F64, f64x2_replace_lane)
3008 #undef CASE_SIMD_REPLACE_LANE_OP
3009       default:
3010         unsupported(decoder, kSimd, "simd");
3011     }
3012   }
3013 
S128Const(FullDecoder * decoder,const Simd128Immediate<validate> & imm,Value * result)3014   void S128Const(FullDecoder* decoder, const Simd128Immediate<validate>& imm,
3015                  Value* result) {
3016     if (!CpuFeatures::SupportsWasmSimd128()) {
3017       return unsupported(decoder, kSimd, "simd");
3018     }
3019     constexpr RegClass result_rc = reg_class_for(ValueType::kS128);
3020     LiftoffRegister dst = __ GetUnusedRegister(result_rc, {});
3021     bool all_zeroes = std::all_of(std::begin(imm.value), std::end(imm.value),
3022                                   [](uint8_t v) { return v == 0; });
3023     bool all_ones = std::all_of(std::begin(imm.value), std::end(imm.value),
3024                                 [](uint8_t v) { return v == 0xff; });
3025     if (all_zeroes) {
3026       __ LiftoffAssembler::emit_s128_xor(dst, dst, dst);
3027     } else if (all_ones) {
3028       // Any SIMD eq will work, i32x4 is efficient on all archs.
3029       __ LiftoffAssembler::emit_i32x4_eq(dst, dst, dst);
3030     } else {
3031       __ LiftoffAssembler::emit_s128_const(dst, imm.value);
3032     }
3033     __ PushRegister(kWasmS128, dst);
3034   }
3035 
Simd8x16ShuffleOp(FullDecoder * decoder,const Simd128Immediate<validate> & imm,const Value & input0,const Value & input1,Value * result)3036   void Simd8x16ShuffleOp(FullDecoder* decoder,
3037                          const Simd128Immediate<validate>& imm,
3038                          const Value& input0, const Value& input1,
3039                          Value* result) {
3040     if (!CpuFeatures::SupportsWasmSimd128()) {
3041       return unsupported(decoder, kSimd, "simd");
3042     }
3043     static constexpr RegClass result_rc = reg_class_for(ValueType::kS128);
3044     LiftoffRegister rhs = __ PopToRegister();
3045     LiftoffRegister lhs = __ PopToRegister(LiftoffRegList::ForRegs(rhs));
3046     LiftoffRegister dst = __ GetUnusedRegister(result_rc, {lhs, rhs}, {});
3047 
3048     uint8_t shuffle[kSimd128Size];
3049     memcpy(shuffle, imm.value, sizeof(shuffle));
3050     bool is_swizzle;
3051     bool needs_swap;
3052     wasm::SimdShuffle::CanonicalizeShuffle(lhs == rhs, shuffle, &needs_swap,
3053                                            &is_swizzle);
3054     if (needs_swap) {
3055       std::swap(lhs, rhs);
3056     }
3057     __ LiftoffAssembler::emit_i8x16_shuffle(dst, lhs, rhs, shuffle, is_swizzle);
3058     __ PushRegister(kWasmS128, dst);
3059   }
3060 
Throw(FullDecoder * decoder,const ExceptionIndexImmediate<validate> &,const Vector<Value> & args)3061   void Throw(FullDecoder* decoder, const ExceptionIndexImmediate<validate>&,
3062              const Vector<Value>& args) {
3063     unsupported(decoder, kExceptionHandling, "throw");
3064   }
Rethrow(FullDecoder * decoder,const Value & exception)3065   void Rethrow(FullDecoder* decoder, const Value& exception) {
3066     unsupported(decoder, kExceptionHandling, "rethrow");
3067   }
BrOnException(FullDecoder * decoder,const Value & exception,const ExceptionIndexImmediate<validate> & imm,uint32_t depth,Vector<Value> values)3068   void BrOnException(FullDecoder* decoder, const Value& exception,
3069                      const ExceptionIndexImmediate<validate>& imm,
3070                      uint32_t depth, Vector<Value> values) {
3071     unsupported(decoder, kExceptionHandling, "br_on_exn");
3072   }
3073 
AtomicStoreMem(FullDecoder * decoder,StoreType type,const MemoryAccessImmediate<validate> & imm)3074   void AtomicStoreMem(FullDecoder* decoder, StoreType type,
3075                       const MemoryAccessImmediate<validate>& imm) {
3076     LiftoffRegList pinned;
3077     LiftoffRegister value = pinned.set(__ PopToRegister());
3078     Register index = pinned.set(__ PopToRegister(pinned)).gp();
3079     if (BoundsCheckMem(decoder, type.size(), imm.offset, index, pinned,
3080                        kDoForceCheck)) {
3081       return;
3082     }
3083     AlignmentCheckMem(decoder, type.size(), imm.offset, index, pinned);
3084     uint32_t offset = imm.offset;
3085     index = AddMemoryMasking(index, &offset, &pinned);
3086     DEBUG_CODE_COMMENT("atomic store to memory");
3087     Register addr = pinned.set(__ GetUnusedRegister(kGpReg, pinned)).gp();
3088     LOAD_INSTANCE_FIELD(addr, MemoryStart, kSystemPointerSize);
3089     LiftoffRegList outer_pinned;
3090     if (FLAG_trace_wasm_memory) outer_pinned.set(index);
3091     __ AtomicStore(addr, index, offset, value, type, outer_pinned);
3092     if (FLAG_trace_wasm_memory) {
3093       TraceMemoryOperation(true, type.mem_rep(), index, offset,
3094                            decoder->position());
3095     }
3096   }
3097 
AtomicLoadMem(FullDecoder * decoder,LoadType type,const MemoryAccessImmediate<validate> & imm)3098   void AtomicLoadMem(FullDecoder* decoder, LoadType type,
3099                      const MemoryAccessImmediate<validate>& imm) {
3100     ValueType value_type = type.value_type();
3101     LiftoffRegList pinned;
3102     Register index = pinned.set(__ PopToRegister()).gp();
3103     if (BoundsCheckMem(decoder, type.size(), imm.offset, index, pinned,
3104                        kDoForceCheck)) {
3105       return;
3106     }
3107     AlignmentCheckMem(decoder, type.size(), imm.offset, index, pinned);
3108     uint32_t offset = imm.offset;
3109     index = AddMemoryMasking(index, &offset, &pinned);
3110     DEBUG_CODE_COMMENT("atomic load from memory");
3111     Register addr = pinned.set(__ GetUnusedRegister(kGpReg, pinned)).gp();
3112     LOAD_INSTANCE_FIELD(addr, MemoryStart, kSystemPointerSize);
3113     RegClass rc = reg_class_for(value_type);
3114     LiftoffRegister value = pinned.set(__ GetUnusedRegister(rc, pinned));
3115     __ AtomicLoad(value, addr, index, offset, type, pinned);
3116     __ PushRegister(value_type, value);
3117 
3118     if (FLAG_trace_wasm_memory) {
3119       TraceMemoryOperation(false, type.mem_type().representation(), index,
3120                            offset, decoder->position());
3121     }
3122   }
3123 
AtomicBinop(FullDecoder * decoder,StoreType type,const MemoryAccessImmediate<validate> & imm,void (LiftoffAssembler::* emit_fn)(Register,Register,uint32_t,LiftoffRegister,LiftoffRegister,StoreType))3124   void AtomicBinop(FullDecoder* decoder, StoreType type,
3125                    const MemoryAccessImmediate<validate>& imm,
3126                    void (LiftoffAssembler::*emit_fn)(Register, Register,
3127                                                      uint32_t, LiftoffRegister,
3128                                                      LiftoffRegister,
3129                                                      StoreType)) {
3130     ValueType result_type = type.value_type();
3131     LiftoffRegList pinned;
3132     LiftoffRegister value = pinned.set(__ PopToRegister());
3133 #ifdef V8_TARGET_ARCH_IA32
3134     // We have to reuse the value register as the result register so that we
3135     //  don't run out of registers on ia32. For this we use the value register
3136     //  as the result register if it has no other uses. Otherwise  we allocate
3137     //  a new register and let go of the value register to get spilled.
3138     LiftoffRegister result = value;
3139     if (__ cache_state()->is_used(value)) {
3140       result = pinned.set(__ GetUnusedRegister(value.reg_class(), pinned));
3141       __ Move(result, value, result_type);
3142       pinned.clear(value);
3143       value = result;
3144     }
3145 #else
3146     LiftoffRegister result =
3147         pinned.set(__ GetUnusedRegister(value.reg_class(), pinned));
3148 #endif
3149     Register index = pinned.set(__ PopToRegister(pinned)).gp();
3150     if (BoundsCheckMem(decoder, type.size(), imm.offset, index, pinned,
3151                        kDoForceCheck)) {
3152       return;
3153     }
3154     AlignmentCheckMem(decoder, type.size(), imm.offset, index, pinned);
3155 
3156     uint32_t offset = imm.offset;
3157     index = AddMemoryMasking(index, &offset, &pinned);
3158     Register addr = pinned.set(__ GetUnusedRegister(kGpReg, pinned)).gp();
3159     LOAD_INSTANCE_FIELD(addr, MemoryStart, kSystemPointerSize);
3160 
3161     (asm_.*emit_fn)(addr, index, offset, value, result, type);
3162     __ PushRegister(result_type, result);
3163   }
3164 
AtomicCompareExchange(FullDecoder * decoder,StoreType type,const MemoryAccessImmediate<validate> & imm)3165   void AtomicCompareExchange(FullDecoder* decoder, StoreType type,
3166                              const MemoryAccessImmediate<validate>& imm) {
3167 #ifdef V8_TARGET_ARCH_IA32
3168     // On ia32 we don't have enough registers to first pop all the values off
3169     // the stack and then start with the code generation. Instead we do the
3170     // complete address calculation first, so that the address only needs a
3171     // single register. Afterwards we load all remaining values into the
3172     // other registers.
3173     LiftoffRegList pinned;
3174     Register index_reg = pinned.set(__ PeekToRegister(2, pinned)).gp();
3175     if (BoundsCheckMem(decoder, type.size(), imm.offset, index_reg, pinned,
3176                        kDoForceCheck)) {
3177       return;
3178     }
3179     AlignmentCheckMem(decoder, type.size(), imm.offset, index_reg, pinned);
3180 
3181     uint32_t offset = imm.offset;
3182     index_reg = AddMemoryMasking(index_reg, &offset, &pinned);
3183     Register addr = pinned.set(__ GetUnusedRegister(kGpReg, pinned)).gp();
3184     LOAD_INSTANCE_FIELD(addr, MemoryStart, kSystemPointerSize);
3185     __ emit_i32_add(addr, addr, index_reg);
3186     pinned.clear(LiftoffRegister(index_reg));
3187     LiftoffRegister new_value = pinned.set(__ PopToRegister(pinned));
3188     LiftoffRegister expected = pinned.set(__ PopToRegister(pinned));
3189 
3190     // Pop the index from the stack.
3191     __ cache_state()->stack_state.pop_back(1);
3192 
3193     LiftoffRegister result = expected;
3194 
3195     // We already added the index to addr, so we can just pass no_reg to the
3196     // assembler now.
3197     __ AtomicCompareExchange(addr, no_reg, offset, expected, new_value, result,
3198                              type);
3199     __ PushRegister(type.value_type(), result);
3200     return;
3201 #else
3202     ValueType result_type = type.value_type();
3203     LiftoffRegList pinned;
3204     LiftoffRegister new_value = pinned.set(__ PopToRegister());
3205     LiftoffRegister expected = pinned.set(__ PopToRegister(pinned));
3206     Register index = pinned.set(__ PopToRegister(pinned)).gp();
3207     if (BoundsCheckMem(decoder, type.size(), imm.offset, index, pinned,
3208                        kDoForceCheck)) {
3209       return;
3210     }
3211     AlignmentCheckMem(decoder, type.size(), imm.offset, index, pinned);
3212 
3213     uint32_t offset = imm.offset;
3214     index = AddMemoryMasking(index, &offset, &pinned);
3215     Register addr = pinned.set(__ GetUnusedRegister(kGpReg, pinned)).gp();
3216     LOAD_INSTANCE_FIELD(addr, MemoryStart, kSystemPointerSize);
3217     LiftoffRegister result =
3218         pinned.set(__ GetUnusedRegister(reg_class_for(result_type), pinned));
3219 
3220     __ AtomicCompareExchange(addr, index, offset, expected, new_value, result,
3221                              type);
3222     __ PushRegister(result_type, result);
3223 #endif
3224   }
3225 
3226   template <typename BuiltinDescriptor>
GetBuiltinCallDescriptor(Zone * zone)3227   compiler::CallDescriptor* GetBuiltinCallDescriptor(Zone* zone) {
3228     BuiltinDescriptor interface_descriptor;
3229     return compiler::Linkage::GetStubCallDescriptor(
3230         zone,                                           // zone
3231         interface_descriptor,                           // descriptor
3232         interface_descriptor.GetStackParameterCount(),  // stack parameter count
3233         compiler::CallDescriptor::kNoFlags,             // flags
3234         compiler::Operator::kNoProperties,              // properties
3235         StubCallMode::kCallWasmRuntimeStub);            // stub call mode
3236   }
3237 
AtomicWait(FullDecoder * decoder,ValueType type,const MemoryAccessImmediate<validate> & imm)3238   void AtomicWait(FullDecoder* decoder, ValueType type,
3239                   const MemoryAccessImmediate<validate>& imm) {
3240     LiftoffRegList pinned;
3241     Register index_reg = pinned.set(__ PeekToRegister(2, pinned)).gp();
3242     if (BoundsCheckMem(decoder, type.element_size_bytes(), imm.offset,
3243                        index_reg, pinned, kDoForceCheck)) {
3244       return;
3245     }
3246     AlignmentCheckMem(decoder, type.element_size_bytes(), imm.offset, index_reg,
3247                       pinned);
3248 
3249     uint32_t offset = imm.offset;
3250     index_reg = AddMemoryMasking(index_reg, &offset, &pinned);
3251     Register index_plus_offset =
3252         __ cache_state()->is_used(LiftoffRegister(index_reg))
3253             ? pinned.set(__ GetUnusedRegister(kGpReg, pinned)).gp()
3254             : index_reg;
3255     if (offset) {
3256       __ emit_i32_addi(index_plus_offset, index_reg, offset);
3257       __ emit_ptrsize_zeroextend_i32(index_plus_offset, index_plus_offset);
3258     } else {
3259       __ emit_ptrsize_zeroextend_i32(index_plus_offset, index_reg);
3260     }
3261 
3262     LiftoffAssembler::VarState timeout =
3263         __ cache_state()->stack_state.end()[-1];
3264     LiftoffAssembler::VarState expected_value =
3265         __ cache_state()->stack_state.end()[-2];
3266     LiftoffAssembler::VarState index = __ cache_state()->stack_state.end()[-3];
3267 
3268     // We have to set the correct register for the index. It may have changed
3269     // above in {AddMemoryMasking}.
3270     index.MakeRegister(LiftoffRegister(index_plus_offset));
3271 
3272     WasmCode::RuntimeStubId target;
3273     compiler::CallDescriptor* call_descriptor;
3274     if (type == kWasmI32) {
3275       if (kNeedI64RegPair) {
3276         target = WasmCode::kWasmI32AtomicWait32;
3277         call_descriptor =
3278             GetBuiltinCallDescriptor<WasmI32AtomicWait32Descriptor>(
3279                 compilation_zone_);
3280       } else {
3281         target = WasmCode::kWasmI32AtomicWait64;
3282         call_descriptor =
3283             GetBuiltinCallDescriptor<WasmI32AtomicWait64Descriptor>(
3284                 compilation_zone_);
3285       }
3286     } else {
3287       if (kNeedI64RegPair) {
3288         target = WasmCode::kWasmI64AtomicWait32;
3289         call_descriptor =
3290             GetBuiltinCallDescriptor<WasmI64AtomicWait32Descriptor>(
3291                 compilation_zone_);
3292       } else {
3293         target = WasmCode::kWasmI64AtomicWait64;
3294         call_descriptor =
3295             GetBuiltinCallDescriptor<WasmI64AtomicWait64Descriptor>(
3296                 compilation_zone_);
3297       }
3298     }
3299 
3300     ValueType sig_reps[] = {kPointerValueType, type, kWasmI64};
3301     FunctionSig sig(0, 3, sig_reps);
3302 
3303     __ PrepareBuiltinCall(&sig, call_descriptor,
3304                           {index, expected_value, timeout});
3305     __ CallRuntimeStub(target);
3306     DefineSafepoint();
3307     // Pop parameters from the value stack.
3308     __ cache_state()->stack_state.pop_back(3);
3309 
3310     RegisterDebugSideTableEntry(DebugSideTableBuilder::kDidSpill);
3311 
3312     __ PushRegister(kWasmI32, LiftoffRegister(kReturnRegister0));
3313   }
3314 
AtomicNotify(FullDecoder * decoder,const MemoryAccessImmediate<validate> & imm)3315   void AtomicNotify(FullDecoder* decoder,
3316                     const MemoryAccessImmediate<validate>& imm) {
3317     LiftoffRegList pinned;
3318     Register index_reg = pinned.set(__ PeekToRegister(1, pinned)).gp();
3319     if (BoundsCheckMem(decoder, kWasmI32.element_size_bytes(), imm.offset,
3320                        index_reg, pinned, kDoForceCheck)) {
3321       return;
3322     }
3323     AlignmentCheckMem(decoder, kWasmI32.element_size_bytes(), imm.offset,
3324                       index_reg, pinned);
3325 
3326     uint32_t offset = imm.offset;
3327     index_reg = AddMemoryMasking(index_reg, &offset, &pinned);
3328     Register index_plus_offset =
3329         __ cache_state()->is_used(LiftoffRegister(index_reg))
3330             ? pinned.set(__ GetUnusedRegister(kGpReg, pinned)).gp()
3331             : index_reg;
3332     if (offset) {
3333       __ emit_i32_addi(index_plus_offset, index_reg, offset);
3334       __ emit_ptrsize_zeroextend_i32(index_plus_offset, index_plus_offset);
3335     } else {
3336       __ emit_ptrsize_zeroextend_i32(index_plus_offset, index_reg);
3337     }
3338 
3339     ValueType sig_reps[] = {kWasmI32, kPointerValueType, kWasmI32};
3340     FunctionSig sig(1, 2, sig_reps);
3341     auto call_descriptor =
3342         GetBuiltinCallDescriptor<WasmAtomicNotifyDescriptor>(compilation_zone_);
3343 
3344     LiftoffAssembler::VarState count = __ cache_state()->stack_state.end()[-1];
3345     LiftoffAssembler::VarState index = __ cache_state()->stack_state.end()[-2];
3346     index.MakeRegister(LiftoffRegister(index_plus_offset));
3347 
3348     __ PrepareBuiltinCall(&sig, call_descriptor, {index, count});
3349     __ CallRuntimeStub(WasmCode::kWasmAtomicNotify);
3350     DefineSafepoint();
3351     // Pop parameters from the value stack.
3352     __ cache_state()->stack_state.pop_back(2);
3353 
3354     RegisterDebugSideTableEntry(DebugSideTableBuilder::kDidSpill);
3355 
3356     __ PushRegister(kWasmI32, LiftoffRegister(kReturnRegister0));
3357   }
3358 
3359 #define ATOMIC_STORE_LIST(V)        \
3360   V(I32AtomicStore, kI32Store)      \
3361   V(I64AtomicStore, kI64Store)      \
3362   V(I32AtomicStore8U, kI32Store8)   \
3363   V(I32AtomicStore16U, kI32Store16) \
3364   V(I64AtomicStore8U, kI64Store8)   \
3365   V(I64AtomicStore16U, kI64Store16) \
3366   V(I64AtomicStore32U, kI64Store32)
3367 
3368 #define ATOMIC_LOAD_LIST(V)        \
3369   V(I32AtomicLoad, kI32Load)       \
3370   V(I64AtomicLoad, kI64Load)       \
3371   V(I32AtomicLoad8U, kI32Load8U)   \
3372   V(I32AtomicLoad16U, kI32Load16U) \
3373   V(I64AtomicLoad8U, kI64Load8U)   \
3374   V(I64AtomicLoad16U, kI64Load16U) \
3375   V(I64AtomicLoad32U, kI64Load32U)
3376 
3377 #define ATOMIC_BINOP_INSTRUCTION_LIST(V)         \
3378   V(Add, I32AtomicAdd, kI32Store)                \
3379   V(Add, I64AtomicAdd, kI64Store)                \
3380   V(Add, I32AtomicAdd8U, kI32Store8)             \
3381   V(Add, I32AtomicAdd16U, kI32Store16)           \
3382   V(Add, I64AtomicAdd8U, kI64Store8)             \
3383   V(Add, I64AtomicAdd16U, kI64Store16)           \
3384   V(Add, I64AtomicAdd32U, kI64Store32)           \
3385   V(Sub, I32AtomicSub, kI32Store)                \
3386   V(Sub, I64AtomicSub, kI64Store)                \
3387   V(Sub, I32AtomicSub8U, kI32Store8)             \
3388   V(Sub, I32AtomicSub16U, kI32Store16)           \
3389   V(Sub, I64AtomicSub8U, kI64Store8)             \
3390   V(Sub, I64AtomicSub16U, kI64Store16)           \
3391   V(Sub, I64AtomicSub32U, kI64Store32)           \
3392   V(And, I32AtomicAnd, kI32Store)                \
3393   V(And, I64AtomicAnd, kI64Store)                \
3394   V(And, I32AtomicAnd8U, kI32Store8)             \
3395   V(And, I32AtomicAnd16U, kI32Store16)           \
3396   V(And, I64AtomicAnd8U, kI64Store8)             \
3397   V(And, I64AtomicAnd16U, kI64Store16)           \
3398   V(And, I64AtomicAnd32U, kI64Store32)           \
3399   V(Or, I32AtomicOr, kI32Store)                  \
3400   V(Or, I64AtomicOr, kI64Store)                  \
3401   V(Or, I32AtomicOr8U, kI32Store8)               \
3402   V(Or, I32AtomicOr16U, kI32Store16)             \
3403   V(Or, I64AtomicOr8U, kI64Store8)               \
3404   V(Or, I64AtomicOr16U, kI64Store16)             \
3405   V(Or, I64AtomicOr32U, kI64Store32)             \
3406   V(Xor, I32AtomicXor, kI32Store)                \
3407   V(Xor, I64AtomicXor, kI64Store)                \
3408   V(Xor, I32AtomicXor8U, kI32Store8)             \
3409   V(Xor, I32AtomicXor16U, kI32Store16)           \
3410   V(Xor, I64AtomicXor8U, kI64Store8)             \
3411   V(Xor, I64AtomicXor16U, kI64Store16)           \
3412   V(Xor, I64AtomicXor32U, kI64Store32)           \
3413   V(Exchange, I32AtomicExchange, kI32Store)      \
3414   V(Exchange, I64AtomicExchange, kI64Store)      \
3415   V(Exchange, I32AtomicExchange8U, kI32Store8)   \
3416   V(Exchange, I32AtomicExchange16U, kI32Store16) \
3417   V(Exchange, I64AtomicExchange8U, kI64Store8)   \
3418   V(Exchange, I64AtomicExchange16U, kI64Store16) \
3419   V(Exchange, I64AtomicExchange32U, kI64Store32)
3420 
3421 #define ATOMIC_COMPARE_EXCHANGE_LIST(V)       \
3422   V(I32AtomicCompareExchange, kI32Store)      \
3423   V(I64AtomicCompareExchange, kI64Store)      \
3424   V(I32AtomicCompareExchange8U, kI32Store8)   \
3425   V(I32AtomicCompareExchange16U, kI32Store16) \
3426   V(I64AtomicCompareExchange8U, kI64Store8)   \
3427   V(I64AtomicCompareExchange16U, kI64Store16) \
3428   V(I64AtomicCompareExchange32U, kI64Store32)
3429 
AtomicOp(FullDecoder * decoder,WasmOpcode opcode,Vector<Value> args,const MemoryAccessImmediate<validate> & imm,Value * result)3430   void AtomicOp(FullDecoder* decoder, WasmOpcode opcode, Vector<Value> args,
3431                 const MemoryAccessImmediate<validate>& imm, Value* result) {
3432     switch (opcode) {
3433 #define ATOMIC_STORE_OP(name, type)                \
3434   case wasm::kExpr##name:                          \
3435     AtomicStoreMem(decoder, StoreType::type, imm); \
3436     break;
3437 
3438       ATOMIC_STORE_LIST(ATOMIC_STORE_OP)
3439 #undef ATOMIC_STORE_OP
3440 
3441 #define ATOMIC_LOAD_OP(name, type)               \
3442   case wasm::kExpr##name:                        \
3443     AtomicLoadMem(decoder, LoadType::type, imm); \
3444     break;
3445 
3446       ATOMIC_LOAD_LIST(ATOMIC_LOAD_OP)
3447 #undef ATOMIC_LOAD_OP
3448 
3449 #define ATOMIC_BINOP_OP(op, name, type)                                        \
3450   case wasm::kExpr##name:                                                      \
3451     AtomicBinop(decoder, StoreType::type, imm, &LiftoffAssembler::Atomic##op); \
3452     break;
3453 
3454       ATOMIC_BINOP_INSTRUCTION_LIST(ATOMIC_BINOP_OP)
3455 #undef ATOMIC_BINOP_OP
3456 
3457 #define ATOMIC_COMPARE_EXCHANGE_OP(name, type)            \
3458   case wasm::kExpr##name:                                 \
3459     AtomicCompareExchange(decoder, StoreType::type, imm); \
3460     break;
3461 
3462       ATOMIC_COMPARE_EXCHANGE_LIST(ATOMIC_COMPARE_EXCHANGE_OP)
3463 #undef ATOMIC_COMPARE_EXCHANGE_OP
3464 
3465       case kExprI32AtomicWait:
3466         AtomicWait(decoder, kWasmI32, imm);
3467         break;
3468       case kExprI64AtomicWait:
3469         AtomicWait(decoder, kWasmI64, imm);
3470         break;
3471       case kExprAtomicNotify:
3472         AtomicNotify(decoder, imm);
3473         break;
3474       default:
3475         unsupported(decoder, kAtomics, "atomicop");
3476     }
3477   }
3478 
3479 #undef ATOMIC_STORE_LIST
3480 #undef ATOMIC_LOAD_LIST
3481 #undef ATOMIC_BINOP_INSTRUCTION_LIST
3482 #undef ATOMIC_COMPARE_EXCHANGE_LIST
3483 
AtomicFence(FullDecoder * decoder)3484   void AtomicFence(FullDecoder* decoder) { __ AtomicFence(); }
3485 
MemoryInit(FullDecoder * decoder,const MemoryInitImmediate<validate> & imm,const Value &,const Value &,const Value &)3486   void MemoryInit(FullDecoder* decoder,
3487                   const MemoryInitImmediate<validate>& imm, const Value&,
3488                   const Value&, const Value&) {
3489     LiftoffRegList pinned;
3490     LiftoffRegister size = pinned.set(__ PopToRegister());
3491     LiftoffRegister src = pinned.set(__ PopToRegister(pinned));
3492     LiftoffRegister dst = pinned.set(__ PopToRegister(pinned));
3493 
3494     Register instance = pinned.set(__ GetUnusedRegister(kGpReg, pinned)).gp();
3495     __ FillInstanceInto(instance);
3496 
3497     LiftoffRegister segment_index =
3498         pinned.set(__ GetUnusedRegister(kGpReg, pinned));
3499     __ LoadConstant(segment_index, WasmValue(imm.data_segment_index));
3500 
3501     ExternalReference ext_ref = ExternalReference::wasm_memory_init();
3502     ValueType sig_reps[] = {kWasmI32, kPointerValueType, kWasmI32,
3503                             kWasmI32, kWasmI32,          kWasmI32};
3504     FunctionSig sig(1, 5, sig_reps);
3505     LiftoffRegister args[] = {LiftoffRegister(instance), dst, src,
3506                               segment_index, size};
3507     // We don't need the instance anymore after the call. We can use the
3508     // register for the result.
3509     LiftoffRegister result(instance);
3510     GenerateCCall(&result, &sig, kWasmStmt, args, ext_ref);
3511     Label* trap_label = AddOutOfLineTrap(
3512         decoder->position(), WasmCode::kThrowWasmTrapMemOutOfBounds);
3513     __ emit_cond_jump(kEqual, trap_label, kWasmI32, result.gp());
3514   }
3515 
DataDrop(FullDecoder * decoder,const DataDropImmediate<validate> & imm)3516   void DataDrop(FullDecoder* decoder, const DataDropImmediate<validate>& imm) {
3517     LiftoffRegList pinned;
3518 
3519     Register seg_size_array =
3520         pinned.set(__ GetUnusedRegister(kGpReg, pinned)).gp();
3521     LOAD_INSTANCE_FIELD(seg_size_array, DataSegmentSizes, kSystemPointerSize);
3522 
3523     LiftoffRegister seg_index =
3524         pinned.set(__ GetUnusedRegister(kGpReg, pinned));
3525     // Scale the seg_index for the array access.
3526     __ LoadConstant(seg_index,
3527                     WasmValue(imm.index << kWasmI32.element_size_log2()));
3528 
3529     // Set the length of the segment to '0' to drop it.
3530     LiftoffRegister null_reg = pinned.set(__ GetUnusedRegister(kGpReg, pinned));
3531     __ LoadConstant(null_reg, WasmValue(0));
3532     __ Store(seg_size_array, seg_index.gp(), 0, null_reg, StoreType::kI32Store,
3533              pinned);
3534   }
3535 
MemoryCopy(FullDecoder * decoder,const MemoryCopyImmediate<validate> & imm,const Value &,const Value &,const Value &)3536   void MemoryCopy(FullDecoder* decoder,
3537                   const MemoryCopyImmediate<validate>& imm, const Value&,
3538                   const Value&, const Value&) {
3539     LiftoffRegList pinned;
3540     LiftoffRegister size = pinned.set(__ PopToRegister());
3541     LiftoffRegister src = pinned.set(__ PopToRegister(pinned));
3542     LiftoffRegister dst = pinned.set(__ PopToRegister(pinned));
3543     Register instance = pinned.set(__ GetUnusedRegister(kGpReg, pinned)).gp();
3544     __ FillInstanceInto(instance);
3545     ExternalReference ext_ref = ExternalReference::wasm_memory_copy();
3546     ValueType sig_reps[] = {kWasmI32, kPointerValueType, kWasmI32, kWasmI32,
3547                             kWasmI32};
3548     FunctionSig sig(1, 4, sig_reps);
3549     LiftoffRegister args[] = {LiftoffRegister(instance), dst, src, size};
3550     // We don't need the instance anymore after the call. We can use the
3551     // register for the result.
3552     LiftoffRegister result(instance);
3553     GenerateCCall(&result, &sig, kWasmStmt, args, ext_ref);
3554     Label* trap_label = AddOutOfLineTrap(
3555         decoder->position(), WasmCode::kThrowWasmTrapMemOutOfBounds);
3556     __ emit_cond_jump(kEqual, trap_label, kWasmI32, result.gp());
3557   }
3558 
MemoryFill(FullDecoder * decoder,const MemoryIndexImmediate<validate> & imm,const Value &,const Value &,const Value &)3559   void MemoryFill(FullDecoder* decoder,
3560                   const MemoryIndexImmediate<validate>& imm, const Value&,
3561                   const Value&, const Value&) {
3562     LiftoffRegList pinned;
3563     LiftoffRegister size = pinned.set(__ PopToRegister());
3564     LiftoffRegister value = pinned.set(__ PopToRegister(pinned));
3565     LiftoffRegister dst = pinned.set(__ PopToRegister(pinned));
3566     Register instance = pinned.set(__ GetUnusedRegister(kGpReg, pinned)).gp();
3567     __ FillInstanceInto(instance);
3568     ExternalReference ext_ref = ExternalReference::wasm_memory_fill();
3569     ValueType sig_reps[] = {kWasmI32, kPointerValueType, kWasmI32, kWasmI32,
3570                             kWasmI32};
3571     FunctionSig sig(1, 4, sig_reps);
3572     LiftoffRegister args[] = {LiftoffRegister(instance), dst, value, size};
3573     // We don't need the instance anymore after the call. We can use the
3574     // register for the result.
3575     LiftoffRegister result(instance);
3576     GenerateCCall(&result, &sig, kWasmStmt, args, ext_ref);
3577     Label* trap_label = AddOutOfLineTrap(
3578         decoder->position(), WasmCode::kThrowWasmTrapMemOutOfBounds);
3579     __ emit_cond_jump(kEqual, trap_label, kWasmI32, result.gp());
3580   }
3581 
TableInit(FullDecoder * decoder,const TableInitImmediate<validate> & imm,Vector<Value> args)3582   void TableInit(FullDecoder* decoder, const TableInitImmediate<validate>& imm,
3583                  Vector<Value> args) {
3584     LiftoffRegList pinned;
3585     LiftoffRegister table_index_reg =
3586         pinned.set(__ GetUnusedRegister(kGpReg, pinned));
3587 
3588 #if V8_TARGET_ARCH_32_BIT || defined(V8_COMPRESS_POINTERS)
3589     WasmValue table_index_val(
3590         static_cast<uint32_t>(Smi::FromInt(imm.table.index).ptr()));
3591     WasmValue segment_index_val(
3592         static_cast<uint32_t>(Smi::FromInt(imm.elem_segment_index).ptr()));
3593 #else
3594     WasmValue table_index_val(
3595         static_cast<uint64_t>(Smi::FromInt(imm.table.index).ptr()));
3596     WasmValue segment_index_val(
3597         static_cast<uint64_t>(Smi::FromInt(imm.elem_segment_index).ptr()));
3598 #endif
3599     __ LoadConstant(table_index_reg, table_index_val);
3600     LiftoffAssembler::VarState table_index(kPointerValueType, table_index_reg,
3601                                            0);
3602 
3603     LiftoffRegister segment_index_reg =
3604         pinned.set(__ GetUnusedRegister(kGpReg, pinned));
3605     __ LoadConstant(segment_index_reg, segment_index_val);
3606     LiftoffAssembler::VarState segment_index(kPointerValueType,
3607                                              segment_index_reg, 0);
3608 
3609     LiftoffAssembler::VarState size = __ cache_state()->stack_state.end()[-1];
3610     LiftoffAssembler::VarState src = __ cache_state()->stack_state.end()[-2];
3611     LiftoffAssembler::VarState dst = __ cache_state()->stack_state.end()[-3];
3612 
3613     WasmCode::RuntimeStubId target = WasmCode::kWasmTableInit;
3614     compiler::CallDescriptor* call_descriptor =
3615         GetBuiltinCallDescriptor<WasmTableInitDescriptor>(compilation_zone_);
3616 
3617     ValueType sig_reps[] = {kWasmI32, kWasmI32, kWasmI32,
3618                             table_index_val.type(), segment_index_val.type()};
3619     FunctionSig sig(0, 5, sig_reps);
3620 
3621     __ PrepareBuiltinCall(&sig, call_descriptor,
3622                           {dst, src, size, table_index, segment_index});
3623     __ CallRuntimeStub(target);
3624     DefineSafepoint();
3625 
3626     // Pop parameters from the value stack.
3627     __ cache_state()->stack_state.pop_back(3);
3628 
3629     RegisterDebugSideTableEntry(DebugSideTableBuilder::kDidSpill);
3630   }
3631 
ElemDrop(FullDecoder * decoder,const ElemDropImmediate<validate> & imm)3632   void ElemDrop(FullDecoder* decoder, const ElemDropImmediate<validate>& imm) {
3633     LiftoffRegList pinned;
3634     Register seg_size_array =
3635         pinned.set(__ GetUnusedRegister(kGpReg, pinned)).gp();
3636     LOAD_INSTANCE_FIELD(seg_size_array, DroppedElemSegments,
3637                         kSystemPointerSize);
3638 
3639     LiftoffRegister seg_index =
3640         pinned.set(__ GetUnusedRegister(kGpReg, pinned));
3641     __ LoadConstant(seg_index, WasmValue(imm.index));
3642 
3643     // Set the length of the segment to '0' to drop it.
3644     LiftoffRegister one_reg = pinned.set(__ GetUnusedRegister(kGpReg, pinned));
3645     __ LoadConstant(one_reg, WasmValue(1));
3646     __ Store(seg_size_array, seg_index.gp(), 0, one_reg, StoreType::kI32Store,
3647              pinned);
3648   }
3649 
TableCopy(FullDecoder * decoder,const TableCopyImmediate<validate> & imm,Vector<Value> args)3650   void TableCopy(FullDecoder* decoder, const TableCopyImmediate<validate>& imm,
3651                  Vector<Value> args) {
3652     LiftoffRegList pinned;
3653 
3654 #if V8_TARGET_ARCH_32_BIT || defined(V8_COMPRESS_POINTERS)
3655     WasmValue table_dst_index_val(
3656         static_cast<uint32_t>(Smi::FromInt(imm.table_dst.index).ptr()));
3657     WasmValue table_src_index_val(
3658         static_cast<uint32_t>(Smi::FromInt(imm.table_src.index).ptr()));
3659 #else
3660     WasmValue table_dst_index_val(
3661         static_cast<uint64_t>(Smi::FromInt(imm.table_dst.index).ptr()));
3662     WasmValue table_src_index_val(
3663         static_cast<uint64_t>(Smi::FromInt(imm.table_src.index).ptr()));
3664 #endif
3665 
3666     LiftoffRegister table_dst_index_reg =
3667         pinned.set(__ GetUnusedRegister(kGpReg, pinned));
3668     __ LoadConstant(table_dst_index_reg, table_dst_index_val);
3669     LiftoffAssembler::VarState table_dst_index(kPointerValueType,
3670                                                table_dst_index_reg, 0);
3671 
3672     LiftoffRegister table_src_index_reg =
3673         pinned.set(__ GetUnusedRegister(kGpReg, pinned));
3674     __ LoadConstant(table_src_index_reg, table_src_index_val);
3675     LiftoffAssembler::VarState table_src_index(kPointerValueType,
3676                                                table_src_index_reg, 0);
3677 
3678     LiftoffAssembler::VarState size = __ cache_state()->stack_state.end()[-1];
3679     LiftoffAssembler::VarState src = __ cache_state()->stack_state.end()[-2];
3680     LiftoffAssembler::VarState dst = __ cache_state()->stack_state.end()[-3];
3681 
3682     WasmCode::RuntimeStubId target = WasmCode::kWasmTableCopy;
3683     compiler::CallDescriptor* call_descriptor =
3684         GetBuiltinCallDescriptor<WasmTableCopyDescriptor>(compilation_zone_);
3685 
3686     ValueType sig_reps[] = {kWasmI32, kWasmI32, kWasmI32,
3687                             table_dst_index_val.type(),
3688                             table_src_index_val.type()};
3689     FunctionSig sig(0, 5, sig_reps);
3690 
3691     __ PrepareBuiltinCall(&sig, call_descriptor,
3692                           {dst, src, size, table_dst_index, table_src_index});
3693     __ CallRuntimeStub(target);
3694     DefineSafepoint();
3695 
3696     // Pop parameters from the value stack.
3697     __ cache_state()->stack_state.pop_back(3);
3698 
3699     RegisterDebugSideTableEntry(DebugSideTableBuilder::kDidSpill);
3700   }
3701 
TableGrow(FullDecoder * decoder,const TableIndexImmediate<validate> & imm,const Value & value,const Value & delta,Value * result)3702   void TableGrow(FullDecoder* decoder, const TableIndexImmediate<validate>& imm,
3703                  const Value& value, const Value& delta, Value* result) {
3704     unsupported(decoder, kRefTypes, "table.grow");
3705   }
3706 
TableSize(FullDecoder * decoder,const TableIndexImmediate<validate> & imm,Value * result)3707   void TableSize(FullDecoder* decoder, const TableIndexImmediate<validate>& imm,
3708                  Value* result) {
3709     unsupported(decoder, kRefTypes, "table.size");
3710   }
3711 
TableFill(FullDecoder * decoder,const TableIndexImmediate<validate> & imm,const Value & start,const Value & value,const Value & count)3712   void TableFill(FullDecoder* decoder, const TableIndexImmediate<validate>& imm,
3713                  const Value& start, const Value& value, const Value& count) {
3714     unsupported(decoder, kRefTypes, "table.fill");
3715   }
3716 
StructNewWithRtt(FullDecoder * decoder,const StructIndexImmediate<validate> & imm,const Value & rtt,const Value args[],Value * result)3717   void StructNewWithRtt(FullDecoder* decoder,
3718                         const StructIndexImmediate<validate>& imm,
3719                         const Value& rtt, const Value args[], Value* result) {
3720     // TODO(7748): Implement.
3721     unsupported(decoder, kGC, "struct.new_with_rtt");
3722   }
StructNewDefault(FullDecoder * decoder,const StructIndexImmediate<validate> & imm,const Value & rtt,Value * result)3723   void StructNewDefault(FullDecoder* decoder,
3724                         const StructIndexImmediate<validate>& imm,
3725                         const Value& rtt, Value* result) {
3726     // TODO(7748): Implement.
3727     unsupported(decoder, kGC, "struct.new_default_with_rtt");
3728   }
StructGet(FullDecoder * decoder,const Value & struct_obj,const FieldIndexImmediate<validate> & field,bool is_signed,Value * result)3729   void StructGet(FullDecoder* decoder, const Value& struct_obj,
3730                  const FieldIndexImmediate<validate>& field, bool is_signed,
3731                  Value* result) {
3732     // TODO(7748): Implement.
3733     unsupported(decoder, kGC, "struct.get");
3734   }
StructSet(FullDecoder * decoder,const Value & struct_obj,const FieldIndexImmediate<validate> & field,const Value & field_value)3735   void StructSet(FullDecoder* decoder, const Value& struct_obj,
3736                  const FieldIndexImmediate<validate>& field,
3737                  const Value& field_value) {
3738     // TODO(7748): Implement.
3739     unsupported(decoder, kGC, "struct.set");
3740   }
3741 
ArrayNewWithRtt(FullDecoder * decoder,const ArrayIndexImmediate<validate> & imm,const Value & length,const Value & initial_value,const Value & rtt,Value * result)3742   void ArrayNewWithRtt(FullDecoder* decoder,
3743                        const ArrayIndexImmediate<validate>& imm,
3744                        const Value& length, const Value& initial_value,
3745                        const Value& rtt, Value* result) {
3746     // TODO(7748): Implement.
3747     unsupported(decoder, kGC, "array.new_with_rtt");
3748   }
ArrayNewDefault(FullDecoder * decoder,const ArrayIndexImmediate<validate> & imm,const Value & length,const Value & rtt,Value * result)3749   void ArrayNewDefault(FullDecoder* decoder,
3750                        const ArrayIndexImmediate<validate>& imm,
3751                        const Value& length, const Value& rtt, Value* result) {
3752     // TODO(7748): Implement.
3753     unsupported(decoder, kGC, "array.new_default_with_rtt");
3754   }
ArrayGet(FullDecoder * decoder,const Value & array_obj,const ArrayIndexImmediate<validate> & imm,const Value & index,bool is_signed,Value * result)3755   void ArrayGet(FullDecoder* decoder, const Value& array_obj,
3756                 const ArrayIndexImmediate<validate>& imm, const Value& index,
3757                 bool is_signed, Value* result) {
3758     // TODO(7748): Implement.
3759     unsupported(decoder, kGC, "array.get");
3760   }
ArraySet(FullDecoder * decoder,const Value & array_obj,const ArrayIndexImmediate<validate> & imm,const Value & index,const Value & value)3761   void ArraySet(FullDecoder* decoder, const Value& array_obj,
3762                 const ArrayIndexImmediate<validate>& imm, const Value& index,
3763                 const Value& value) {
3764     // TODO(7748): Implement.
3765     unsupported(decoder, kGC, "array.set");
3766   }
ArrayLen(FullDecoder * decoder,const Value & array_obj,Value * result)3767   void ArrayLen(FullDecoder* decoder, const Value& array_obj, Value* result) {
3768     // TODO(7748): Implement.
3769     unsupported(decoder, kGC, "array.len");
3770   }
3771 
I31New(FullDecoder * decoder,const Value & input,Value * result)3772   void I31New(FullDecoder* decoder, const Value& input, Value* result) {
3773     // TODO(7748): Implement.
3774     unsupported(decoder, kGC, "i31.new");
3775   }
I31GetS(FullDecoder * decoder,const Value & input,Value * result)3776   void I31GetS(FullDecoder* decoder, const Value& input, Value* result) {
3777     // TODO(7748): Implement.
3778     unsupported(decoder, kGC, "i31.get_s");
3779   }
I31GetU(FullDecoder * decoder,const Value & input,Value * result)3780   void I31GetU(FullDecoder* decoder, const Value& input, Value* result) {
3781     // TODO(7748): Implement.
3782     unsupported(decoder, kGC, "i31.get_u");
3783   }
3784 
RttCanon(FullDecoder * decoder,const HeapTypeImmediate<validate> & imm,Value * result)3785   void RttCanon(FullDecoder* decoder, const HeapTypeImmediate<validate>& imm,
3786                 Value* result) {
3787     // TODO(7748): Implement.
3788     unsupported(decoder, kGC, "rtt.canon");
3789   }
RttSub(FullDecoder * decoder,const HeapTypeImmediate<validate> & imm,const Value & parent,Value * result)3790   void RttSub(FullDecoder* decoder, const HeapTypeImmediate<validate>& imm,
3791               const Value& parent, Value* result) {
3792     // TODO(7748): Implement.
3793     unsupported(decoder, kGC, "rtt.sub");
3794   }
3795 
RefTest(FullDecoder * decoder,const Value & obj,const Value & rtt,Value * result)3796   void RefTest(FullDecoder* decoder, const Value& obj, const Value& rtt,
3797                Value* result) {
3798     // TODO(7748): Implement.
3799     unsupported(decoder, kGC, "ref.test");
3800   }
RefCast(FullDecoder * decoder,const Value & obj,const Value & rtt,Value * result)3801   void RefCast(FullDecoder* decoder, const Value& obj, const Value& rtt,
3802                Value* result) {
3803     // TODO(7748): Implement.
3804     unsupported(decoder, kGC, "ref.cast");
3805   }
BrOnCast(FullDecoder * decoder,const Value & obj,const Value & rtt,Value * result_on_branch,uint32_t depth)3806   void BrOnCast(FullDecoder* decoder, const Value& obj, const Value& rtt,
3807                 Value* result_on_branch, uint32_t depth) {
3808     // TODO(7748): Implement.
3809     unsupported(decoder, kGC, "br_on_cast");
3810   }
3811 
PassThrough(FullDecoder * decoder,const Value & from,Value * to)3812   void PassThrough(FullDecoder* decoder, const Value& from, Value* to) {
3813     // TODO(7748): Implement.
3814     unsupported(decoder, kGC, "");
3815   }
3816 
3817  private:
CallDirect(FullDecoder * decoder,const CallFunctionImmediate<validate> & imm,const Value args[],Value returns[],CallKind call_kind)3818   void CallDirect(FullDecoder* decoder,
3819                   const CallFunctionImmediate<validate>& imm,
3820                   const Value args[], Value returns[], CallKind call_kind) {
3821     for (ValueType ret : imm.sig->returns()) {
3822       if (!CheckSupportedType(decoder,
3823                               FLAG_experimental_liftoff_extern_ref
3824                                   ? kSupportedTypes
3825                                   : kSupportedTypesWithoutRefs,
3826                               ret, "return")) {
3827         // TODO(7581): Remove this once reference-types are full supported.
3828         if (!ret.is_reference_type()) {
3829           return;
3830         }
3831       }
3832     }
3833 
3834     auto call_descriptor =
3835         compiler::GetWasmCallDescriptor(compilation_zone_, imm.sig);
3836     call_descriptor =
3837         GetLoweredCallDescriptor(compilation_zone_, call_descriptor);
3838 
3839     if (imm.index < env_->module->num_imported_functions) {
3840       // A direct call to an imported function.
3841       LiftoffRegList pinned;
3842       Register tmp = pinned.set(__ GetUnusedRegister(kGpReg, pinned)).gp();
3843       Register target = pinned.set(__ GetUnusedRegister(kGpReg, pinned)).gp();
3844 
3845       Register imported_targets = tmp;
3846       LOAD_INSTANCE_FIELD(imported_targets, ImportedFunctionTargets,
3847                           kSystemPointerSize);
3848       __ Load(LiftoffRegister(target), imported_targets, no_reg,
3849               imm.index * sizeof(Address), kPointerLoadType, pinned);
3850 
3851       Register imported_function_refs = tmp;
3852       LOAD_TAGGED_PTR_INSTANCE_FIELD(imported_function_refs,
3853                                      ImportedFunctionRefs);
3854       Register imported_function_ref = tmp;
3855       __ LoadTaggedPointer(
3856           imported_function_ref, imported_function_refs, no_reg,
3857           ObjectAccess::ElementOffsetInTaggedFixedArray(imm.index), pinned);
3858 
3859       Register* explicit_instance = &imported_function_ref;
3860       __ PrepareCall(imm.sig, call_descriptor, &target, explicit_instance);
3861       if (call_kind == kReturnCall) {
3862         __ PrepareTailCall(
3863             static_cast<int>(call_descriptor->StackParameterCount()),
3864             static_cast<int>(
3865                 call_descriptor->GetStackParameterDelta(descriptor_)));
3866         __ TailCallIndirect(target);
3867       } else {
3868         source_position_table_builder_.AddPosition(
3869             __ pc_offset(), SourcePosition(decoder->position()), true);
3870         __ CallIndirect(imm.sig, call_descriptor, target);
3871       }
3872     } else {
3873       // A direct call within this module just gets the current instance.
3874       __ PrepareCall(imm.sig, call_descriptor);
3875       // Just encode the function index. This will be patched at instantiation.
3876       Address addr = static_cast<Address>(imm.index);
3877       if (call_kind == kReturnCall) {
3878         DCHECK(descriptor_->CanTailCall(call_descriptor));
3879         __ PrepareTailCall(
3880             static_cast<int>(call_descriptor->StackParameterCount()),
3881             static_cast<int>(
3882                 call_descriptor->GetStackParameterDelta(descriptor_)));
3883         __ TailCallNativeWasmCode(addr);
3884       } else {
3885         source_position_table_builder_.AddPosition(
3886             __ pc_offset(), SourcePosition(decoder->position()), true);
3887         __ CallNativeWasmCode(addr);
3888       }
3889     }
3890 
3891     DefineSafepoint();
3892     RegisterDebugSideTableEntry(DebugSideTableBuilder::kDidSpill);
3893 
3894     __ FinishCall(imm.sig, call_descriptor);
3895   }
3896 
CallIndirect(FullDecoder * decoder,const Value & index_val,const CallIndirectImmediate<validate> & imm,CallKind call_kind)3897   void CallIndirect(FullDecoder* decoder, const Value& index_val,
3898                     const CallIndirectImmediate<validate>& imm,
3899                     CallKind call_kind) {
3900     if (imm.table_index != 0) {
3901       return unsupported(decoder, kRefTypes, "table index != 0");
3902     }
3903     for (ValueType ret : imm.sig->returns()) {
3904       if (!CheckSupportedType(decoder,
3905                               FLAG_experimental_liftoff_extern_ref
3906                                   ? kSupportedTypes
3907                                   : kSupportedTypesWithoutRefs,
3908                               ret, "return")) {
3909         return;
3910       }
3911     }
3912 
3913     // Pop the index.
3914     Register index = __ PopToRegister().gp();
3915     // If that register is still being used after popping, we move it to another
3916     // register, because we want to modify that register.
3917     if (__ cache_state()->is_used(LiftoffRegister(index))) {
3918       Register new_index =
3919           __ GetUnusedRegister(kGpReg, LiftoffRegList::ForRegs(index)).gp();
3920       __ Move(new_index, index, kWasmI32);
3921       index = new_index;
3922     }
3923 
3924     LiftoffRegList pinned = LiftoffRegList::ForRegs(index);
3925     // Get three temporary registers.
3926     Register table = pinned.set(__ GetUnusedRegister(kGpReg, pinned)).gp();
3927     Register tmp_const = pinned.set(__ GetUnusedRegister(kGpReg, pinned)).gp();
3928     Register scratch = pinned.set(__ GetUnusedRegister(kGpReg, pinned)).gp();
3929 
3930     // Bounds check against the table size.
3931     Label* invalid_func_label = AddOutOfLineTrap(
3932         decoder->position(), WasmCode::kThrowWasmTrapTableOutOfBounds);
3933 
3934     uint32_t canonical_sig_num =
3935         env_->module->canonicalized_type_ids[imm.sig_index];
3936     DCHECK_GE(canonical_sig_num, 0);
3937     DCHECK_GE(kMaxInt, canonical_sig_num);
3938 
3939     // Compare against table size stored in
3940     // {instance->indirect_function_table_size}.
3941     LOAD_INSTANCE_FIELD(tmp_const, IndirectFunctionTableSize, kUInt32Size);
3942     __ emit_cond_jump(kUnsignedGreaterEqual, invalid_func_label, kWasmI32,
3943                       index, tmp_const);
3944 
3945     // Mask the index to prevent SSCA.
3946     if (FLAG_untrusted_code_mitigations) {
3947       DEBUG_CODE_COMMENT("Mask indirect call index");
3948       // mask = ((index - size) & ~index) >> 31
3949       // Reuse allocated registers; note: size is still stored in {tmp_const}.
3950       Register diff = table;
3951       Register neg_index = tmp_const;
3952       Register mask = scratch;
3953       // 1) diff = index - size
3954       __ emit_i32_sub(diff, index, tmp_const);
3955       // 2) neg_index = ~index
3956       __ LoadConstant(LiftoffRegister(neg_index), WasmValue(int32_t{-1}));
3957       __ emit_i32_xor(neg_index, neg_index, index);
3958       // 3) mask = diff & neg_index
3959       __ emit_i32_and(mask, diff, neg_index);
3960       // 4) mask = mask >> 31
3961       __ emit_i32_sari(mask, mask, 31);
3962 
3963       // Apply mask.
3964       __ emit_i32_and(index, index, mask);
3965     }
3966 
3967     DEBUG_CODE_COMMENT("Check indirect call signature");
3968     // Load the signature from {instance->ift_sig_ids[key]}
3969     LOAD_INSTANCE_FIELD(table, IndirectFunctionTableSigIds, kSystemPointerSize);
3970     // Shift {index} by 2 (multiply by 4) to represent kInt32Size items.
3971     STATIC_ASSERT((1 << 2) == kInt32Size);
3972     __ emit_i32_shli(index, index, 2);
3973     __ Load(LiftoffRegister(scratch), table, index, 0, LoadType::kI32Load,
3974             pinned);
3975 
3976     // Compare against expected signature.
3977     __ LoadConstant(LiftoffRegister(tmp_const), WasmValue(canonical_sig_num));
3978 
3979     Label* sig_mismatch_label = AddOutOfLineTrap(
3980         decoder->position(), WasmCode::kThrowWasmTrapFuncSigMismatch);
3981     __ emit_cond_jump(kUnequal, sig_mismatch_label,
3982                       LiftoffAssembler::kWasmIntPtr, scratch, tmp_const);
3983 
3984     // At this point {index} has already been multiplied by 4.
3985     DEBUG_CODE_COMMENT("Execute indirect call");
3986     if (kTaggedSize != kInt32Size) {
3987       DCHECK_EQ(kTaggedSize, kInt32Size * 2);
3988       // Multiply {index} by another 2 to represent kTaggedSize items.
3989       __ emit_i32_add(index, index, index);
3990     }
3991     // At this point {index} has already been multiplied by kTaggedSize.
3992 
3993     // Load the instance from {instance->ift_instances[key]}
3994     LOAD_TAGGED_PTR_INSTANCE_FIELD(table, IndirectFunctionTableRefs);
3995     __ LoadTaggedPointer(tmp_const, table, index,
3996                          ObjectAccess::ElementOffsetInTaggedFixedArray(0),
3997                          pinned);
3998 
3999     if (kTaggedSize != kSystemPointerSize) {
4000       DCHECK_EQ(kSystemPointerSize, kTaggedSize * 2);
4001       // Multiply {index} by another 2 to represent kSystemPointerSize items.
4002       __ emit_i32_add(index, index, index);
4003     }
4004     // At this point {index} has already been multiplied by kSystemPointerSize.
4005 
4006     Register* explicit_instance = &tmp_const;
4007 
4008     // Load the target from {instance->ift_targets[key]}
4009     LOAD_INSTANCE_FIELD(table, IndirectFunctionTableTargets,
4010                         kSystemPointerSize);
4011     __ Load(LiftoffRegister(scratch), table, index, 0, kPointerLoadType,
4012             pinned);
4013 
4014     auto call_descriptor =
4015         compiler::GetWasmCallDescriptor(compilation_zone_, imm.sig);
4016     call_descriptor =
4017         GetLoweredCallDescriptor(compilation_zone_, call_descriptor);
4018 
4019     Register target = scratch;
4020     __ PrepareCall(imm.sig, call_descriptor, &target, explicit_instance);
4021     if (call_kind == kReturnCall) {
4022       __ PrepareTailCall(
4023           static_cast<int>(call_descriptor->StackParameterCount()),
4024           static_cast<int>(
4025               call_descriptor->GetStackParameterDelta(descriptor_)));
4026       __ TailCallIndirect(target);
4027     } else {
4028       source_position_table_builder_.AddPosition(
4029           __ pc_offset(), SourcePosition(decoder->position()), true);
4030       __ CallIndirect(imm.sig, call_descriptor, target);
4031     }
4032 
4033     DefineSafepoint();
4034     RegisterDebugSideTableEntry(DebugSideTableBuilder::kDidSpill);
4035 
4036     __ FinishCall(imm.sig, call_descriptor);
4037   }
4038 
4039   static constexpr WasmOpcode kNoOutstandingOp = kExprUnreachable;
4040 
4041   LiftoffAssembler asm_;
4042 
4043   // Used for merging code generation of subsequent operations (via look-ahead).
4044   // Set by the first opcode, reset by the second.
4045   WasmOpcode outstanding_op_ = kNoOutstandingOp;
4046 
4047   compiler::CallDescriptor* const descriptor_;
4048   CompilationEnv* const env_;
4049   DebugSideTableBuilder* const debug_sidetable_builder_;
4050   const ForDebugging for_debugging_;
4051   LiftoffBailoutReason bailout_reason_ = kSuccess;
4052   const int func_index_;
4053   ZoneVector<OutOfLineCode> out_of_line_code_;
4054   SourcePositionTableBuilder source_position_table_builder_;
4055   ZoneVector<trap_handler::ProtectedInstructionData> protected_instructions_;
4056   // Zone used to store information during compilation. The result will be
4057   // stored independently, such that this zone can die together with the
4058   // LiftoffCompiler after compilation.
4059   Zone* compilation_zone_;
4060   SafepointTableBuilder safepoint_table_builder_;
4061   // The pc offset of the instructions to reserve the stack frame. Needed to
4062   // patch the actually needed stack size in the end.
4063   uint32_t pc_offset_stack_frame_construction_ = 0;
4064   // For emitting breakpoint, we store a pointer to the position of the next
4065   // breakpoint, and a pointer after the list of breakpoints as end marker.
4066   // A single breakpoint at offset 0 indicates that we should prepare the
4067   // function for stepping by flooding it with breakpoints.
4068   int* next_breakpoint_ptr_ = nullptr;
4069   int* next_breakpoint_end_ = nullptr;
4070 
4071   // Introduce a dead breakpoint to ensure that the calculation of the return
4072   // address in OSR is correct.
4073   int dead_breakpoint_ = 0;
4074 
4075   // Remember whether the "hook on function call" has already been checked.
4076   // This happens at the first breakable opcode in the function (if compiling
4077   // for debugging).
4078   bool checked_hook_on_function_call_ = false;
4079 
has_outstanding_op() const4080   bool has_outstanding_op() const {
4081     return outstanding_op_ != kNoOutstandingOp;
4082   }
4083 
TraceCacheState(FullDecoder * decoder) const4084   void TraceCacheState(FullDecoder* decoder) const {
4085     if (!FLAG_trace_liftoff) return;
4086     StdoutStream os;
4087     for (int control_depth = decoder->control_depth() - 1; control_depth >= -1;
4088          --control_depth) {
4089       auto* cache_state =
4090           control_depth == -1 ? __ cache_state()
4091                               : &decoder->control_at(control_depth)
4092                                      ->label_state;
4093       os << PrintCollection(cache_state->stack_state);
4094       if (control_depth != -1) PrintF("; ");
4095     }
4096     os << "\n";
4097   }
4098 
DefineSafepoint()4099   void DefineSafepoint() {
4100     Safepoint safepoint = safepoint_table_builder_.DefineSafepoint(
4101         &asm_, Safepoint::kNoLazyDeopt);
4102     __ cache_state()->DefineSafepoint(safepoint);
4103   }
4104 
4105   DISALLOW_IMPLICIT_CONSTRUCTORS(LiftoffCompiler);
4106 };
4107 
4108 }  // namespace
4109 
ExecuteLiftoffCompilation(AccountingAllocator * allocator,CompilationEnv * env,const FunctionBody & func_body,int func_index,ForDebugging for_debugging,Counters * counters,WasmFeatures * detected,Vector<int> breakpoints,std::unique_ptr<DebugSideTable> * debug_sidetable,int dead_breakpoint)4110 WasmCompilationResult ExecuteLiftoffCompilation(
4111     AccountingAllocator* allocator, CompilationEnv* env,
4112     const FunctionBody& func_body, int func_index, ForDebugging for_debugging,
4113     Counters* counters, WasmFeatures* detected, Vector<int> breakpoints,
4114     std::unique_ptr<DebugSideTable>* debug_sidetable, int dead_breakpoint) {
4115   int func_body_size = static_cast<int>(func_body.end - func_body.start);
4116   TRACE_EVENT2(TRACE_DISABLED_BY_DEFAULT("v8.wasm.detailed"),
4117                "wasm.CompileBaseline", "funcIndex", func_index, "bodySize",
4118                func_body_size);
4119 
4120   Zone zone(allocator, "LiftoffCompilationZone");
4121   auto call_descriptor = compiler::GetWasmCallDescriptor(&zone, func_body.sig);
4122   size_t code_size_estimate =
4123       WasmCodeManager::EstimateLiftoffCodeSize(func_body_size);
4124   // Allocate the initial buffer a bit bigger to avoid reallocation during code
4125   // generation.
4126   std::unique_ptr<wasm::WasmInstructionBuffer> instruction_buffer =
4127       wasm::WasmInstructionBuffer::New(128 + code_size_estimate * 4 / 3);
4128   std::unique_ptr<DebugSideTableBuilder> debug_sidetable_builder;
4129   // If we are emitting breakpoints, we should also emit the debug side table.
4130   DCHECK_IMPLIES(!breakpoints.empty(), debug_sidetable != nullptr);
4131   if (debug_sidetable) {
4132     debug_sidetable_builder = std::make_unique<DebugSideTableBuilder>();
4133   }
4134   WasmFullDecoder<Decoder::kBooleanValidation, LiftoffCompiler> decoder(
4135       &zone, env->module, env->enabled_features, detected, func_body,
4136       call_descriptor, env, &zone, instruction_buffer->CreateView(),
4137       debug_sidetable_builder.get(), for_debugging, func_index, breakpoints,
4138       dead_breakpoint);
4139   decoder.Decode();
4140   LiftoffCompiler* compiler = &decoder.interface();
4141   if (decoder.failed()) compiler->OnFirstError(&decoder);
4142 
4143   if (counters) {
4144     // Check that the histogram for the bailout reasons has the correct size.
4145     DCHECK_EQ(0, counters->liftoff_bailout_reasons()->min());
4146     DCHECK_EQ(kNumBailoutReasons - 1,
4147               counters->liftoff_bailout_reasons()->max());
4148     DCHECK_EQ(kNumBailoutReasons,
4149               counters->liftoff_bailout_reasons()->num_buckets());
4150     // Register the bailout reason (can also be {kSuccess}).
4151     counters->liftoff_bailout_reasons()->AddSample(
4152         static_cast<int>(compiler->bailout_reason()));
4153     if (compiler->did_bailout()) {
4154       counters->liftoff_unsupported_functions()->Increment();
4155     } else {
4156       counters->liftoff_compiled_functions()->Increment();
4157     }
4158   }
4159 
4160   if (compiler->did_bailout()) return WasmCompilationResult{};
4161 
4162   WasmCompilationResult result;
4163   compiler->GetCode(&result.code_desc);
4164   result.instr_buffer = instruction_buffer->ReleaseBuffer();
4165   result.source_positions = compiler->GetSourcePositionTable();
4166   result.protected_instructions_data = compiler->GetProtectedInstructionsData();
4167   result.frame_slot_count = compiler->GetTotalFrameSlotCountForGC();
4168   result.tagged_parameter_slots = call_descriptor->GetTaggedParameterSlots();
4169   result.func_index = func_index;
4170   result.result_tier = ExecutionTier::kLiftoff;
4171   result.for_debugging = for_debugging;
4172   if (debug_sidetable) {
4173     *debug_sidetable = debug_sidetable_builder->GenerateDebugSideTable();
4174   }
4175 
4176   DCHECK(result.succeeded());
4177   return result;
4178 }
4179 
GenerateLiftoffDebugSideTable(AccountingAllocator * allocator,CompilationEnv * env,const FunctionBody & func_body,int func_index)4180 std::unique_ptr<DebugSideTable> GenerateLiftoffDebugSideTable(
4181     AccountingAllocator* allocator, CompilationEnv* env,
4182     const FunctionBody& func_body, int func_index) {
4183   Zone zone(allocator, "LiftoffDebugSideTableZone");
4184   auto call_descriptor = compiler::GetWasmCallDescriptor(&zone, func_body.sig);
4185   DebugSideTableBuilder debug_sidetable_builder;
4186   WasmFeatures detected;
4187   WasmFullDecoder<Decoder::kBooleanValidation, LiftoffCompiler> decoder(
4188       &zone, env->module, env->enabled_features, &detected, func_body,
4189       call_descriptor, env, &zone,
4190       NewAssemblerBuffer(AssemblerBase::kDefaultBufferSize),
4191       &debug_sidetable_builder, kForDebugging, func_index);
4192   decoder.Decode();
4193   DCHECK(decoder.ok());
4194   DCHECK(!decoder.interface().did_bailout());
4195   return debug_sidetable_builder.GenerateDebugSideTable();
4196 }
4197 
4198 #undef __
4199 #undef TRACE
4200 #undef WASM_INSTANCE_OBJECT_FIELD_OFFSET
4201 #undef WASM_INSTANCE_OBJECT_FIELD_SIZE
4202 #undef LOAD_INSTANCE_FIELD
4203 #undef LOAD_TAGGED_PTR_INSTANCE_FIELD
4204 #undef DEBUG_CODE_COMMENT
4205 
4206 }  // namespace wasm
4207 }  // namespace internal
4208 }  // namespace v8
4209