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