v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
liftoff-assembler-arm-inl.h
Go to the documentation of this file.
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#ifndef V8_WASM_BASELINE_ARM_LIFTOFF_ASSEMBLER_ARM_INL_H_
6#define V8_WASM_BASELINE_ARM_LIFTOFF_ASSEMBLER_ARM_INL_H_
7
8#include <optional>
9
13#include "src/common/globals.h"
21
22namespace v8::internal::wasm {
23
24namespace liftoff {
25
26// half
27// slot Frame
28// -----+--------------------+---------------------------
29// n+3 | parameter n |
30// ... | ... |
31// 4 | parameter 1 | or parameter 2
32// 3 | parameter 0 | or parameter 1
33// 2 | (result address) | or parameter 0
34// -----+--------------------+---------------------------
35// 1 | return addr (lr) |
36// 0 | previous frame (fp)|
37// -----+--------------------+ <-- frame ptr (fp)
38// -1 | StackFrame::WASM |
39// -2 | instance |
40// -3 | feedback vector |
41// -4 | tiering budget |
42// -----+--------------------+---------------------------
43// -5 | slot 0 (high) | ^
44// -6 | slot 0 (low) | |
45// -7 | slot 1 (high) | Frame slots
46// -8 | slot 1 (low) | |
47// | | v
48// -----+--------------------+ <-- stack ptr (sp)
49//
51 "Slot size should be twice the size of the 32 bit pointer.");
52// kPatchInstructionsRequired sets a maximum limit of how many instructions that
53// PatchPrepareStackFrame will use in order to increase the stack appropriately.
54// Three instructions are required to sub a large constant, movw + movt + sub.
55constexpr int32_t kPatchInstructionsRequired = 3;
57
58inline MemOperand GetStackSlot(int offset) { return MemOperand(fp, -offset); }
59
61 int32_t half_offset =
63 return MemOperand(offset > 0 ? fp : sp, -offset + half_offset);
64}
65
69
72 Register offset, int32_t offset_imm,
73 unsigned shift_amount = 0) {
74 if (offset != no_reg) {
75 if (offset_imm == 0) return MemOperand(addr, offset, LSL, shift_amount);
76 Register tmp = temps->Acquire();
77 if (shift_amount == 0) {
78 assm->add(tmp, offset, Operand(offset_imm));
79 } else {
80 assm->lsl(tmp, offset, Operand(shift_amount));
81 assm->add(tmp, tmp, Operand(offset_imm));
82 }
83 return MemOperand(addr, tmp);
84 }
85 return MemOperand(addr, offset_imm);
86}
87
90 Register addr_reg, Register offset_reg,
91 uintptr_t offset_imm,
92 Register result_reg = no_reg) {
93 if (offset_reg == no_reg && offset_imm == 0) {
94 if (result_reg == addr_reg || result_reg == no_reg) return addr_reg;
95 assm->mov(result_reg, addr_reg);
96 return result_reg;
97 }
98 if (result_reg == no_reg) result_reg = temps->Acquire();
99 if (offset_reg == no_reg) {
100 assm->add(result_reg, addr_reg, Operand(offset_imm));
101 } else {
102 assm->add(result_reg, addr_reg, Operand(offset_reg));
103 if (offset_imm != 0) assm->add(result_reg, result_reg, Operand(offset_imm));
104 }
105 return result_reg;
106}
107
109 switch (cond) {
110 case kLessThan:
111 return kUnsignedLessThan;
112 case kLessThanEqual:
114 case kGreaterThan:
118 case kEqual:
119 case kNotEqual:
124 return cond;
125 default:
126 UNREACHABLE();
127 }
128}
129
131 void (Assembler::*op_with_carry)(Register, Register, const Operand&,
132 SBit, Condition)>
135 Register dst_low = dst.low_gp();
136 if (dst_low == lhs.high_gp() || dst_low == rhs.high_gp()) {
137 dst_low =
138 assm->GetUnusedRegister(kGpReg, LiftoffRegList{lhs, rhs, dst.high_gp()})
139 .gp();
140 }
141 (assm->*op)(dst_low, lhs.low_gp(), rhs.low_gp(), SetCC, al);
142 (assm->*op_with_carry)(dst.high_gp(), lhs.high_gp(), Operand(rhs.high_gp()),
143 LeaveCC, al);
144 if (dst_low != dst.low_gp()) assm->mov(dst.low_gp(), dst_low);
145}
146
147template <void (Assembler::*op)(Register, Register, const Operand&, SBit,
148 Condition),
149 void (Assembler::*op_with_carry)(Register, Register, const Operand&,
150 SBit, Condition)>
152 LiftoffRegister lhs, int64_t imm) {
153 // The compiler allocated registers such that either {dst == lhs} or there is
154 // no overlap between the two.
155 DCHECK_NE(dst.low_gp(), lhs.high_gp());
156 int32_t imm_low_word = static_cast<int32_t>(imm);
157 int32_t imm_high_word = static_cast<int32_t>(imm >> 32);
158 (assm->*op)(dst.low_gp(), lhs.low_gp(), Operand(imm_low_word), SetCC, al);
159 (assm->*op_with_carry)(dst.high_gp(), lhs.high_gp(), Operand(imm_high_word),
160 LeaveCC, al);
161}
162
164 Register),
165 bool is_left_shift>
167 LiftoffRegister src, Register amount) {
168 Register src_low = src.low_gp();
169 Register src_high = src.high_gp();
170 Register dst_low = dst.low_gp();
171 Register dst_high = dst.high_gp();
172 // Left shift writes {dst_high} then {dst_low}, right shifts write {dst_low}
173 // then {dst_high}.
174 Register clobbered_dst_reg = is_left_shift ? dst_high : dst_low;
175 LiftoffRegList pinned{clobbered_dst_reg, src};
176 Register amount_capped =
177 pinned.set(assm->GetUnusedRegister(kGpReg, pinned)).gp();
178 assm->and_(amount_capped, amount, Operand(0x3F));
179
180 // Ensure that writing the first half of {dst} does not overwrite the still
181 // needed half of {src}.
182 Register* later_src_reg = is_left_shift ? &src_low : &src_high;
183 if (*later_src_reg == clobbered_dst_reg) {
184 *later_src_reg = assm->GetUnusedRegister(kGpReg, pinned).gp();
185 assm->MacroAssembler::Move(*later_src_reg, clobbered_dst_reg);
186 }
187
188 (assm->*op)(dst_low, dst_high, src_low, src_high, amount_capped);
189}
190
192 DCHECK_LT(reg.code(), kDoubleCode_d16);
193 return LowDwVfpRegister::from_code(reg.code()).low();
194}
195
199
203
204enum class MinOrMax : uint8_t { kMin, kMax };
205template <typename RegisterType>
206inline void EmitFloatMinOrMax(LiftoffAssembler* assm, RegisterType dst,
207 RegisterType lhs, RegisterType rhs,
208 MinOrMax min_or_max) {
209 DCHECK(RegisterType::kSizeInBytes == 4 || RegisterType::kSizeInBytes == 8);
210 if (lhs == rhs) {
211 assm->MacroAssembler::Move(dst, lhs);
212 return;
213 }
214 Label done, is_nan;
215 if (min_or_max == MinOrMax::kMin) {
216 assm->MacroAssembler::FloatMin(dst, lhs, rhs, &is_nan);
217 } else {
218 assm->MacroAssembler::FloatMax(dst, lhs, rhs, &is_nan);
219 }
220 assm->b(&done);
221 assm->bind(&is_nan);
222 // Create a NaN output.
223 assm->vadd(dst, lhs, rhs);
224 assm->bind(&done);
225}
226
228 Register must_not_alias,
230 if (reg != must_not_alias) return reg;
231 Register tmp = temps->Acquire();
232 DCHECK_NE(reg, tmp);
233 assm->mov(tmp, reg);
234 return tmp;
235}
236
240 if (dst == lhs) {
241 assm->vqmovn(dt, sdt, dst.low_fp(), liftoff::GetSimd128Register(lhs));
242 assm->vqmovn(dt, sdt, dst.high_fp(), liftoff::GetSimd128Register(rhs));
243 } else {
244 assm->vqmovn(dt, sdt, dst.high_fp(), liftoff::GetSimd128Register(rhs));
245 assm->vqmovn(dt, sdt, dst.low_fp(), liftoff::GetSimd128Register(lhs));
246 }
247}
248
251 Condition cond) {
252 DCHECK(cond == eq || cond == ne || cond == lt || cond == le);
253
257 UseScratchRegisterScope temps(assm);
258 Register scratch = temps.Acquire();
259
260 assm->mov(scratch, Operand(0));
261 assm->VFPCompareAndSetFlags(left.low(), right.low());
262 assm->mov(scratch, Operand(-1), LeaveCC, cond);
263 if (cond == lt || cond == le) {
264 // Check for NaN.
265 assm->mov(scratch, Operand(0), LeaveCC, vs);
266 }
267 assm->vmov(dest.low(), scratch, scratch);
268
269 assm->mov(scratch, Operand(0));
270 assm->VFPCompareAndSetFlags(left.high(), right.high());
271 assm->mov(scratch, Operand(-1), LeaveCC, cond);
272 if (cond == lt || cond == le) {
273 // Check for NaN.
274 assm->mov(scratch, Operand(0), LeaveCC, vs);
275 }
276 assm->vmov(dest.high(), scratch, scratch);
277}
278
280 ValueKind kind) {
281#ifdef DEBUG
282 // The {str} instruction needs a temp register when the immediate in the
283 // provided MemOperand does not fit into 12 bits. This happens for large stack
284 // frames. This DCHECK checks that the temp register is available when needed.
286#endif
287 switch (kind) {
288 case kI16:
289 assm->strh(src.gp(), dst);
290 break;
291 case kI32:
292 case kRefNull:
293 case kRef:
294 assm->str(src.gp(), dst);
295 break;
296 case kI64:
297 // Positive offsets should be lowered to kI32.
298 assm->str(src.low_gp(), MemOperand(dst.rn(), dst.offset()));
299 assm->str(
300 src.high_gp(),
302 break;
303 case kF32:
304 assm->vstr(liftoff::GetFloatRegister(src.fp()), dst);
305 break;
306 case kF64:
307 assm->vstr(src.fp(), dst);
308 break;
309 case kS128: {
310 UseScratchRegisterScope temps(assm);
311 Register addr = liftoff::CalculateActualAddress(assm, &temps, dst.rn(),
312 no_reg, dst.offset());
313 assm->vst1(Neon8, NeonListOperand(src.low_fp(), 2), NeonMemOperand(addr));
314 break;
315 }
316 default:
317 UNREACHABLE();
318 }
319}
320
322 ValueKind kind) {
323 switch (kind) {
324 case kI16:
325 assm->ldrh(dst.gp(), src);
326 break;
327 case kI32:
328 case kRefNull:
329 case kRef:
330 assm->ldr(dst.gp(), src);
331 break;
332 case kI64:
333 assm->ldr(dst.low_gp(), MemOperand(src.rn(), src.offset()));
334 assm->ldr(
335 dst.high_gp(),
336 MemOperand(src.rn(), src.offset() + liftoff::kHalfStackSlotSize));
337 break;
338 case kF32:
339 assm->vldr(liftoff::GetFloatRegister(dst.fp()), src);
340 break;
341 case kF64:
342 assm->vldr(dst.fp(), src);
343 break;
344 case kS128: {
345 // Get memory address of slot to fill from.
346 UseScratchRegisterScope temps(assm);
347 Register addr = liftoff::CalculateActualAddress(assm, &temps, src.rn(),
348 no_reg, src.offset());
349 assm->vld1(Neon8, NeonListOperand(dst.low_fp(), 2), NeonMemOperand(addr));
350 break;
351 }
352 default:
353 UNREACHABLE();
354 }
355}
356
358 switch (dt) {
359 case NeonS8:
360 case NeonU8:
361 return 7;
362 case NeonS16:
363 case NeonU16:
364 return 15;
365 case NeonS32:
366 case NeonU32:
367 return 31;
368 case NeonS64:
369 case NeonU64:
370 return 63;
371 default:
372 UNREACHABLE();
373 return 0;
374 }
375}
376
378
379template <ShiftDirection dir = kLeft, NeonDataType dt, NeonSize sz>
382 constexpr int mask = MaskFromNeonDataType(dt);
383 UseScratchRegisterScope temps(assm);
384 QwNeonRegister tmp = temps.AcquireQ();
385 Register shift = temps.Acquire();
386 assm->and_(shift, rhs.gp(), Operand(mask));
387 assm->vdup(sz, tmp, shift);
388 if (dir == kRight) {
389 assm->vneg(sz, tmp, tmp);
390 }
391 assm->vshl(dt, liftoff::GetSimd128Register(dst),
393}
394
395template <ShiftDirection dir, NeonDataType dt>
397 LiftoffRegister lhs, int32_t rhs) {
398 // vshr by 0 is not allowed, so check for it, and only move if dst != lhs.
399 int32_t shift = rhs & MaskFromNeonDataType(dt);
400 if (shift) {
401 if (dir == kLeft) {
402 assm->vshl(dt, liftoff::GetSimd128Register(dst),
403 liftoff::GetSimd128Register(lhs), shift);
404 } else {
405 assm->vshr(dt, liftoff::GetSimd128Register(dst),
406 liftoff::GetSimd128Register(lhs), shift);
407 }
408 } else if (dst != lhs) {
411 }
412}
413
415 LiftoffRegister src) {
416 UseScratchRegisterScope temps(assm);
417 DwVfpRegister scratch = temps.AcquireD();
418 assm->vpmax(NeonU32, scratch, src.low_fp(), src.high_fp());
419 assm->vpmax(NeonU32, scratch, scratch, scratch);
420 assm->ExtractLane(dst.gp(), scratch, NeonS32, 0);
421 assm->cmp(dst.gp(), Operand(0));
422 assm->mov(dst.gp(), Operand(1), LeaveCC, ne);
423}
424
425class CacheStatePreservingTempRegisters {
426 public:
428 LiftoffRegList pinned = {})
429 : assm_(assm), pinned_(pinned) {}
430
432 for (Register reg : must_pop_) {
433 assm_->Pop(reg);
434 }
435 }
436
438 if (assm_->cache_state()->has_unused_register(kGpReg, pinned_)) {
439 return pinned_.set(
440 assm_->cache_state()->unused_register(kGpReg, pinned_).gp());
441 }
442
443 RegList available =
444 kLiftoffAssemblerGpCacheRegs - pinned_.GetGpList() - must_pop_;
445 DCHECK(!available.is_empty());
446 // Use {last()} here so we can just iterate forwards in the destructor.
447 Register reg = available.last();
448 assm_->Push(reg);
449 must_pop_.set(reg);
450 return reg;
451 }
452
453 private:
457};
458
459} // namespace liftoff
460
462 if (!CpuFeatures::IsSupported(ARMv7)) {
463 bailout(kUnsupportedArchitecture, "Liftoff needs ARMv7");
464 return 0;
465 }
466 uint32_t offset = static_cast<uint32_t>(pc_offset());
467 // PatchPrepareStackFrame will patch this in order to increase the stack
468 // appropriately. Additional nops are required as the bytes operand might
469 // require extra moves to encode.
470 for (int i = 0; i < liftoff::kPatchInstructionsRequired; i++) {
471 nop();
472 }
474 pc_offset());
475 return offset;
476}
477
479// The standard library used by gcc tryjobs does not consider `std::find` to be
480// `constexpr`, so wrap it in a `#ifdef __clang__` block.
481#ifdef __clang__
482 static_assert(std::find(std::begin(wasm::kGpParamRegisters),
483 std::end(wasm::kGpParamRegisters),
484 kLiftoffFrameSetupFunctionReg) ==
485 std::end(wasm::kGpParamRegisters));
486#endif
487
488 // On ARM, we must push at least {lr} before calling the stub, otherwise
489 // it would get clobbered with no possibility to recover it.
490 Register scratch = r7;
491 mov(scratch, Operand(StackFrame::TypeToMarker(StackFrame::WASM)));
492 PushCommonFrame(scratch);
493 LoadConstant(LiftoffRegister(kLiftoffFrameSetupFunctionReg),
495 CallBuiltin(Builtin::kWasmLiftoffFrameSetup);
496}
497
498void LiftoffAssembler::PrepareTailCall(int num_callee_stack_params,
499 int stack_param_delta) {
500 {
501 UseScratchRegisterScope temps(this);
502 Register scratch = temps.Acquire();
503
504 // Push the return address and frame pointer to complete the stack frame.
505 sub(sp, sp, Operand(8));
506 ldr(scratch, MemOperand(fp, 4));
507 str(scratch, MemOperand(sp, 4));
508 ldr(scratch, MemOperand(fp, 0));
509 str(scratch, MemOperand(sp, 0));
510
511 // Shift the whole frame upwards.
512 int slot_count = num_callee_stack_params + 2;
513 for (int i = slot_count - 1; i >= 0; --i) {
514 ldr(scratch, MemOperand(sp, i * 4));
515 str(scratch, MemOperand(fp, (i - stack_param_delta) * 4));
516 }
517 }
518
519 // Set the new stack and frame pointer.
520 sub(sp, fp, Operand(stack_param_delta * 4));
521 Pop(lr, fp);
522}
523
525
527 int offset, SafepointTableBuilder* safepoint_table_builder,
528 bool feedback_vector_slot, size_t stack_param_slots) {
529 // The frame_size includes the frame marker and the instance slot. Both are
530 // pushed as part of frame construction, so we don't need to allocate memory
531 // for them anymore.
532 int frame_size = GetTotalFrameSize() - 2 * kSystemPointerSize;
533 // The frame setup builtin also pushes the feedback vector.
534 if (feedback_vector_slot) {
535 frame_size -= kSystemPointerSize;
536 }
537
538 PatchingAssembler patching_assembler(AssemblerOptions{},
541 if (V8_LIKELY(frame_size < 4 * KB)) {
542 // This is the standard case for small frames: just subtract from SP and be
543 // done with it.
544 patching_assembler.sub(sp, sp, Operand(frame_size));
545 patching_assembler.PadWithNops();
546 return;
547 }
548
549 // The frame size is bigger than 4KB, so we might overflow the available stack
550 // space if we first allocate the frame and then do the stack check (we will
551 // need some remaining stack space for throwing the exception). That's why we
552 // check the available stack space before we allocate the frame. To do this we
553 // replace the {__ sub(sp, sp, framesize)} with a jump to OOL code that does
554 // this "extended stack check".
555 //
556 // The OOL code can simply be generated here with the normal assembler,
557 // because all other code generation, including OOL code, has already finished
558 // when {PatchPrepareStackFrame} is called. The function prologue then jumps
559 // to the current {pc_offset()} to execute the OOL code for allocating the
560 // large frame.
561
562 // Emit the unconditional branch in the function prologue (from {offset} to
563 // {pc_offset()}).
564 patching_assembler.b(pc_offset() - offset - Instruction::kPcLoadDelta);
565 patching_assembler.PadWithNops();
566
567 // If the frame is bigger than the stack, we throw the stack overflow
568 // exception unconditionally. Thereby we can avoid the integer overflow
569 // check in the condition code.
570 RecordComment("OOL: stack check for large frame");
572 if (frame_size < v8_flags.stack_size * 1024) {
573 UseScratchRegisterScope temps(this);
574 Register stack_limit = temps.Acquire();
576 add(stack_limit, stack_limit, Operand(frame_size));
577 cmp(sp, stack_limit);
578 b(cs /* higher or same */, &continuation);
579 }
580
581 if (v8_flags.experimental_wasm_growable_stacks) {
584 regs_to_save.set(WasmHandleStackOverflowDescriptor::FrameBaseRegister());
585 for (auto reg : kGpParamRegisters) regs_to_save.set(reg);
586 for (auto reg : kFpParamRegisters) regs_to_save.set(reg);
589 add(WasmHandleStackOverflowDescriptor::FrameBaseRegister(), fp,
590 Operand(stack_param_slots * kStackSlotSize +
592 CallBuiltin(Builtin::kWasmHandleStackOverflow);
593 safepoint_table_builder->DefineSafepoint(this);
595 } else {
596 Call(static_cast<Address>(Builtin::kWasmStackOverflow),
598 // The call will not return; just define an empty safepoint.
599 safepoint_table_builder->DefineSafepoint(this);
600 if (v8_flags.debug_code) stop();
601 }
602
604
605 // Now allocate the stack space. Note that this might do more than just
606 // decrementing the SP; consult {MacroAssembler::AllocateStackSpace}.
607 AllocateStackSpace(frame_size);
608
609 // Jump back to the start of the function, from {pc_offset()} to
610 // right after the reserved space for the {__ sub(sp, sp, framesize)} (which
611 // is a branch now).
612 int func_start_offset =
614 b(func_start_offset - pc_offset() - Instruction::kPcLoadDelta);
615}
616
618
620
621// static
625
627 switch (kind) {
628 case kS128:
629 return value_kind_size(kind);
630 default:
631 return kStackSlotSize;
632 }
633}
634
638
639void LiftoffAssembler::CheckTierUp(int declared_func_index, int budget_used,
640 Label* ool_label,
641 const FreezeCacheState& frozen) {
642 {
644 Register budget_array = temps.Acquire();
645
647 if (instance_data == no_reg) {
648 instance_data = budget_array; // Reuse the temp register.
650 }
651
652 constexpr int kArrayOffset = wasm::ObjectAccess::ToTagged(
653 WasmTrustedInstanceData::kTieringBudgetArrayOffset);
654 ldr(budget_array, MemOperand{instance_data, kArrayOffset});
655
656 int budget_arr_offset = kInt32Size * declared_func_index;
657 // If the offset cannot be used in the operand directly, add it once to the
658 // budget array to avoid doing this multiple times below.
659 if (!ImmediateFitsAddrMode2Instruction(budget_arr_offset)) {
660 add(budget_array, budget_array, Operand{budget_arr_offset});
661 budget_arr_offset = 0;
662 }
663
664 Register budget = temps.Acquire();
665 MemOperand budget_addr{budget_array, budget_arr_offset};
666 ldr(budget, budget_addr);
667 sub(budget, budget, Operand{budget_used}, SetCC);
668 str(budget, budget_addr);
669 }
670 b(ool_label, mi);
671}
672
674 if (!v8_flags.experimental_wasm_growable_stacks) {
675 return fp;
676 }
678 Label done, call_runtime;
680 cmp(old_fp.gp(),
681 Operand(StackFrame::TypeToMarker(StackFrame::WASM_SEGMENT_START)));
682 b(&call_runtime, eq);
683 mov(old_fp.gp(), fp);
684 jmp(&done);
685
686 bind(&call_runtime);
691 CallCFunction(ExternalReference::wasm_load_old_fp(), 1);
692 if (old_fp.gp() != kReturnRegister0) {
693 mov(old_fp.gp(), kReturnRegister0);
694 }
696
697 bind(&done);
698 return old_fp.gp();
699}
700
702 {
703 UseScratchRegisterScope temps{this};
704 Register scratch = temps.Acquire();
706 cmp(scratch,
707 Operand(StackFrame::TypeToMarker(StackFrame::WASM_SEGMENT_START)));
708 }
709 Label done;
710 b(&done, ne);
713 for (auto reg : kFpReturnRegisters) regs_to_save.set(reg);
717 CallCFunction(ExternalReference::wasm_shrink_stack(), 1);
718 // Restore old FP. We don't need to restore old SP explicitly, because
719 // it will be restored from FP in LeaveFrame before return.
722 bind(&done);
723}
724
726 switch (value.type().kind()) {
727 case kI32:
728 MacroAssembler::Move(reg.gp(), Operand(value.to_i32()));
729 break;
730 case kI64: {
731 int32_t low_word = value.to_i64();
732 int32_t high_word = value.to_i64() >> 32;
733 MacroAssembler::Move(reg.low_gp(), Operand(low_word));
734 MacroAssembler::Move(reg.high_gp(), Operand(high_word));
735 break;
736 }
737 case kF32:
738 vmov(liftoff::GetFloatRegister(reg.fp()), value.to_f32_boxed());
739 break;
740 case kF64: {
741 Register extra_scratch = GetUnusedRegister(kGpReg, {}).gp();
742 vmov(reg.fp(), base::Double(value.to_f64_boxed().get_bits()),
743 extra_scratch);
744 break;
745 }
746 default:
747 UNREACHABLE();
748 }
749}
750
754
756 int offset, IndirectPointerTag tag) {
757 static_assert(!V8_ENABLE_SANDBOX_BOOL);
758 static_assert(!COMPRESS_POINTERS_BOOL);
759 ldr(dst, MemOperand{src_addr, offset});
760}
761
763 int offset, int size) {
764 DCHECK_LE(0, offset);
765 MemOperand src{instance, offset};
766 switch (size) {
767 case 1:
768 ldrb(dst, src);
769 break;
770 case 4:
771 ldr(dst, src);
772 break;
773 default:
775 }
776}
777
779 Register instance,
780 int offset) {
781 static_assert(kTaggedSize == kSystemPointerSize);
782 ldr(dst, MemOperand{instance, offset});
783}
784
788
790
791namespace liftoff {
792#define __ lasm->
794 Register src_addr, Register offset_reg,
795 int32_t offset_imm, LoadType type,
796 uint32_t* protected_load_pc = nullptr,
797 bool needs_shift = false) {
798 unsigned shift_amount = needs_shift ? type.size_log_2() : 0;
799 DCHECK_IMPLIES(type.value_type() == kWasmI64, dst.is_gp_pair());
800 UseScratchRegisterScope temps(lasm);
801 if (type.value() == LoadType::kF64Load ||
802 type.value() == LoadType::kF32Load ||
803 type.value() == LoadType::kS128Load) {
804 // Remove the DCHECK and implement scaled offsets for these types if needed.
805 // For now this path is never used.
806 DCHECK(!needs_shift);
808 lasm, &temps, src_addr, offset_reg, offset_imm);
809 if (type.value() == LoadType::kF64Load) {
810 // Armv6 is not supported so Neon can be used to avoid alignment issues.
811 CpuFeatureScope scope(lasm, NEON);
812 __ vld1(Neon64, NeonListOperand(dst.fp()),
813 NeonMemOperand(actual_src_addr));
814 } else if (type.value() == LoadType::kF32Load) {
815 // TODO(arm): Use vld1 for f32 when implemented in simulator as used for
816 // f64. It supports unaligned access.
817 Register scratch =
818 (actual_src_addr == src_addr) ? temps.Acquire() : actual_src_addr;
819 __ ldr(scratch, MemOperand(actual_src_addr));
820 __ vmov(liftoff::GetFloatRegister(dst.fp()), scratch);
821 } else {
822 // Armv6 is not supported so Neon can be used to avoid alignment issues.
823 CpuFeatureScope scope(lasm, NEON);
824 __ vld1(Neon8, NeonListOperand(dst.low_fp(), 2),
825 NeonMemOperand(actual_src_addr));
826 }
827 } else {
828 MemOperand src_op = liftoff::GetMemOp(lasm, &temps, src_addr, offset_reg,
829 offset_imm, shift_amount);
830 if (protected_load_pc) *protected_load_pc = __ pc_offset();
831 switch (type.value()) {
832 case LoadType::kI32Load8U:
833 __ ldrb(dst.gp(), src_op);
834 break;
835 case LoadType::kI64Load8U:
836 __ ldrb(dst.low_gp(), src_op);
837 __ mov(dst.high_gp(), Operand(0));
838 break;
839 case LoadType::kI32Load8S:
840 __ ldrsb(dst.gp(), src_op);
841 break;
842 case LoadType::kI64Load8S:
843 __ ldrsb(dst.low_gp(), src_op);
844 __ asr(dst.high_gp(), dst.low_gp(), Operand(31));
845 break;
846 case LoadType::kI32Load16U:
847 __ ldrh(dst.gp(), src_op);
848 break;
849 case LoadType::kI64Load16U:
850 __ ldrh(dst.low_gp(), src_op);
851 __ mov(dst.high_gp(), Operand(0));
852 break;
853 case LoadType::kI32Load16S:
854 __ ldrsh(dst.gp(), src_op);
855 break;
856 case LoadType::kI32Load:
857 __ ldr(dst.gp(), src_op);
858 break;
859 case LoadType::kI64Load16S:
860 __ ldrsh(dst.low_gp(), src_op);
861 __ asr(dst.high_gp(), dst.low_gp(), Operand(31));
862 break;
863 case LoadType::kI64Load32U:
864 __ ldr(dst.low_gp(), src_op);
865 __ mov(dst.high_gp(), Operand(0));
866 break;
867 case LoadType::kI64Load32S:
868 __ ldr(dst.low_gp(), src_op);
869 __ asr(dst.high_gp(), dst.low_gp(), Operand(31));
870 break;
871 case LoadType::kI64Load:
872 __ ldr(dst.low_gp(), src_op);
873 // GetMemOp may use a scratch register as the offset register, in which
874 // case, calling GetMemOp again will fail due to the assembler having
875 // ran out of scratch registers.
876 if (temps.CanAcquire()) {
877 src_op = liftoff::GetMemOp(lasm, &temps, src_addr, offset_reg,
878 offset_imm + kSystemPointerSize);
879 } else {
880 __ add(src_op.rm(), src_op.rm(), Operand(kSystemPointerSize));
881 }
882 __ ldr(dst.high_gp(), src_op);
883 break;
884 default:
885 UNREACHABLE();
886 }
887 }
888}
889#undef __
890} // namespace liftoff
891
893 Register offset_reg,
894 int32_t offset_imm,
895 uint32_t* protected_load_pc,
896 bool needs_shift) {
897 static_assert(kTaggedSize == kInt32Size);
898 liftoff::LoadInternal(this, LiftoffRegister(dst), src_addr, offset_reg,
899 offset_imm, LoadType::kI32Load, protected_load_pc,
900 needs_shift);
901}
902
904 int32_t offset) {
905 static_assert(!V8_ENABLE_SANDBOX_BOOL);
906 LoadTaggedPointer(dst, src_addr, no_reg, offset);
907}
908
910 int32_t offset_imm) {
911 UseScratchRegisterScope temps(this);
912 MemOperand src_op =
913 liftoff::GetMemOp(this, &temps, src_addr, no_reg, offset_imm);
914 ldr(dst, src_op);
915}
916
918 Register offset_reg,
919 int32_t offset_imm, Register src,
920 LiftoffRegList pinned,
921 uint32_t* protected_store_pc,
922 SkipWriteBarrier skip_write_barrier) {
923 static_assert(kTaggedSize == kInt32Size);
924 UseScratchRegisterScope temps{this};
925 Register actual_offset_reg = offset_reg;
926 if (offset_reg != no_reg && offset_imm != 0) {
927 if (cache_state()->is_used(LiftoffRegister(offset_reg))) {
928 // The code below only needs a scratch register if the {MemOperand} given
929 // to {str} has an offset outside the uint12 range. After doing the
930 // addition below we will not pass an immediate offset to {str} though, so
931 // we can use the scratch register here.
932 actual_offset_reg = temps.Acquire();
933 }
934 add(actual_offset_reg, offset_reg, Operand(offset_imm));
935 }
936 MemOperand dst_op = actual_offset_reg == no_reg
937 ? MemOperand(dst_addr, offset_imm)
938 : MemOperand(dst_addr, actual_offset_reg);
939
940 if (protected_store_pc) *protected_store_pc = pc_offset();
941
942 str(src, dst_op);
943
944 if (skip_write_barrier || v8_flags.disable_write_barriers) return;
945
946 // The write barrier.
947 Label exit;
949 kZero, &exit);
950 JumpIfSmi(src, &exit);
953 dst_addr,
954 actual_offset_reg == no_reg ? Operand(offset_imm)
955 : Operand(actual_offset_reg),
956 SaveFPRegsMode::kSave, StubCallMode::kCallWasmRuntimeStub);
957 bind(&exit);
958}
959
961 Register offset_reg, uint32_t offset_imm,
962 LoadType type, uint32_t* protected_load_pc,
963 bool /* is_load_mem */, bool /* i64_offset */,
964 bool needs_shift) {
965 // Offsets >=2GB are statically OOB on 32-bit systems.
966 DCHECK_LE(offset_imm, std::numeric_limits<int32_t>::max());
967 liftoff::LoadInternal(this, dst, src_addr, offset_reg,
968 static_cast<int32_t>(offset_imm), type,
969 protected_load_pc, needs_shift);
970}
971
972void LiftoffAssembler::Store(Register dst_addr, Register offset_reg,
973 uint32_t offset_imm, LiftoffRegister src,
974 StoreType type, LiftoffRegList pinned,
975 uint32_t* protected_store_pc,
976 bool /* is_store_mem */, bool /* i64_offset */) {
977 // Offsets >=2GB are statically OOB on 32-bit systems.
978 DCHECK_LE(offset_imm, std::numeric_limits<int32_t>::max());
979 UseScratchRegisterScope temps{this};
980 if (type.value() == StoreType::kF64Store) {
982 this, &temps, dst_addr, offset_reg, offset_imm);
983 // Armv6 is not supported so Neon can be used to avoid alignment issues.
984 CpuFeatureScope scope(this, NEON);
985 vst1(Neon64, NeonListOperand(src.fp()), NeonMemOperand(actual_dst_addr));
986 } else if (type.value() == StoreType::kS128Store) {
988 this, &temps, dst_addr, offset_reg, offset_imm);
989 // Armv6 is not supported so Neon can be used to avoid alignment issues.
990 CpuFeatureScope scope(this, NEON);
991 vst1(Neon8, NeonListOperand(src.low_fp(), 2),
992 NeonMemOperand(actual_dst_addr));
993 } else if (type.value() == StoreType::kF32Store) {
994 // TODO(arm): Use vst1 for f32 when implemented in simulator as used for
995 // f64. It supports unaligned access.
997 this, &temps, dst_addr, offset_reg, offset_imm);
998 liftoff::CacheStatePreservingTempRegisters liftoff_temps{this, pinned};
999 Register scratch =
1000 temps.CanAcquire() ? temps.Acquire() : liftoff_temps.Acquire();
1001 vmov(scratch, liftoff::GetFloatRegister(src.fp()));
1002 str(scratch, MemOperand(actual_dst_addr));
1003 } else {
1004 MemOperand dst_op =
1005 liftoff::GetMemOp(this, &temps, dst_addr, offset_reg, offset_imm);
1006 if (protected_store_pc) *protected_store_pc = pc_offset();
1007 switch (type.value()) {
1008 case StoreType::kI64Store8:
1009 src = src.low();
1010 [[fallthrough]];
1011 case StoreType::kI32Store8:
1012 strb(src.gp(), dst_op);
1013 break;
1014 case StoreType::kI64Store16:
1015 src = src.low();
1016 [[fallthrough]];
1017 case StoreType::kI32Store16:
1018 strh(src.gp(), dst_op);
1019 break;
1020 case StoreType::kI64Store32:
1021 src = src.low();
1022 [[fallthrough]];
1023 case StoreType::kI32Store:
1024 str(src.gp(), dst_op);
1025 break;
1026 case StoreType::kI64Store:
1027 str(src.low_gp(), dst_op);
1028 // GetMemOp may use a scratch register as the offset register, in which
1029 // case, calling GetMemOp again will fail due to the assembler having
1030 // ran out of scratch registers.
1031 if (temps.CanAcquire()) {
1032 dst_op = liftoff::GetMemOp(this, &temps, dst_addr, offset_reg,
1033 offset_imm + kSystemPointerSize);
1034 } else {
1035 add(dst_op.rm(), dst_op.rm(), Operand(kSystemPointerSize));
1036 }
1037 str(src.high_gp(), dst_op);
1038 break;
1039 default:
1040 UNREACHABLE();
1041 }
1042 }
1043}
1044
1045namespace liftoff {
1046#define __ lasm->
1047
1048inline void AtomicOp32(
1049 LiftoffAssembler* lasm, Register dst_addr, Register offset_reg,
1050 uint32_t offset_imm, LiftoffRegister value, LiftoffRegister result,
1051 LiftoffRegList pinned,
1052 void (Assembler::*load)(Register, Register, Condition),
1053 void (Assembler::*store)(Register, Register, Register, Condition),
1054 void (*op)(LiftoffAssembler*, Register, Register, Register)) {
1055 Register store_result = pinned.set(__ GetUnusedRegister(kGpReg, pinned)).gp();
1056
1057 // Allocate an additional {temp} register to hold the result that should be
1058 // stored to memory. Note that {temp} and {store_result} are not allowed to be
1059 // the same register.
1060 Register temp = pinned.set(__ GetUnusedRegister(kGpReg, pinned)).gp();
1061
1062 // {LiftoffCompiler::AtomicBinop} ensures that {result} is unique.
1063 DCHECK(result.gp() != value.gp() && result.gp() != dst_addr &&
1064 result.gp() != offset_reg);
1065
1066 UseScratchRegisterScope temps(lasm);
1068 lasm, &temps, dst_addr, offset_reg, offset_imm);
1069
1070 __ dmb(ISH);
1071 Label retry;
1072 __ bind(&retry);
1073 (lasm->*load)(result.gp(), actual_addr, al);
1074 op(lasm, temp, result.gp(), value.gp());
1075 (lasm->*store)(store_result, temp, actual_addr, al);
1076 __ cmp(store_result, Operand(0));
1077 __ b(ne, &retry);
1078 __ dmb(ISH);
1079}
1080
1081inline void Add(LiftoffAssembler* lasm, Register dst, Register lhs,
1082 Register rhs) {
1083 __ add(dst, lhs, rhs);
1084}
1085
1086inline void Sub(LiftoffAssembler* lasm, Register dst, Register lhs,
1087 Register rhs) {
1088 __ sub(dst, lhs, rhs);
1089}
1090
1091inline void And(LiftoffAssembler* lasm, Register dst, Register lhs,
1092 Register rhs) {
1093 __ and_(dst, lhs, rhs);
1094}
1095
1096inline void Or(LiftoffAssembler* lasm, Register dst, Register lhs,
1097 Register rhs) {
1098 __ orr(dst, lhs, rhs);
1099}
1100
1101inline void Xor(LiftoffAssembler* lasm, Register dst, Register lhs,
1102 Register rhs) {
1103 __ eor(dst, lhs, rhs);
1104}
1105
1106inline void Exchange(LiftoffAssembler* lasm, Register dst, Register lhs,
1107 Register rhs) {
1108 __ mov(dst, rhs);
1109}
1110
1111inline void AtomicBinop32(LiftoffAssembler* lasm, Register dst_addr,
1112 Register offset_reg, uint32_t offset_imm,
1114 StoreType type,
1115 void (*op)(LiftoffAssembler*, Register, Register,
1116 Register)) {
1117 LiftoffRegList pinned{dst_addr, value, result};
1118 if (offset_reg != no_reg) pinned.set(offset_reg);
1119 switch (type.value()) {
1120 case StoreType::kI64Store8:
1121 __ LoadConstant(result.high(), WasmValue(0));
1122 result = result.low();
1123 value = value.low();
1124 [[fallthrough]];
1125 case StoreType::kI32Store8:
1126 liftoff::AtomicOp32(lasm, dst_addr, offset_reg, offset_imm, value, result,
1127 pinned, &Assembler::ldrexb, &Assembler::strexb, op);
1128 return;
1129 case StoreType::kI64Store16:
1130 __ LoadConstant(result.high(), WasmValue(0));
1131 result = result.low();
1132 value = value.low();
1133 [[fallthrough]];
1134 case StoreType::kI32Store16:
1135 liftoff::AtomicOp32(lasm, dst_addr, offset_reg, offset_imm, value, result,
1136 pinned, &Assembler::ldrexh, &Assembler::strexh, op);
1137 return;
1138 case StoreType::kI64Store32:
1139 __ LoadConstant(result.high(), WasmValue(0));
1140 result = result.low();
1141 value = value.low();
1142 [[fallthrough]];
1143 case StoreType::kI32Store:
1144 liftoff::AtomicOp32(lasm, dst_addr, offset_reg, offset_imm, value, result,
1145 pinned, &Assembler::ldrex, &Assembler::strex, op);
1146 return;
1147 default:
1148 UNREACHABLE();
1149 }
1150}
1151
1152inline void AtomicOp64(LiftoffAssembler* lasm, Register dst_addr,
1153 Register offset_reg, uint32_t offset_imm,
1154 LiftoffRegister value,
1155 std::optional<LiftoffRegister> result,
1156 void (*op)(LiftoffAssembler*, LiftoffRegister,
1158 // strexd loads a 64 bit word into two registers. The first register needs
1159 // to have an even index, e.g. r8, the second register needs to be the one
1160 // with the next higher index, e.g. r9 if the first register is r8. In the
1161 // following code we use the fixed register pair r8/r9 to make the code here
1162 // simpler, even though other register pairs would also be possible.
1163 constexpr Register dst_low = r8;
1164 constexpr Register dst_high = r9;
1165
1166 // Make sure {dst_low} and {dst_high} are not occupied by any other value.
1167 Register value_low = value.low_gp();
1168 Register value_high = value.high_gp();
1169 LiftoffRegList pinned{dst_low, dst_high};
1170 auto regs_to_check = {&dst_addr, &offset_reg, &value_low, &value_high};
1171 auto re_pin = [regs_to_check, &pinned] {
1172 for (auto* reg : regs_to_check) {
1173 if (*reg != no_reg) pinned.set(*reg);
1174 }
1175 };
1176 re_pin();
1177 __ ClearRegister(dst_low, regs_to_check, pinned);
1178 re_pin();
1179 __ ClearRegister(dst_high, regs_to_check, pinned);
1180 re_pin();
1181
1182 // Make sure that {result}, if it exists, also does not overlap with
1183 // {dst_low} and {dst_high}. We don't have to transfer the value stored in
1184 // {result}.
1185 Register result_low = no_reg;
1186 Register result_high = no_reg;
1187 if (result.has_value()) {
1188 result_low = result.value().low_gp();
1189 if (pinned.has(result_low)) {
1190 result_low = __ GetUnusedRegister(kGpReg, pinned).gp();
1191 }
1192 pinned.set(result_low);
1193
1194 result_high = result.value().high_gp();
1195 if (pinned.has(result_high)) {
1196 result_high = __ GetUnusedRegister(kGpReg, pinned).gp();
1197 }
1198 pinned.set(result_high);
1199 }
1200
1201 Register store_result = __ GetUnusedRegister(kGpReg, pinned).gp();
1202
1203 UseScratchRegisterScope temps(lasm);
1205 lasm, &temps, dst_addr, offset_reg, offset_imm);
1206
1207 __ dmb(ISH);
1208 Label retry;
1209 __ bind(&retry);
1210 // {ldrexd} is needed here so that the {strexd} instruction below can
1211 // succeed. We don't need the value we are reading. We use {dst_low} and
1212 // {dst_high} as the destination registers because {ldrexd} has the same
1213 // restrictions on registers as {strexd}, see the comment above.
1214 __ ldrexd(dst_low, dst_high, actual_addr);
1215 if (result.has_value()) {
1216 __ mov(result_low, dst_low);
1217 __ mov(result_high, dst_high);
1218 }
1219 op(lasm, LiftoffRegister::ForPair(dst_low, dst_high),
1220 LiftoffRegister::ForPair(dst_low, dst_high),
1221 LiftoffRegister::ForPair(value_low, value_high));
1222 __ strexd(store_result, dst_low, dst_high, actual_addr);
1223 __ cmp(store_result, Operand(0));
1224 __ b(ne, &retry);
1225 __ dmb(ISH);
1226
1227 if (result.has_value()) {
1228 if (result_low != result.value().low_gp()) {
1229 __ mov(result.value().low_gp(), result_low);
1230 }
1231 if (result_high != result.value().high_gp()) {
1232 __ mov(result.value().high_gp(), result_high);
1233 }
1234 }
1235}
1236
1239 __ mov(dst.low_gp(), src.low_gp());
1240 __ mov(dst.high_gp(), src.high_gp());
1241}
1242
1243#undef __
1244} // namespace liftoff
1245
1247 Register offset_reg, uint32_t offset_imm,
1248 LoadType type, LiftoffRegList /* pinned */,
1249 bool /* i64_offset */) {
1250 if (type.value() != LoadType::kI64Load) {
1251 Load(dst, src_addr, offset_reg, offset_imm, type, nullptr, true);
1252 dmb(ISH);
1253 return;
1254 }
1255 // ldrexd loads a 64 bit word into two registers. The first register needs to
1256 // have an even index, e.g. r8, the second register needs to be the one with
1257 // the next higher index, e.g. r9 if the first register is r8. In the
1258 // following code we use the fixed register pair r8/r9 to make the code here
1259 // simpler, even though other register pairs would also be possible.
1260 constexpr Register dst_low = r8;
1261 constexpr Register dst_high = r9;
1262 SpillRegisters(dst_low, dst_high);
1263 {
1264 UseScratchRegisterScope temps(this);
1266 this, &temps, src_addr, offset_reg, offset_imm);
1267 ldrexd(dst_low, dst_high, actual_addr);
1268 dmb(ISH);
1269 }
1270
1272 {{dst, LiftoffRegister::ForPair(dst_low, dst_high), kI64}});
1273}
1274
1276 uint32_t offset_imm, LiftoffRegister src,
1277 StoreType type, LiftoffRegList pinned,
1278 bool /* i64_offset */) {
1279 if (type.value() == StoreType::kI64Store) {
1280 liftoff::AtomicOp64(this, dst_addr, offset_reg, offset_imm, src, {},
1282 return;
1283 }
1284
1285 dmb(ISH);
1286 Store(dst_addr, offset_reg, offset_imm, src, type, pinned, nullptr, true);
1287 dmb(ISH);
1288 return;
1289}
1290
1292 uint32_t offset_imm, LiftoffRegister value,
1294 bool /* i64_offset */) {
1295 if (type.value() == StoreType::kI64Store) {
1296 liftoff::AtomicOp64(this, dst_addr, offset_reg, offset_imm, value, {result},
1298 return;
1299 }
1300 liftoff::AtomicBinop32(this, dst_addr, offset_reg, offset_imm, value, result,
1301 type, &liftoff::Add);
1302}
1303
1305 uint32_t offset_imm, LiftoffRegister value,
1307 bool /* i64_offset */) {
1308 if (type.value() == StoreType::kI64Store) {
1309 liftoff::AtomicOp64(this, dst_addr, offset_reg, offset_imm, value, {result},
1311 return;
1312 }
1313 liftoff::AtomicBinop32(this, dst_addr, offset_reg, offset_imm, value, result,
1314 type, &liftoff::Sub);
1315}
1316
1318 uint32_t offset_imm, LiftoffRegister value,
1320 bool /* i64_offset */) {
1321 if (type.value() == StoreType::kI64Store) {
1322 liftoff::AtomicOp64(this, dst_addr, offset_reg, offset_imm, value, {result},
1324 return;
1325 }
1326 liftoff::AtomicBinop32(this, dst_addr, offset_reg, offset_imm, value, result,
1327 type, &liftoff::And);
1328}
1329
1331 uint32_t offset_imm, LiftoffRegister value,
1333 bool /* i64_offset */) {
1334 if (type.value() == StoreType::kI64Store) {
1335 liftoff::AtomicOp64(this, dst_addr, offset_reg, offset_imm, value, {result},
1337 return;
1338 }
1339 liftoff::AtomicBinop32(this, dst_addr, offset_reg, offset_imm, value, result,
1340 type, &liftoff::Or);
1341}
1342
1344 uint32_t offset_imm, LiftoffRegister value,
1346 bool /* i64_offset */) {
1347 if (type.value() == StoreType::kI64Store) {
1348 liftoff::AtomicOp64(this, dst_addr, offset_reg, offset_imm, value, {result},
1350 return;
1351 }
1352 liftoff::AtomicBinop32(this, dst_addr, offset_reg, offset_imm, value, result,
1353 type, &liftoff::Xor);
1354}
1355
1357 uint32_t offset_imm,
1358 LiftoffRegister value,
1360 bool /* i64_offset */) {
1361 if (type.value() == StoreType::kI64Store) {
1362 liftoff::AtomicOp64(this, dst_addr, offset_reg, offset_imm, value, {result},
1364 return;
1365 }
1366 liftoff::AtomicBinop32(this, dst_addr, offset_reg, offset_imm, value, result,
1367 type, &liftoff::Exchange);
1368}
1369
1370namespace liftoff {
1371#define __ lasm->
1372
1374 Register dst_addr_reg, Register offset_reg,
1375 uint32_t offset_imm,
1376 LiftoffRegister expected,
1377 LiftoffRegister new_value,
1379 // To implement I64AtomicCompareExchange, we nearly need all registers, with
1380 // some registers having special constraints, e.g. like for {new_value} and
1381 // {result} the low-word register has to have an even register code, and the
1382 // high-word has to be in the next higher register. To avoid complicated
1383 // register allocation code here, we just assign fixed registers to all
1384 // values here, and then move all values into the correct register.
1385 Register dst_addr = r0;
1386 Register offset = r1;
1387 Register result_low = r4;
1388 Register result_high = r5;
1389 Register new_value_low = r2;
1390 Register new_value_high = r3;
1391 Register store_result = r6;
1392 Register expected_low = r8;
1393 Register expected_high = r9;
1394
1395 // We spill all registers, so that we can re-assign them afterwards.
1396 __ SpillRegisters(dst_addr, offset, result_low, result_high, new_value_low,
1397 new_value_high, store_result, expected_low, expected_high);
1398
1399 __ ParallelRegisterMove(
1400 {{LiftoffRegister::ForPair(new_value_low, new_value_high), new_value,
1401 kI64},
1402 {LiftoffRegister::ForPair(expected_low, expected_high), expected, kI64},
1403 {dst_addr, dst_addr_reg, kI32},
1404 {offset, offset_reg != no_reg ? offset_reg : offset, kI32}});
1405
1406 {
1407 UseScratchRegisterScope temps(lasm);
1408 [[maybe_unused]] Register temp = liftoff::CalculateActualAddress(
1409 lasm, &temps, dst_addr, offset_reg == no_reg ? no_reg : offset,
1410 offset_imm, dst_addr);
1411 // Make sure the actual address is stored in the right register.
1412 DCHECK_EQ(dst_addr, temp);
1413 }
1414
1415 Label retry;
1416 Label done;
1417 __ dmb(ISH);
1418 __ bind(&retry);
1419 __ ldrexd(result_low, result_high, dst_addr);
1420 __ cmp(result_low, expected_low);
1421 __ b(ne, &done);
1422 __ cmp(result_high, expected_high);
1423 __ b(ne, &done);
1424 __ strexd(store_result, new_value_low, new_value_high, dst_addr);
1425 __ cmp(store_result, Operand(0));
1426 __ b(ne, &retry);
1427 __ dmb(ISH);
1428 __ bind(&done);
1429
1430 __ ParallelRegisterMove(
1431 {{result, LiftoffRegister::ForPair(result_low, result_high), kI64}});
1432}
1433#undef __
1434} // namespace liftoff
1435
1437 Register dst_addr, Register offset_reg, uint32_t offset_imm,
1439 StoreType type, bool /* i64_offset */) {
1440 if (type.value() == StoreType::kI64Store) {
1441 liftoff::AtomicI64CompareExchange(this, dst_addr, offset_reg, offset_imm,
1442 expected, new_value, result);
1443 return;
1444 }
1445
1446 // The other versions of CompareExchange can share code, but need special load
1447 // and store instructions.
1448 void (Assembler::*load)(Register, Register, Condition) = nullptr;
1449 void (Assembler::*store)(Register, Register, Register, Condition) = nullptr;
1450
1451 LiftoffRegList pinned{dst_addr};
1452 if (offset_reg != no_reg) pinned.set(offset_reg);
1453 // We need to remember the high word of {result}, so we can set it to zero in
1454 // the end if necessary.
1455 Register result_high = no_reg;
1456 switch (type.value()) {
1457 case StoreType::kI64Store8:
1458 result_high = result.high_gp();
1459 result = result.low();
1460 new_value = new_value.low();
1461 expected = expected.low();
1462 [[fallthrough]];
1463 case StoreType::kI32Store8:
1464 load = &Assembler::ldrexb;
1465 store = &Assembler::strexb;
1466 // We have to clear the high bits of {expected}, as we can only do a
1467 // 32-bit comparison. If the {expected} register is used, we spill it
1468 // first.
1469 if (cache_state()->is_used(expected)) {
1470 SpillRegister(expected);
1471 }
1472 uxtb(expected.gp(), expected.gp());
1473 break;
1474 case StoreType::kI64Store16:
1475 result_high = result.high_gp();
1476 result = result.low();
1477 new_value = new_value.low();
1478 expected = expected.low();
1479 [[fallthrough]];
1480 case StoreType::kI32Store16:
1481 load = &Assembler::ldrexh;
1482 store = &Assembler::strexh;
1483 // We have to clear the high bits of {expected}, as we can only do a
1484 // 32-bit comparison. If the {expected} register is used, we spill it
1485 // first.
1486 if (cache_state()->is_used(expected)) {
1487 SpillRegister(expected);
1488 }
1489 uxth(expected.gp(), expected.gp());
1490 break;
1491 case StoreType::kI64Store32:
1492 result_high = result.high_gp();
1493 result = result.low();
1494 new_value = new_value.low();
1495 expected = expected.low();
1496 [[fallthrough]];
1497 case StoreType::kI32Store:
1498 load = &Assembler::ldrex;
1499 store = &Assembler::strex;
1500 break;
1501 default:
1502 UNREACHABLE();
1503 }
1504 pinned.set(new_value);
1505 pinned.set(expected);
1506
1507 Register result_reg = result.gp();
1508 if (pinned.has(result)) {
1509 result_reg = GetUnusedRegister(kGpReg, pinned).gp();
1510 }
1511 pinned.set(LiftoffRegister(result));
1512 Register store_result = GetUnusedRegister(kGpReg, pinned).gp();
1513
1514 UseScratchRegisterScope temps(this);
1516 this, &temps, dst_addr, offset_reg, offset_imm);
1517
1518 Label retry;
1519 Label done;
1520 dmb(ISH);
1521 bind(&retry);
1522 (this->*load)(result.gp(), actual_addr, al);
1523 cmp(result.gp(), expected.gp());
1524 b(ne, &done);
1525 (this->*store)(store_result, new_value.gp(), actual_addr, al);
1526 cmp(store_result, Operand(0));
1527 b(ne, &retry);
1528 dmb(ISH);
1529 bind(&done);
1530
1531 if (result.gp() != result_reg) {
1532 mov(result.gp(), result_reg);
1533 }
1534 if (result_high != no_reg) {
1535 LoadConstant(LiftoffRegister(result_high), WasmValue(0));
1536 }
1537}
1538
1540
1542 uint32_t caller_slot_idx,
1543 ValueKind kind) {
1544 MemOperand src(fp, (caller_slot_idx + 1) * kSystemPointerSize);
1545 liftoff::Load(this, dst, src, kind);
1546}
1547
1549 uint32_t caller_slot_idx,
1551 Register frame_pointer) {
1552 MemOperand dst(frame_pointer, (caller_slot_idx + 1) * kSystemPointerSize);
1553 liftoff::Store(this, src, dst, kind);
1554}
1555
1557 ValueKind kind) {
1558 MemOperand src(sp, offset);
1559 liftoff::Load(this, dst, src, kind);
1560}
1561
1562void LiftoffAssembler::MoveStackValue(uint32_t dst_offset, uint32_t src_offset,
1563 ValueKind kind) {
1564 DCHECK_NE(dst_offset, src_offset);
1566 Register scratch = temps.Acquire();
1567 const int kRegSize = 4;
1568 DCHECK_EQ(0, SlotSizeForType(kind) % kRegSize);
1569 int words = SlotSizeForType(kind) / kRegSize;
1570 if (src_offset < dst_offset) {
1571 do {
1572 ldr(scratch, liftoff::GetStackSlot(src_offset));
1573 str(scratch, liftoff::GetStackSlot(dst_offset));
1574 dst_offset -= kSystemPointerSize;
1575 src_offset -= kSystemPointerSize;
1576 } while (--words);
1577 } else {
1578 while (words--) {
1579 ldr(scratch, liftoff::GetStackSlot(src_offset - words * kRegSize));
1580 str(scratch, liftoff::GetStackSlot(dst_offset - words * kRegSize));
1581 }
1582 }
1583}
1584
1586 DCHECK_NE(dst, src);
1588 MacroAssembler::Move(dst, src);
1589}
1590
1592 ValueKind kind) {
1593 DCHECK_NE(dst, src);
1594 if (kind == kF32) {
1596 } else if (kind == kF64) {
1597 vmov(dst, src);
1598 } else {
1601 }
1602}
1603
1605 // The {str} instruction needs a temp register when the immediate in the
1606 // provided MemOperand does not fit into 12 bits. This happens for large stack
1607 // frames. This DCHECK checks that the temp register is available when needed.
1609 DCHECK_LT(0, offset);
1611 MemOperand dst(fp, -offset);
1612 liftoff::Store(this, reg, dst, kind);
1613}
1614
1618 UseScratchRegisterScope assembler_temps(this);
1620 Register src = no_reg;
1621 // The scratch register will be required by str if multiple instructions
1622 // are required to encode the offset, and so we cannot use it in that case.
1624 src = liftoff_temps.Acquire();
1625 } else {
1626 src = assembler_temps.Acquire();
1627 }
1628 switch (value.type().kind()) {
1629 case kI32:
1630 mov(src, Operand(value.to_i32()));
1631 str(src, dst);
1632 break;
1633 case kI64: {
1634 int32_t low_word = value.to_i64();
1635 mov(src, Operand(low_word));
1637 int32_t high_word = value.to_i64() >> 32;
1638 mov(src, Operand(high_word));
1640 break;
1641 }
1642 default:
1643 // We do not track f32 and f64 constants, hence they are unreachable.
1644 UNREACHABLE();
1645 }
1646}
1647
1651
1655
1657 DCHECK_LT(0, size);
1658 DCHECK_EQ(0, size % 4);
1660
1661 // We need a zero reg. Always use r0 for that, and push it before to restore
1662 // its value afterwards.
1663 push(r0);
1664 mov(r0, Operand(0));
1665
1666 if (size <= 36) {
1667 // Special straight-line code for up to 9 words. Generates one
1668 // instruction per word.
1669 for (int offset = 4; offset <= size; offset += 4) {
1671 }
1672 } else {
1673 // General case for bigger counts (9 instructions).
1674 // Use r1 for start address (inclusive), r2 for end address (exclusive).
1675 push(r1);
1676 push(r2);
1677 sub(r1, fp, Operand(start + size));
1678 sub(r2, fp, Operand(start));
1679
1680 Label loop;
1681 bind(&loop);
1682 str(r0, MemOperand(r1, /* offset */ kSystemPointerSize, PostIndex));
1683 cmp(r1, r2);
1684 b(&loop, ne);
1685
1686 pop(r2);
1687 pop(r1);
1688 }
1689
1690 pop(r0);
1691}
1692
1694 ValueKind /* kind */) {
1695 sub(dst, fp, Operand(offset));
1696}
1697
1699 add(dst, lhs, rhs);
1700}
1702 add(dst, lhs, Operand(imm));
1703}
1704
1706 sub(dst, lhs, rhs);
1707}
1709 sub(dst, lhs, Operand(imm));
1710}
1711
1713 mul(dst, lhs, rhs);
1714}
1716 if (base::bits::IsPowerOfTwo(imm)) {
1718 return;
1719 }
1720 UseScratchRegisterScope temps{this};
1721 Register scratch = temps.Acquire();
1722 mov(scratch, Operand{imm});
1723 mul(dst, lhs, scratch);
1724}
1725
1727 and_(dst, lhs, rhs);
1728}
1730 and_(dst, lhs, Operand(imm));
1731}
1732
1734 orr(dst, lhs, rhs);
1735}
1737 orr(dst, lhs, Operand(imm));
1738}
1739
1741 eor(dst, lhs, rhs);
1742}
1744 eor(dst, lhs, Operand(imm));
1745}
1746
1748 Register amount) {
1749 UseScratchRegisterScope temps(this);
1750 Register scratch = temps.Acquire();
1751 and_(scratch, amount, Operand(0x1f));
1752 lsl(dst, src, Operand(scratch));
1753}
1755 int32_t amount) {
1756 if (V8_LIKELY((amount & 31) != 0)) {
1757 lsl(dst, src, Operand(amount & 31));
1758 } else if (dst != src) {
1759 mov(dst, src);
1760 }
1761}
1762
1764 Register amount) {
1765 UseScratchRegisterScope temps(this);
1766 Register scratch = temps.Acquire();
1767 and_(scratch, amount, Operand(0x1f));
1768 asr(dst, src, Operand(scratch));
1769}
1771 int32_t amount) {
1772 if (V8_LIKELY((amount & 31) != 0)) {
1773 asr(dst, src, Operand(amount & 31));
1774 } else if (dst != src) {
1775 mov(dst, src);
1776 }
1777}
1778
1780 Register amount) {
1781 UseScratchRegisterScope temps(this);
1782 Register scratch = temps.Acquire();
1783 and_(scratch, amount, Operand(0x1f));
1784 lsr(dst, src, Operand(scratch));
1785}
1787 int32_t amount) {
1788 if (V8_LIKELY((amount & 31) != 0)) {
1789 lsr(dst, src, Operand(amount & 31));
1790 } else if (dst != src) {
1791 mov(dst, src);
1792 }
1793}
1794
1800
1806
1812
1818
1822
1826
1830
1832 DoubleRegister rhs) {
1833 vadd(dst, lhs, rhs);
1834}
1835
1837 DoubleRegister rhs) {
1838 vsub(dst, lhs, rhs);
1839}
1840
1842 DoubleRegister rhs) {
1843 vmul(dst, lhs, rhs);
1844}
1845
1847 DoubleRegister rhs) {
1848 vdiv(dst, lhs, rhs);
1849}
1850
1854
1858
1862
1864 clz(dst, src);
1865}
1866
1868 rbit(dst, src);
1869 clz(dst, dst);
1870}
1871
1872namespace liftoff {
1873inline void GeneratePopCnt(Assembler* assm, Register dst, Register src,
1874 Register scratch1, Register scratch2) {
1875 DCHECK(!AreAliased(dst, scratch1, scratch2));
1876 if (src == scratch1) std::swap(scratch1, scratch2);
1877 // x = x - ((x & (0x55555555 << 1)) >> 1)
1878 assm->and_(scratch1, src, Operand(0xaaaaaaaa));
1879 assm->sub(dst, src, Operand(scratch1, LSR, 1));
1880 // x = (x & 0x33333333) + ((x & (0x33333333 << 2)) >> 2)
1881 assm->mov(scratch1, Operand(0x33333333));
1882 assm->and_(scratch2, dst, Operand(scratch1, LSL, 2));
1883 assm->and_(scratch1, dst, scratch1);
1884 assm->add(dst, scratch1, Operand(scratch2, LSR, 2));
1885 // x = (x + (x >> 4)) & 0x0F0F0F0F
1886 assm->add(dst, dst, Operand(dst, LSR, 4));
1887 assm->and_(dst, dst, Operand(0x0f0f0f0f));
1888 // x = x + (x >> 8)
1889 assm->add(dst, dst, Operand(dst, LSR, 8));
1890 // x = x + (x >> 16)
1891 assm->add(dst, dst, Operand(dst, LSR, 16));
1892 // x = x & 0x3F
1893 assm->and_(dst, dst, Operand(0x3f));
1894}
1895} // namespace liftoff
1896
1898 LiftoffRegList pinned{dst};
1899 Register scratch1 = pinned.set(GetUnusedRegister(kGpReg, pinned)).gp();
1900 Register scratch2 = GetUnusedRegister(kGpReg, pinned).gp();
1901 liftoff::GeneratePopCnt(this, dst, src, scratch1, scratch2);
1902 return true;
1903}
1904
1906 Label* trap_div_by_zero,
1907 Label* trap_div_unrepresentable) {
1908 if (!CpuFeatures::IsSupported(SUDIV)) {
1909 bailout(kMissingCPUFeature, "i32_divs");
1910 return;
1911 }
1912 CpuFeatureScope scope(this, SUDIV);
1913 // Issue division early so we can perform the trapping checks whilst it
1914 // completes.
1915 bool speculative_sdiv = dst != lhs && dst != rhs;
1916 if (speculative_sdiv) {
1917 sdiv(dst, lhs, rhs);
1918 }
1919 Label noTrap;
1920 // Check for division by zero.
1921 cmp(rhs, Operand(0));
1922 b(trap_div_by_zero, eq);
1923 // Check for kMinInt / -1. This is unrepresentable.
1924 cmp(rhs, Operand(-1));
1925 b(&noTrap, ne);
1926 cmp(lhs, Operand(kMinInt));
1927 b(trap_div_unrepresentable, eq);
1928 bind(&noTrap);
1929 if (!speculative_sdiv) {
1930 sdiv(dst, lhs, rhs);
1931 }
1932}
1933
1935 Label* trap_div_by_zero) {
1936 if (!CpuFeatures::IsSupported(SUDIV)) {
1937 bailout(kMissingCPUFeature, "i32_divu");
1938 return;
1939 }
1940 CpuFeatureScope scope(this, SUDIV);
1941 // Check for division by zero.
1942 cmp(rhs, Operand(0));
1943 b(trap_div_by_zero, eq);
1944 udiv(dst, lhs, rhs);
1945}
1946
1948 Label* trap_div_by_zero) {
1949 if (!CpuFeatures::IsSupported(SUDIV)) {
1950 // When this case is handled, a check for ARMv7 is required to use mls.
1951 // Mls support is implied with SUDIV support.
1952 bailout(kMissingCPUFeature, "i32_rems");
1953 return;
1954 }
1955 CpuFeatureScope scope(this, SUDIV);
1956 // No need to check kMinInt / -1 because the result is kMinInt and then
1957 // kMinInt * -1 -> kMinInt. In this case, the Msub result is therefore 0.
1958 UseScratchRegisterScope temps(this);
1959 Register scratch = temps.Acquire();
1960 sdiv(scratch, lhs, rhs);
1961 // Check for division by zero.
1962 cmp(rhs, Operand(0));
1963 b(trap_div_by_zero, eq);
1964 // Compute remainder.
1965 mls(dst, scratch, rhs, lhs);
1966}
1967
1969 Label* trap_div_by_zero) {
1970 if (!CpuFeatures::IsSupported(SUDIV)) {
1971 // When this case is handled, a check for ARMv7 is required to use mls.
1972 // Mls support is implied with SUDIV support.
1973 bailout(kMissingCPUFeature, "i32_remu");
1974 return;
1975 }
1976 CpuFeatureScope scope(this, SUDIV);
1977 // No need to check kMinInt / -1 because the result is kMinInt and then
1978 // kMinInt * -1 -> kMinInt. In this case, the Msub result is therefore 0.
1979 UseScratchRegisterScope temps(this);
1980 Register scratch = temps.Acquire();
1981 udiv(scratch, lhs, rhs);
1982 // Check for division by zero.
1983 cmp(rhs, Operand(0));
1984 b(trap_div_by_zero, eq);
1985 // Compute remainder.
1986 mls(dst, scratch, rhs, lhs);
1987}
1988
1993
1998
2003
2005 LiftoffRegister rhs) {
2006 // Idea:
2007 // [ lhs_hi | lhs_lo ] * [ rhs_hi | rhs_lo ]
2008 // = [ lhs_hi * rhs_lo | ] (32 bit mul, shift 32)
2009 // + [ lhs_lo * rhs_hi | ] (32 bit mul, shift 32)
2010 // + [ lhs_lo * rhs_lo ] (32x32->64 mul, shift 0)
2011 UseScratchRegisterScope temps(this);
2012 Register scratch = temps.Acquire();
2013 // scratch = lhs_hi * rhs_lo
2014 mul(scratch, lhs.high_gp(), rhs.low_gp());
2015 // scratch += lhs_lo * rhs_hi
2016 mla(scratch, lhs.low_gp(), rhs.high_gp(), scratch);
2017 // TODO(arm): use umlal once implemented correctly in the simulator.
2018 // [dst_hi|dst_lo] = lhs_lo * rhs_lo
2019 umull(dst.low_gp(), dst.high_gp(), lhs.low_gp(), rhs.low_gp());
2020 // dst_hi += scratch
2021 add(dst.high_gp(), dst.high_gp(), scratch);
2022}
2023
2025 LiftoffRegister rhs,
2026 Label* trap_div_by_zero,
2027 Label* trap_div_unrepresentable) {
2028 return false;
2029}
2030
2032 LiftoffRegister rhs,
2033 Label* trap_div_by_zero) {
2034 return false;
2035}
2036
2038 LiftoffRegister rhs,
2039 Label* trap_div_by_zero) {
2040 return false;
2041}
2042
2044 LiftoffRegister rhs,
2045 Label* trap_div_by_zero) {
2046 return false;
2047}
2048
2053
2055 int32_t amount) {
2056 UseScratchRegisterScope temps(this);
2057 // {src.low_gp()} will still be needed after writing {dst.high_gp()}.
2058 Register src_low =
2059 liftoff::EnsureNoAlias(this, src.low_gp(), dst.high_gp(), &temps);
2060
2061 LslPair(dst.low_gp(), dst.high_gp(), src_low, src.high_gp(), amount & 63);
2062}
2063
2068
2070 int32_t amount) {
2071 UseScratchRegisterScope temps(this);
2072 // {src.high_gp()} will still be needed after writing {dst.low_gp()}.
2073 Register src_high =
2074 liftoff::EnsureNoAlias(this, src.high_gp(), dst.low_gp(), &temps);
2075
2076 AsrPair(dst.low_gp(), dst.high_gp(), src.low_gp(), src_high, amount & 63);
2077}
2078
2083
2085 int32_t amount) {
2086 UseScratchRegisterScope temps(this);
2087 // {src.high_gp()} will still be needed after writing {dst.low_gp()}.
2088 Register src_high =
2089 liftoff::EnsureNoAlias(this, src.high_gp(), dst.low_gp(), &temps);
2090
2091 LsrPair(dst.low_gp(), dst.high_gp(), src.low_gp(), src_high, amount & 63);
2092}
2093
2095 // return high == 0 ? 32 + CLZ32(low) : CLZ32(high);
2096 Label done;
2097 Label high_is_zero;
2098 cmp(src.high_gp(), Operand(0));
2099 b(&high_is_zero, eq);
2100
2101 clz(dst.low_gp(), src.high_gp());
2102 jmp(&done);
2103
2104 bind(&high_is_zero);
2105 clz(dst.low_gp(), src.low_gp());
2106 add(dst.low_gp(), dst.low_gp(), Operand(32));
2107
2108 bind(&done);
2109 mov(dst.high_gp(), Operand(0)); // High word of result is always 0.
2110}
2111
2113 // return low == 0 ? 32 + CTZ32(high) : CTZ32(low);
2114 // CTZ32(x) = CLZ(RBIT(x))
2115 Label done;
2116 Label low_is_zero;
2117 cmp(src.low_gp(), Operand(0));
2118 b(&low_is_zero, eq);
2119
2120 rbit(dst.low_gp(), src.low_gp());
2121 clz(dst.low_gp(), dst.low_gp());
2122 jmp(&done);
2123
2124 bind(&low_is_zero);
2125 rbit(dst.low_gp(), src.high_gp());
2126 clz(dst.low_gp(), dst.low_gp());
2127 add(dst.low_gp(), dst.low_gp(), Operand(32));
2128
2129 bind(&done);
2130 mov(dst.high_gp(), Operand(0)); // High word of result is always 0.
2131}
2132
2134 LiftoffRegister src) {
2135 // Produce partial popcnts in the two dst registers, making sure not to
2136 // overwrite the second src register before using it.
2137 Register src1 = src.high_gp() == dst.low_gp() ? src.high_gp() : src.low_gp();
2138 Register src2 = src.high_gp() == dst.low_gp() ? src.low_gp() : src.high_gp();
2139 LiftoffRegList pinned{dst, src2};
2140 Register scratch1 = pinned.set(GetUnusedRegister(kGpReg, pinned)).gp();
2141 Register scratch2 = GetUnusedRegister(kGpReg, pinned).gp();
2142 liftoff::GeneratePopCnt(this, dst.low_gp(), src1, scratch1, scratch2);
2143 liftoff::GeneratePopCnt(this, dst.high_gp(), src2, scratch1, scratch2);
2144 // Now add the two into the lower dst reg and clear the higher dst reg.
2145 add(dst.low_gp(), dst.low_gp(), dst.high_gp());
2146 mov(dst.high_gp(), Operand(0));
2147 return true;
2148}
2149
2151 if (!is_int12(offset)) {
2152 // For large offsets, ldr/str will need a scratch register, but we need
2153 // the single available scratch register here. So fold the offset into the
2154 // base address.
2155 // Note: if we ever want to use this function for callers that don't want
2156 // {dst} to get clobbered, we could spill it to the stack and restore it
2157 // later.
2158 add(dst.gp(), dst.gp(), Operand(offset));
2159 offset = 0;
2160 }
2161 UseScratchRegisterScope temps(this);
2162 Register scratch = temps.Acquire();
2163 ldr(scratch, MemOperand(dst.gp(), offset));
2164 add(scratch, scratch, Operand(Smi::FromInt(1)));
2165 str(scratch, MemOperand(dst.gp(), offset));
2166}
2167
2169 if (CpuFeatures::IsSupported(ARMv8)) {
2170 CpuFeatureScope scope(this, ARMv8);
2172 return true;
2173 }
2174 return false;
2175}
2176
2178 if (CpuFeatures::IsSupported(ARMv8)) {
2179 CpuFeatureScope scope(this, ARMv8);
2181 return true;
2182 }
2183 return false;
2184}
2185
2187 if (CpuFeatures::IsSupported(ARMv8)) {
2188 CpuFeatureScope scope(this, ARMv8);
2190 return true;
2191 }
2192 return false;
2193}
2194
2196 DoubleRegister src) {
2197 if (CpuFeatures::IsSupported(ARMv8)) {
2198 CpuFeatureScope scope(this, ARMv8);
2200 return true;
2201 }
2202 return false;
2203}
2204
2211
2218
2220 if (CpuFeatures::IsSupported(ARMv8)) {
2221 CpuFeatureScope scope(this, ARMv8);
2222 vrintp(dst, src);
2223 return true;
2224 }
2225 return false;
2226}
2227
2229 if (CpuFeatures::IsSupported(ARMv8)) {
2230 CpuFeatureScope scope(this, ARMv8);
2231 vrintm(dst, src);
2232 return true;
2233 }
2234 return false;
2235}
2236
2238 if (CpuFeatures::IsSupported(ARMv8)) {
2239 CpuFeatureScope scope(this, ARMv8);
2240 vrintz(dst, src);
2241 return true;
2242 }
2243 return false;
2244}
2245
2247 DoubleRegister src) {
2248 if (CpuFeatures::IsSupported(ARMv8)) {
2249 CpuFeatureScope scope(this, ARMv8);
2250 vrintn(dst, src);
2251 return true;
2252 }
2253 return false;
2254}
2255
2260
2265
2267 DoubleRegister rhs) {
2268 constexpr uint32_t kF32SignBit = uint32_t{1} << 31;
2269 UseScratchRegisterScope temps(this);
2270 Register scratch = GetUnusedRegister(kGpReg, {}).gp();
2271 Register scratch2 = temps.Acquire();
2272 VmovLow(scratch, lhs);
2273 // Clear sign bit in {scratch}.
2274 bic(scratch, scratch, Operand(kF32SignBit));
2275 VmovLow(scratch2, rhs);
2276 // Isolate sign bit in {scratch2}.
2277 and_(scratch2, scratch2, Operand(kF32SignBit));
2278 // Combine {scratch2} into {scratch}.
2279 orr(scratch, scratch, scratch2);
2280 VmovLow(dst, scratch);
2281}
2282
2284 DoubleRegister rhs) {
2285 constexpr uint32_t kF64SignBitHighWord = uint32_t{1} << 31;
2286 // On arm, we cannot hold the whole f64 value in a gp register, so we just
2287 // operate on the upper half (UH).
2288 UseScratchRegisterScope temps(this);
2289 Register scratch = GetUnusedRegister(kGpReg, {}).gp();
2290 Register scratch2 = temps.Acquire();
2291 VmovHigh(scratch, lhs);
2292 // Clear sign bit in {scratch}.
2293 bic(scratch, scratch, Operand(kF64SignBitHighWord));
2294 VmovHigh(scratch2, rhs);
2295 // Isolate sign bit in {scratch2}.
2296 and_(scratch2, scratch2, Operand(kF64SignBitHighWord));
2297 // Combine {scratch2} into {scratch}.
2298 orr(scratch, scratch, scratch2);
2299 vmov(dst, lhs);
2300 VmovHigh(dst, scratch);
2301}
2302
2304 LiftoffRegister dst,
2305 LiftoffRegister src, Label* trap) {
2306 switch (opcode) {
2307 case kExprI32ConvertI64:
2308 MacroAssembler::Move(dst.gp(), src.low_gp());
2309 return true;
2310 case kExprI32SConvertF32: {
2311 UseScratchRegisterScope temps(this);
2312 SwVfpRegister scratch_f = temps.AcquireS();
2314 scratch_f,
2315 liftoff::GetFloatRegister(src.fp())); // f32 -> i32 round to zero.
2316 vmov(dst.gp(), scratch_f);
2317 // Check underflow and NaN.
2318 vmov(scratch_f, Float32(static_cast<float>(INT32_MIN)));
2320 b(trap, lt);
2321 // Check overflow.
2322 cmp(dst.gp(), Operand(-1));
2323 b(trap, vs);
2324 return true;
2325 }
2326 case kExprI32UConvertF32: {
2327 UseScratchRegisterScope temps(this);
2328 SwVfpRegister scratch_f = temps.AcquireS();
2330 scratch_f,
2331 liftoff::GetFloatRegister(src.fp())); // f32 -> i32 round to zero.
2332 vmov(dst.gp(), scratch_f);
2333 // Check underflow and NaN.
2334 vmov(scratch_f, Float32(-1.0f));
2336 b(trap, le);
2337 // Check overflow.
2338 cmp(dst.gp(), Operand(-1));
2339 b(trap, eq);
2340 return true;
2341 }
2342 case kExprI32SConvertF64: {
2343 UseScratchRegisterScope temps(this);
2344 SwVfpRegister scratch_f = temps.AcquireS();
2345 vcvt_s32_f64(scratch_f, src.fp()); // f64 -> i32 round to zero.
2346 vmov(dst.gp(), scratch_f);
2347 // Check underflow and NaN.
2348 DwVfpRegister scratch_d = temps.AcquireD();
2349 vmov(scratch_d, base::Double(static_cast<double>(INT32_MIN - 1.0)));
2350 VFPCompareAndSetFlags(src.fp(), scratch_d);
2351 b(trap, le);
2352 // Check overflow.
2353 vmov(scratch_d, base::Double(static_cast<double>(INT32_MAX + 1.0)));
2354 VFPCompareAndSetFlags(src.fp(), scratch_d);
2355 b(trap, ge);
2356 return true;
2357 }
2358 case kExprI32UConvertF64: {
2359 UseScratchRegisterScope temps(this);
2360 SwVfpRegister scratch_f = temps.AcquireS();
2361 vcvt_u32_f64(scratch_f, src.fp()); // f64 -> i32 round to zero.
2362 vmov(dst.gp(), scratch_f);
2363 // Check underflow and NaN.
2364 DwVfpRegister scratch_d = temps.AcquireD();
2365 vmov(scratch_d, base::Double(static_cast<double>(-1.0)));
2366 VFPCompareAndSetFlags(src.fp(), scratch_d);
2367 b(trap, le);
2368 // Check overflow.
2369 vmov(scratch_d, base::Double(static_cast<double>(UINT32_MAX + 1.0)));
2370 VFPCompareAndSetFlags(src.fp(), scratch_d);
2371 b(trap, ge);
2372 return true;
2373 }
2374 case kExprI32SConvertSatF32: {
2375 UseScratchRegisterScope temps(this);
2376 SwVfpRegister scratch_f = temps.AcquireS();
2378 scratch_f,
2379 liftoff::GetFloatRegister(src.fp())); // f32 -> i32 round to zero.
2380 vmov(dst.gp(), scratch_f);
2381 return true;
2382 }
2383 case kExprI32UConvertSatF32: {
2384 UseScratchRegisterScope temps(this);
2385 SwVfpRegister scratch_f = temps.AcquireS();
2387 scratch_f,
2388 liftoff::GetFloatRegister(src.fp())); // f32 -> u32 round to zero.
2389 vmov(dst.gp(), scratch_f);
2390 return true;
2391 }
2392 case kExprI32SConvertSatF64: {
2393 UseScratchRegisterScope temps(this);
2394 SwVfpRegister scratch_f = temps.AcquireS();
2395 vcvt_s32_f64(scratch_f, src.fp()); // f64 -> i32 round to zero.
2396 vmov(dst.gp(), scratch_f);
2397 return true;
2398 }
2399 case kExprI32UConvertSatF64: {
2400 UseScratchRegisterScope temps(this);
2401 SwVfpRegister scratch_f = temps.AcquireS();
2402 vcvt_u32_f64(scratch_f, src.fp()); // f64 -> u32 round to zero.
2403 vmov(dst.gp(), scratch_f);
2404 return true;
2405 }
2406 case kExprI32ReinterpretF32:
2407 vmov(dst.gp(), liftoff::GetFloatRegister(src.fp()));
2408 return true;
2409 case kExprI64SConvertI32:
2410 if (dst.low_gp() != src.gp()) mov(dst.low_gp(), src.gp());
2411 mov(dst.high_gp(), Operand(src.gp(), ASR, 31));
2412 return true;
2413 case kExprI64UConvertI32:
2414 if (dst.low_gp() != src.gp()) mov(dst.low_gp(), src.gp());
2415 mov(dst.high_gp(), Operand(0));
2416 return true;
2417 case kExprI64ReinterpretF64:
2418 vmov(dst.low_gp(), dst.high_gp(), src.fp());
2419 return true;
2420 case kExprF32SConvertI32: {
2421 SwVfpRegister dst_float = liftoff::GetFloatRegister(dst.fp());
2422 vmov(dst_float, src.gp());
2423 vcvt_f32_s32(dst_float, dst_float);
2424 return true;
2425 }
2426 case kExprF32UConvertI32: {
2427 SwVfpRegister dst_float = liftoff::GetFloatRegister(dst.fp());
2428 vmov(dst_float, src.gp());
2429 vcvt_f32_u32(dst_float, dst_float);
2430 return true;
2431 }
2432 case kExprF32ConvertF64:
2433 vcvt_f32_f64(liftoff::GetFloatRegister(dst.fp()), src.fp());
2434 return true;
2435 case kExprF32ReinterpretI32:
2436 vmov(liftoff::GetFloatRegister(dst.fp()), src.gp());
2437 return true;
2438 case kExprF64SConvertI32: {
2439 vmov(liftoff::GetFloatRegister(dst.fp()), src.gp());
2441 return true;
2442 }
2443 case kExprF64UConvertI32: {
2444 vmov(liftoff::GetFloatRegister(dst.fp()), src.gp());
2446 return true;
2447 }
2448 case kExprF64ConvertF32:
2449 vcvt_f64_f32(dst.fp(), liftoff::GetFloatRegister(src.fp()));
2450 return true;
2451 case kExprF64ReinterpretI64:
2452 vmov(dst.fp(), src.low_gp(), src.high_gp());
2453 return true;
2454 case kExprF64SConvertI64:
2455 case kExprF64UConvertI64:
2456 case kExprI64SConvertF32:
2457 case kExprI64UConvertF32:
2458 case kExprI64SConvertSatF32:
2459 case kExprI64UConvertSatF32:
2460 case kExprF32SConvertI64:
2461 case kExprF32UConvertI64:
2462 case kExprI64SConvertF64:
2463 case kExprI64UConvertF64:
2464 case kExprI64SConvertSatF64:
2465 case kExprI64UConvertSatF64:
2466 // These cases can be handled by the C fallback function.
2467 return false;
2468 default:
2469 UNREACHABLE();
2470 }
2471}
2472
2476
2480
2482 LiftoffRegister src) {
2483 emit_i32_signextend_i8(dst.low_gp(), src.low_gp());
2484 mov(dst.high_gp(), Operand(dst.low_gp(), ASR, 31));
2485}
2486
2488 LiftoffRegister src) {
2489 emit_i32_signextend_i16(dst.low_gp(), src.low_gp());
2490 mov(dst.high_gp(), Operand(dst.low_gp(), ASR, 31));
2491}
2492
2494 LiftoffRegister src) {
2495 MacroAssembler::Move(dst.low_gp(), src.low_gp());
2496 mov(dst.high_gp(), Operand(src.low_gp(), ASR, 31));
2497}
2498
2500
2501void LiftoffAssembler::emit_jump(Register target) { bx(target); }
2502
2504 ValueKind kind, Register lhs,
2505 Register rhs,
2506 const FreezeCacheState& frozen) {
2507 if (rhs == no_reg) {
2509 cmp(lhs, Operand(0));
2510 } else {
2511 DCHECK(kind == kI32 ||
2512 (is_reference(kind) && (cond == kEqual || cond == kNotEqual)));
2513 cmp(lhs, rhs);
2514 }
2515 b(label, cond);
2516}
2517
2519 Register lhs, int32_t imm,
2520 const FreezeCacheState& frozen) {
2521 cmp(lhs, Operand(imm));
2522 b(label, cond);
2523}
2524
2526 clz(dst, src);
2527 mov(dst, Operand(dst, LSR, kRegSizeInBitsLog2));
2528}
2529
2531 Register lhs, Register rhs) {
2532 cmp(lhs, rhs);
2533 mov(dst, Operand(0), LeaveCC);
2534 mov(dst, Operand(1), LeaveCC, cond);
2535}
2536
2538 orr(dst, src.low_gp(), src.high_gp());
2539 clz(dst, dst);
2540 mov(dst, Operand(dst, LSR, 5));
2541}
2542
2544 LiftoffRegister lhs,
2545 LiftoffRegister rhs) {
2546 // For signed i64 comparisons, we still need to use unsigned comparison for
2547 // the low word (the only bit carrying signedness information is the MSB in
2548 // the high word).
2549 Condition unsigned_cond = liftoff::MakeUnsigned(cond);
2550 Label set_cond;
2551 Label cont;
2552 LiftoffRegister dest = LiftoffRegister(dst);
2553 bool speculative_move = !dest.overlaps(lhs) && !dest.overlaps(rhs);
2554 if (speculative_move) {
2555 mov(dst, Operand(0));
2556 }
2557 // Compare high word first. If it differs, use it for the set_cond. If it's
2558 // equal, compare the low word and use that for set_cond.
2559 cmp(lhs.high_gp(), rhs.high_gp());
2560 if (unsigned_cond == cond) {
2561 cmp(lhs.low_gp(), rhs.low_gp(), eq);
2562 if (!speculative_move) {
2563 mov(dst, Operand(0));
2564 }
2565 mov(dst, Operand(1), LeaveCC, cond);
2566 } else {
2567 // If the condition predicate for the low differs from that for the high
2568 // word, the conditional move instructions must be separated.
2569 b(ne, &set_cond);
2570 cmp(lhs.low_gp(), rhs.low_gp());
2571 if (!speculative_move) {
2572 mov(dst, Operand(0));
2573 }
2574 mov(dst, Operand(1), LeaveCC, unsigned_cond);
2575 b(&cont);
2576 bind(&set_cond);
2577 if (!speculative_move) {
2578 mov(dst, Operand(0));
2579 }
2580 mov(dst, Operand(1), LeaveCC, cond);
2581 bind(&cont);
2582 }
2583}
2584
2586 DoubleRegister lhs,
2587 DoubleRegister rhs) {
2590 mov(dst, Operand(0), LeaveCC);
2591 mov(dst, Operand(1), LeaveCC, cond);
2592 if (cond != ne) {
2593 // If V flag set, at least one of the arguments was a Nan -> false.
2594 mov(dst, Operand(0), LeaveCC, vs);
2595 }
2596}
2597
2599 DoubleRegister lhs,
2600 DoubleRegister rhs) {
2601 VFPCompareAndSetFlags(lhs, rhs);
2602 mov(dst, Operand(0), LeaveCC);
2603 mov(dst, Operand(1), LeaveCC, cond);
2604 if (cond != ne) {
2605 // If V flag set, at least one of the arguments was a Nan -> false.
2606 mov(dst, Operand(0), LeaveCC, vs);
2607 }
2608}
2609
2611 LiftoffRegister true_value,
2612 LiftoffRegister false_value,
2613 ValueKind kind) {
2614 return false;
2615}
2616
2618 SmiCheckMode mode,
2619 const FreezeCacheState& frozen) {
2620 tst(obj, Operand(kSmiTagMask));
2621 Condition condition = mode == kJumpOnSmi ? eq : ne;
2622 b(condition, target);
2623}
2624
2626 Register offset_reg, uintptr_t offset_imm,
2627 LoadType type,
2628 LoadTransformationKind transform,
2629 uint32_t* protected_load_pc,
2630 bool i64_offset) {
2631 UseScratchRegisterScope temps(this);
2632 Register actual_src_addr = liftoff::CalculateActualAddress(
2633 this, &temps, src_addr, offset_reg, offset_imm);
2634 *protected_load_pc = pc_offset();
2635 MachineType memtype = type.mem_type();
2636
2637 if (transform == LoadTransformationKind::kExtend) {
2638 if (memtype == MachineType::Int8()) {
2640 NeonMemOperand(actual_src_addr));
2642 } else if (memtype == MachineType::Uint8()) {
2644 NeonMemOperand(actual_src_addr));
2646 } else if (memtype == MachineType::Int16()) {
2648 NeonMemOperand(actual_src_addr));
2650 } else if (memtype == MachineType::Uint16()) {
2652 NeonMemOperand(actual_src_addr));
2654 } else if (memtype == MachineType::Int32()) {
2656 NeonMemOperand(actual_src_addr));
2658 } else if (memtype == MachineType::Uint32()) {
2660 NeonMemOperand(actual_src_addr));
2662 }
2663 } else if (transform == LoadTransformationKind::kZeroExtend) {
2665 if (memtype == MachineType::Int32()) {
2666 vmov(dest, 0);
2668 NeonMemOperand(actual_src_addr));
2669 } else {
2670 DCHECK_EQ(MachineType::Int64(), memtype);
2671 vmov(dest.high(), 0);
2672 vld1(Neon64, NeonListOperand(dest.low()),
2673 NeonMemOperand(actual_src_addr));
2674 }
2675 } else {
2677 if (memtype == MachineType::Int8()) {
2679 NeonMemOperand(actual_src_addr));
2680 } else if (memtype == MachineType::Int16()) {
2682 NeonMemOperand(actual_src_addr));
2683 } else if (memtype == MachineType::Int32()) {
2685 NeonMemOperand(actual_src_addr));
2686 } else if (memtype == MachineType::Int64()) {
2688 NeonMemOperand(actual_src_addr));
2689 MacroAssembler::Move(dst.high_fp(), dst.low_fp());
2690 }
2691 }
2692}
2693
2695 Register addr, Register offset_reg,
2696 uintptr_t offset_imm, LoadType type,
2697 uint8_t laneidx, uint32_t* protected_load_pc,
2698 bool /* i64_offset */) {
2699 UseScratchRegisterScope temps(this);
2700 Register actual_src_addr = liftoff::CalculateActualAddress(
2701 this, &temps, addr, offset_reg, offset_imm);
2704 *protected_load_pc = pc_offset();
2705 LoadStoreLaneParams load_params(type.mem_type().representation(), laneidx);
2706 NeonListOperand dst_op =
2707 NeonListOperand(load_params.low_op ? dst.low_fp() : dst.high_fp());
2708 MacroAssembler::LoadLane(load_params.sz, dst_op, load_params.laneidx,
2709 NeonMemOperand(actual_src_addr));
2710}
2711
2713 uintptr_t offset_imm, LiftoffRegister src,
2714 StoreType type, uint8_t laneidx,
2715 uint32_t* protected_store_pc,
2716 bool /* i64_offset */) {
2717 UseScratchRegisterScope temps(this);
2718 Register actual_dst_addr =
2719 liftoff::CalculateActualAddress(this, &temps, dst, offset, offset_imm);
2720 *protected_store_pc = pc_offset();
2721
2722 LoadStoreLaneParams store_params(type.mem_rep(), laneidx);
2723 NeonListOperand src_op =
2724 NeonListOperand(store_params.low_op ? src.low_fp() : src.high_fp());
2725 MacroAssembler::StoreLane(store_params.sz, src_op, store_params.laneidx,
2726 NeonMemOperand(actual_dst_addr));
2727}
2728
2730 LiftoffRegister lhs,
2731 LiftoffRegister rhs) {
2732 UseScratchRegisterScope temps(this);
2733
2735 if (dst == lhs) {
2736 // dst will be overwritten, so keep the table somewhere else.
2737 QwNeonRegister tbl = temps.AcquireQ();
2739 table = NeonListOperand(tbl);
2740 }
2741
2742 vtbl(dst.low_fp(), table, rhs.low_fp());
2743 vtbl(dst.high_fp(), table, rhs.high_fp());
2744}
2745
2751
2757
2763
2768
2773
2775 LiftoffRegister src1,
2776 LiftoffRegister src2,
2778 int lane_width) {
2779 // ARM uses bytewise selection for all lane widths.
2780 emit_s128_select(dst, src1, src2, mask);
2781}
2782
2788
2790 LiftoffRegister lhs,
2791 uint8_t imm_lane_idx) {
2792 ExtractLane(dst.fp(), liftoff::GetSimd128Register(lhs), imm_lane_idx);
2793}
2794
2796 LiftoffRegister src1,
2797 LiftoffRegister src2,
2798 uint8_t imm_lane_idx) {
2800 liftoff::GetSimd128Register(src1), src2.fp(), imm_lane_idx);
2801}
2802
2804 LiftoffRegister src) {
2805 vabs(dst.low_fp(), src.low_fp());
2806 vabs(dst.high_fp(), src.high_fp());
2807}
2808
2810 LiftoffRegister src) {
2811 vneg(dst.low_fp(), src.low_fp());
2812 vneg(dst.high_fp(), src.high_fp());
2813}
2814
2816 LiftoffRegister src) {
2817 vsqrt(dst.low_fp(), src.low_fp());
2818 vsqrt(dst.high_fp(), src.high_fp());
2819}
2820
2822 LiftoffRegister src) {
2823 if (!CpuFeatures::IsSupported(ARMv8)) {
2824 return false;
2825 }
2826
2827 CpuFeatureScope scope(this, ARMv8);
2828 vrintp(dst.low_fp(), src.low_fp());
2829 vrintp(dst.high_fp(), src.high_fp());
2830 return true;
2831}
2832
2834 LiftoffRegister src) {
2835 if (!CpuFeatures::IsSupported(ARMv8)) {
2836 return false;
2837 }
2838
2839 CpuFeatureScope scope(this, ARMv8);
2840 vrintm(dst.low_fp(), src.low_fp());
2841 vrintm(dst.high_fp(), src.high_fp());
2842 return true;
2843}
2844
2846 LiftoffRegister src) {
2847 if (!CpuFeatures::IsSupported(ARMv8)) {
2848 return false;
2849 }
2850
2851 CpuFeatureScope scope(this, ARMv8);
2852 vrintz(dst.low_fp(), src.low_fp());
2853 vrintz(dst.high_fp(), src.high_fp());
2854 return true;
2855}
2856
2858 LiftoffRegister src) {
2859 if (!CpuFeatures::IsSupported(ARMv8)) {
2860 return false;
2861 }
2862
2863 CpuFeatureScope scope(this, ARMv8);
2864 vrintn(dst.low_fp(), src.low_fp());
2865 vrintn(dst.high_fp(), src.high_fp());
2866 return true;
2867}
2868
2870 LiftoffRegister rhs) {
2871 vadd(dst.low_fp(), lhs.low_fp(), rhs.low_fp());
2872 vadd(dst.high_fp(), lhs.high_fp(), rhs.high_fp());
2873}
2874
2876 LiftoffRegister rhs) {
2877 vsub(dst.low_fp(), lhs.low_fp(), rhs.low_fp());
2878 vsub(dst.high_fp(), lhs.high_fp(), rhs.high_fp());
2879}
2880
2882 LiftoffRegister rhs) {
2883 vmul(dst.low_fp(), lhs.low_fp(), rhs.low_fp());
2884 vmul(dst.high_fp(), lhs.high_fp(), rhs.high_fp());
2885}
2886
2888 LiftoffRegister rhs) {
2889 vdiv(dst.low_fp(), lhs.low_fp(), rhs.low_fp());
2890 vdiv(dst.high_fp(), lhs.high_fp(), rhs.high_fp());
2891}
2892
2894 LiftoffRegister rhs) {
2898
2899 liftoff::EmitFloatMinOrMax(this, dest.low(), left.low(), right.low(),
2901 liftoff::EmitFloatMinOrMax(this, dest.high(), left.high(), right.high(),
2903}
2904
2906 LiftoffRegister rhs) {
2910
2911 liftoff::EmitFloatMinOrMax(this, dest.low(), left.low(), right.low(),
2913 liftoff::EmitFloatMinOrMax(this, dest.high(), left.high(), right.high(),
2915}
2916
2918 LiftoffRegister rhs) {
2922
2923 VFPCompareAndSetFlags(right.low(), left.low());
2924 if (dst != rhs) vmov(dest.low(), right.low(), mi);
2925 if (dst != lhs) vmov(dest.low(), left.low(), NegateCondition(mi));
2926 VFPCompareAndSetFlags(right.high(), left.high());
2927 if (dst != rhs) vmov(dest.high(), right.high(), mi);
2928 if (dst != lhs) vmov(dest.high(), left.high(), NegateCondition(mi));
2929}
2930
2932 LiftoffRegister rhs) {
2936
2937 VFPCompareAndSetFlags(right.low(), left.low());
2938 if (dst != rhs) vmov(dest.low(), right.low(), gt);
2939 if (dst != lhs) vmov(dest.low(), left.low(), NegateCondition(gt));
2940 VFPCompareAndSetFlags(right.high(), left.high());
2941 if (dst != rhs) vmov(dest.high(), right.high(), gt);
2942 if (dst != lhs) vmov(dest.high(), left.high(), NegateCondition(gt));
2943}
2944
2950
2956
2962
2968
2974
2979
2981 LiftoffRegister lhs,
2982 uint8_t imm_lane_idx) {
2984 liftoff::GetSimd128Register(lhs), imm_lane_idx);
2985}
2986
2988 LiftoffRegister src1,
2989 LiftoffRegister src2,
2990 uint8_t imm_lane_idx) {
2993 liftoff::GetFloatRegister(src2.fp()), imm_lane_idx);
2994}
2995
3000
3005
3007 LiftoffRegister src) {
3008 // The list of d registers available to us is from d0 to d15, which always
3009 // maps to 2 s registers.
3011 LowDwVfpRegister src_low = LowDwVfpRegister::from_code(src.low_fp().code());
3012
3014 LowDwVfpRegister src_high = LowDwVfpRegister::from_code(src.high_fp().code());
3015
3016 vsqrt(dst_low.low(), src_low.low());
3017 vsqrt(dst_low.high(), src_low.high());
3018 vsqrt(dst_high.low(), src_high.low());
3019 vsqrt(dst_high.high(), src_high.high());
3020}
3021
3023 LiftoffRegister src) {
3024 if (!CpuFeatures::IsSupported(ARMv8)) {
3025 return false;
3026 }
3027
3028 CpuFeatureScope scope(this, ARMv8);
3031 return true;
3032}
3033
3035 LiftoffRegister src) {
3036 if (!CpuFeatures::IsSupported(ARMv8)) {
3037 return false;
3038 }
3039
3040 CpuFeatureScope scope(this, ARMv8);
3043 return true;
3044}
3045
3047 LiftoffRegister src) {
3048 if (!CpuFeatures::IsSupported(ARMv8)) {
3049 return false;
3050 }
3051
3052 CpuFeatureScope scope(this, ARMv8);
3055 return true;
3056}
3057
3059 LiftoffRegister src) {
3060 if (!CpuFeatures::IsSupported(ARMv8)) {
3061 return false;
3062 }
3063
3064 CpuFeatureScope scope(this, ARMv8);
3067 return true;
3068}
3069
3075
3081
3087
3089 LiftoffRegister rhs) {
3090 // The list of d registers available to us is from d0 to d15, which always
3091 // maps to 2 s registers.
3095
3099
3100 vdiv(dst_low.low(), lhs_low.low(), rhs_low.low());
3101 vdiv(dst_low.high(), lhs_low.high(), rhs_low.high());
3102 vdiv(dst_high.low(), lhs_high.low(), rhs_high.low());
3103 vdiv(dst_high.high(), lhs_high.high(), rhs_high.high());
3104}
3105
3111
3117
3124
3131
3133 LiftoffRegister rhs) {
3134 UseScratchRegisterScope temps(this);
3135
3137 if (dst == lhs || dst == rhs) {
3138 tmp = temps.AcquireQ();
3139 }
3140
3143 vcgt(tmp, left, right);
3144 vbsl(tmp, right, left);
3145
3146 if (dst == lhs || dst == rhs) {
3148 }
3149}
3150
3152 LiftoffRegister rhs) {
3153 UseScratchRegisterScope temps(this);
3154
3156 if (dst == lhs || dst == rhs) {
3157 tmp = temps.AcquireQ();
3158 }
3159
3162 vcgt(tmp, right, left);
3163 vbsl(tmp, right, left);
3164
3165 if (dst == lhs || dst == rhs) {
3167 }
3168}
3169
3171 LiftoffRegister src) {
3173 vdup(Neon32, dst_simd, src.low_gp());
3174 ReplaceLane(dst_simd, dst_simd, src.high_gp(), NeonS32, 1);
3175 ReplaceLane(dst_simd, dst_simd, src.high_gp(), NeonS32, 3);
3176}
3177
3179 LiftoffRegister lhs,
3180 uint8_t imm_lane_idx) {
3182 imm_lane_idx * 2);
3184 imm_lane_idx * 2 + 1);
3185}
3186
3188 LiftoffRegister src1,
3189 LiftoffRegister src2,
3190 uint8_t imm_lane_idx) {
3193 ReplaceLane(dst_simd, src1_simd, src2.low_gp(), NeonS32, imm_lane_idx * 2);
3194 ReplaceLane(dst_simd, dst_simd, src2.high_gp(), NeonS32,
3195 imm_lane_idx * 2 + 1);
3196}
3197
3199 LiftoffRegister src) {
3200 UseScratchRegisterScope temps(this);
3201 QwNeonRegister zero =
3202 dst == src ? temps.AcquireQ() : liftoff::GetSimd128Register(dst);
3203 vmov(zero, uint64_t{0});
3206}
3207
3212
3217
3223
3229
3235
3241
3247
3253
3259
3261 LiftoffRegister rhs) {
3262 UseScratchRegisterScope temps(this);
3263
3267
3268 // These temporary registers will be modified. We can directly modify lhs and
3269 // rhs if they are not uesd, saving on temporaries.
3270 QwNeonRegister tmp1 = left;
3271 QwNeonRegister tmp2 = right;
3272
3273 LiftoffRegList used_plus_dst =
3275
3276 if (used_plus_dst.has(lhs) && used_plus_dst.has(rhs)) {
3277 tmp1 = temps.AcquireQ();
3278 // We only have 1 scratch Q register, so acquire another ourselves.
3279 LiftoffRegList pinned{dst};
3280 LiftoffRegister unused_pair = GetUnusedRegister(kFpRegPair, pinned);
3281 tmp2 = liftoff::GetSimd128Register(unused_pair);
3282 } else if (used_plus_dst.has(lhs)) {
3283 tmp1 = temps.AcquireQ();
3284 } else if (used_plus_dst.has(rhs)) {
3285 tmp2 = temps.AcquireQ();
3286 }
3287
3288 // Algorithm from code-generator-arm.cc, refer to comments there for details.
3289 if (tmp1 != left) {
3290 vmov(tmp1, left);
3291 }
3292 if (tmp2 != right) {
3293 vmov(tmp2, right);
3294 }
3295
3296 vtrn(Neon32, tmp1.low(), tmp1.high());
3297 vtrn(Neon32, tmp2.low(), tmp2.high());
3298
3299 vmull(NeonU32, dst_neon, tmp1.low(), tmp2.high());
3300 vmlal(NeonU32, dst_neon, tmp1.high(), tmp2.low());
3301 vshl(NeonU64, dst_neon, dst_neon, 32);
3302
3303 vmlal(NeonU32, dst_neon, tmp1.low(), tmp2.low());
3304}
3305
3312
3319
3326
3333
3338
3343
3348
3353
3358
3363
3365 LiftoffRegister lhs,
3366 uint8_t imm_lane_idx) {
3368 imm_lane_idx);
3369}
3370
3372 LiftoffRegister src1,
3373 LiftoffRegister src2,
3374 uint8_t imm_lane_idx) {
3376 liftoff::GetSimd128Register(src1), src2.gp(), NeonS32,
3377 imm_lane_idx);
3378}
3379
3385
3387 LiftoffRegister src) {
3388 UseScratchRegisterScope temps(this);
3389 DwVfpRegister scratch = temps.AcquireD();
3390 vpmin(NeonU32, scratch, src.low_fp(), src.high_fp());
3391 vpmin(NeonU32, scratch, scratch, scratch);
3392 ExtractLane(dst.gp(), scratch, NeonS32, 0);
3393 cmp(dst.gp(), Operand(0));
3394 mov(dst.gp(), Operand(1), LeaveCC, ne);
3395}
3396
3398 LiftoffRegister src) {
3399 UseScratchRegisterScope temps(this);
3401 Simd128Register mask = temps.AcquireQ();
3402
3403 if (cache_state()->is_used(src)) {
3404 // We only have 1 scratch Q register, so try and reuse src.
3405 LiftoffRegList pinned{src};
3406 LiftoffRegister unused_pair = GetUnusedRegister(kFpRegPair, pinned);
3407 mask = liftoff::GetSimd128Register(unused_pair);
3408 }
3409
3411 // Set i-th bit of each lane i. When AND with tmp, the lanes that
3412 // are signed will have i-th bit set, unsigned will be 0.
3413 vmov(mask.low(), base::Double((uint64_t)0x0000'0002'0000'0001));
3414 vmov(mask.high(), base::Double((uint64_t)0x0000'0008'0000'0004));
3415 vand(tmp, mask, tmp);
3416 vpadd(Neon32, tmp.low(), tmp.low(), tmp.high());
3417 vpadd(Neon32, tmp.low(), tmp.low(), kDoubleRegZero);
3418 VmovLow(dst.gp(), tmp.low());
3419}
3420
3425
3431
3437
3443
3449
3455
3461
3467
3473
3480
3487
3494
3501
3503 LiftoffRegister lhs,
3504 LiftoffRegister rhs) {
3508
3509 UseScratchRegisterScope temps(this);
3510 Simd128Register scratch = temps.AcquireQ();
3511
3512 vmull(NeonS16, scratch, left.low(), right.low());
3513 vpadd(Neon32, dest.low(), scratch.low(), scratch.high());
3514
3515 vmull(NeonS16, scratch, left.high(), right.high());
3516 vpadd(Neon32, dest.high(), scratch.low(), scratch.high());
3517}
3518
3524
3530
3537
3544
3551
3558
3563
3569
3571 LiftoffRegister src) {
3572 UseScratchRegisterScope temps(this);
3573 DwVfpRegister scratch = temps.AcquireD();
3574 vpmin(NeonU16, scratch, src.low_fp(), src.high_fp());
3575 vpmin(NeonU16, scratch, scratch, scratch);
3576 vpmin(NeonU16, scratch, scratch, scratch);
3577 ExtractLane(dst.gp(), scratch, NeonS16, 0);
3578 cmp(dst.gp(), Operand(0));
3579 mov(dst.gp(), Operand(1), LeaveCC, ne);
3580}
3581
3583 LiftoffRegister src) {
3584 UseScratchRegisterScope temps(this);
3586 Simd128Register mask = temps.AcquireQ();
3587
3588 if (cache_state()->is_used(src)) {
3589 // We only have 1 scratch Q register, so try and reuse src.
3590 LiftoffRegList pinned{src};
3591 LiftoffRegister unused_pair = GetUnusedRegister(kFpRegPair, pinned);
3592 mask = liftoff::GetSimd128Register(unused_pair);
3593 }
3594
3596 // Set i-th bit of each lane i. When AND with tmp, the lanes that
3597 // are signed will have i-th bit set, unsigned will be 0.
3598 vmov(mask.low(), base::Double((uint64_t)0x0008'0004'0002'0001));
3599 vmov(mask.high(), base::Double((uint64_t)0x0080'0040'0020'0010));
3600 vand(tmp, mask, tmp);
3601 vpadd(Neon16, tmp.low(), tmp.low(), tmp.high());
3602 vpadd(Neon16, tmp.low(), tmp.low(), tmp.low());
3603 vpadd(Neon16, tmp.low(), tmp.low(), tmp.low());
3604 vmov(NeonU16, dst.gp(), tmp.low(), 0);
3605}
3606
3611
3617
3623
3629
3635
3641
3647
3654
3660
3667
3674
3680
3687
3694
3701
3708
3715
3717 LiftoffRegister lhs,
3718 uint8_t imm_lane_idx) {
3720 imm_lane_idx);
3721}
3722
3724 LiftoffRegister lhs,
3725 uint8_t imm_lane_idx) {
3727 imm_lane_idx);
3728}
3729
3731 LiftoffRegister src1,
3732 LiftoffRegister src2,
3733 uint8_t imm_lane_idx) {
3735 liftoff::GetSimd128Register(src1), src2.gp(), NeonS16,
3736 imm_lane_idx);
3737}
3738
3744
3750
3756
3762
3769
3776
3784
3792
3794 LiftoffRegister lhs,
3795 LiftoffRegister rhs) {
3799
3800 UseScratchRegisterScope temps(this);
3801 Simd128Register scratch = temps.AcquireQ();
3802
3803 vmull(NeonS8, scratch, left.low(), right.low());
3804 vpadd(Neon16, dest.low(), scratch.low(), scratch.high());
3805
3806 vmull(NeonS8, scratch, left.high(), right.high());
3807 vpadd(Neon16, dest.high(), scratch.low(), scratch.high());
3808}
3809
3811 LiftoffRegister lhs,
3812 LiftoffRegister rhs,
3813 LiftoffRegister acc) {
3814 DCHECK_NE(dst, acc);
3819
3820 UseScratchRegisterScope temps(this);
3821 Simd128Register scratch = temps.AcquireQ();
3822
3823 vmull(NeonS8, scratch, left.low(), right.low());
3824 vpadd(Neon16, dest.low(), scratch.low(), scratch.high());
3825
3826 vmull(NeonS8, scratch, left.high(), right.high());
3827 vpadd(Neon16, dest.high(), scratch.low(), scratch.high());
3828
3829 vpaddl(NeonS16, dest, dest);
3830 vadd(Neon32, dest, dest, accu);
3831}
3832
3834 LiftoffRegister lhs,
3835 LiftoffRegister rhs,
3836 const uint8_t shuffle[16],
3837 bool is_swizzle) {
3841 UseScratchRegisterScope temps(this);
3842 Simd128Register scratch = temps.AcquireQ();
3843 if ((src1 != src2) && src1.code() + 1 != src2.code()) {
3844 // vtbl requires the operands to be consecutive or the same.
3845 // If they are the same, we build a smaller list operand (table_size = 2).
3846 // If they are not the same, and not consecutive, we move the src1 and src2
3847 // to q14 and q15, which will be unused since they are not allocatable in
3848 // Liftoff. If the operands are the same, then we build a smaller list
3849 // operand below.
3850 static_assert(!kLiftoffAssemblerFpCacheRegs.has(d28),
3851 "This only works if q14-q15 (d28-d31) are not used.");
3852 static_assert(!kLiftoffAssemblerFpCacheRegs.has(d29),
3853 "This only works if q14-q15 (d28-d31) are not used.");
3854 static_assert(!kLiftoffAssemblerFpCacheRegs.has(d30),
3855 "This only works if q14-q15 (d28-d31) are not used.");
3856 static_assert(!kLiftoffAssemblerFpCacheRegs.has(d31),
3857 "This only works if q14-q15 (d28-d31) are not used.");
3858 vmov(q14, src1);
3859 src1 = q14;
3860 vmov(q15, src2);
3861 src2 = q15;
3862 }
3863
3864 int table_size = src1 == src2 ? 2 : 4;
3865
3866 int scratch_s_base = scratch.code() * 4;
3867 for (int j = 0; j < 4; j++) {
3868 uint32_t imm = 0;
3869 for (int i = 3; i >= 0; i--) {
3870 imm = (imm << 8) | shuffle[j * 4 + i];
3871 }
3872 DCHECK_EQ(0, imm & (table_size == 2 ? 0xF0F0F0F0 : 0xE0E0E0E0));
3873 // Ensure indices are in [0,15] if table_size is 2, or [0,31] if 4.
3874 vmov(SwVfpRegister::from_code(scratch_s_base + j), Float32::FromBits(imm));
3875 }
3876
3877 DwVfpRegister table_base = src1.low();
3878 NeonListOperand table(table_base, table_size);
3879
3880 if (dest != src1 && dest != src2) {
3881 vtbl(dest.low(), table, scratch.low());
3882 vtbl(dest.high(), table, scratch.high());
3883 } else {
3884 vtbl(scratch.low(), table, scratch.low());
3885 vtbl(scratch.high(), table, scratch.high());
3886 vmov(dest, scratch);
3887 }
3888}
3889
3894
3899
3901 LiftoffRegister lhs,
3902 uint8_t imm_lane_idx) {
3903 ExtractLane(dst.gp(), liftoff::GetSimd128Register(lhs), NeonU8, imm_lane_idx);
3904}
3905
3907 LiftoffRegister lhs,
3908 uint8_t imm_lane_idx) {
3909 ExtractLane(dst.gp(), liftoff::GetSimd128Register(lhs), NeonS8, imm_lane_idx);
3910}
3911
3913 LiftoffRegister src1,
3914 LiftoffRegister src2,
3915 uint8_t imm_lane_idx) {
3917 liftoff::GetSimd128Register(src1), src2.gp(), NeonS8,
3918 imm_lane_idx);
3919}
3920
3926
3931
3933 LiftoffRegister src) {
3934 UseScratchRegisterScope temps(this);
3935 DwVfpRegister scratch = temps.AcquireD();
3936 vpmin(NeonU8, scratch, src.low_fp(), src.high_fp());
3937 vpmin(NeonU8, scratch, scratch, scratch);
3938 vpmin(NeonU8, scratch, scratch, scratch);
3939 vpmin(NeonU8, scratch, scratch, scratch);
3940 ExtractLane(dst.gp(), scratch, NeonS8, 0);
3941 cmp(dst.gp(), Operand(0));
3942 mov(dst.gp(), Operand(1), LeaveCC, ne);
3943}
3944
3946 LiftoffRegister src) {
3947 UseScratchRegisterScope temps(this);
3949 Simd128Register mask = temps.AcquireQ();
3950
3951 if (cache_state()->is_used(src)) {
3952 // We only have 1 scratch Q register, so try and reuse src.
3953 LiftoffRegList pinned{src};
3954 LiftoffRegister unused_pair = GetUnusedRegister(kFpRegPair, pinned);
3955 mask = liftoff::GetSimd128Register(unused_pair);
3956 }
3957
3959 // Set i-th bit of each lane i. When AND with tmp, the lanes that
3960 // are signed will have i-th bit set, unsigned will be 0.
3961 vmov(mask.low(), base::Double((uint64_t)0x8040'2010'0804'0201));
3962 vmov(mask.high(), base::Double((uint64_t)0x8040'2010'0804'0201));
3963 vand(tmp, mask, tmp);
3964 vext(mask, tmp, tmp, 8);
3965 vzip(Neon8, mask, tmp);
3966 vpadd(Neon16, tmp.low(), tmp.low(), tmp.high());
3967 vpadd(Neon16, tmp.low(), tmp.low(), tmp.low());
3968 vpadd(Neon16, tmp.low(), tmp.low(), tmp.low());
3969 vmov(NeonU16, dst.gp(), tmp.low(), 0);
3970}
3971
3976
3982
3988
3993
3999
4004
4010
4017
4023
4030
4037
4044
4051
4058
4065
4072
4078
4085
4091
4097
4103
4109
4115
4122
4128
4134
4140
4146
4152
4159
4165
4171
4177
4183
4189
4195
4201
4207
4213
4220
4226
4232
4237
4242
4247
4252
4254 const uint8_t imms[16]) {
4255 uint64_t vals[2];
4256 memcpy(vals, imms, sizeof(vals));
4257 vmov(dst.low_fp(), base::Double(vals[0]));
4258 vmov(dst.high_fp(), base::Double(vals[1]));
4259}
4260
4264
4270
4276
4282
4293
4299
4305
4311
4317
4319 LiftoffRegister src) {
4321 vcvt_f32_f64(dst_d.low(), src.low_fp());
4322 vcvt_f32_f64(dst_d.high(), src.high_fp());
4323 vmov(dst.high_fp(), 0);
4324}
4325
4331
4337
4343
4349
4354
4359
4364
4369
4374
4379
4384
4389
4391 LiftoffRegister src) {
4393 vcvt_s32_f64(dst_d.low(), src.low_fp());
4394 vcvt_s32_f64(dst_d.high(), src.high_fp());
4395 vmov(dst.high_fp(), 0);
4396}
4397
4399 LiftoffRegister src) {
4401 vcvt_u32_f64(dst_d.low(), src.low_fp());
4402 vcvt_u32_f64(dst_d.high(), src.high_fp());
4403 vmov(dst.high_fp(), 0);
4404}
4405
4412
4419
4426
4432
4438
4444
4449
4451 LiftoffRegister src1,
4452 LiftoffRegister src2,
4453 LiftoffRegister src3) {
4454 UseScratchRegisterScope temps(this);
4455 Simd128Register scratch =
4456 dst == src3 ? temps.AcquireQ() : liftoff::GetSimd128Register(dst);
4457 vmul(scratch, liftoff::GetSimd128Register(src1),
4460 scratch);
4461}
4462
4464 LiftoffRegister src1,
4465 LiftoffRegister src2,
4466 LiftoffRegister src3) {
4467 UseScratchRegisterScope temps(this);
4468 Simd128Register scratch =
4469 dst == src3 ? temps.AcquireQ() : liftoff::GetSimd128Register(dst);
4470 vmul(scratch, liftoff::GetSimd128Register(src1),
4473 scratch);
4474}
4475
4477 LiftoffRegister src1,
4478 LiftoffRegister src2,
4479 LiftoffRegister src3) {
4480 UseScratchRegisterScope temps(this);
4481 Simd128Register scratch =
4482 dst == src3 ? temps.AcquireQ() : liftoff::GetSimd128Register(dst);
4483 vmul(scratch.low(), src1.low_fp(), src2.low_fp());
4484 vmul(scratch.high(), src1.high_fp(), src2.high_fp());
4485 vadd(dst.low_fp(), src3.low_fp(), scratch.low());
4486 vadd(dst.high_fp(), src3.high_fp(), scratch.high());
4487}
4488
4490 LiftoffRegister src1,
4491 LiftoffRegister src2,
4492 LiftoffRegister src3) {
4493 UseScratchRegisterScope temps(this);
4494 Simd128Register scratch =
4495 dst == src3 ? temps.AcquireQ() : liftoff::GetSimd128Register(dst);
4496 vmul(scratch.low(), src1.low_fp(), src2.low_fp());
4497 vmul(scratch.high(), src1.high_fp(), src2.high_fp());
4498 vsub(dst.low_fp(), src3.low_fp(), scratch.low());
4499 vsub(dst.high_fp(), src3.high_fp(), scratch.high());
4500}
4501
4503 LiftoffRegister src) {
4504 return false;
4505}
4506
4508 LiftoffRegister lhs,
4509 uint8_t imm_lane_idx) {
4510 return false;
4511}
4512
4514 LiftoffRegister src1,
4515 LiftoffRegister src2,
4516 uint8_t imm_lane_idx) {
4517 return false;
4518}
4519
4521 LiftoffRegister src) {
4522 return false;
4523}
4524
4526 LiftoffRegister src) {
4527 return false;
4528}
4529
4531 LiftoffRegister src) {
4532 return false;
4533}
4534
4536 LiftoffRegister src) {
4537 return false;
4538}
4539
4541 LiftoffRegister src) {
4542 return false;
4543}
4544
4546 LiftoffRegister src) {
4547 return false;
4548}
4549
4551 LiftoffRegister src) {
4552 return false;
4553}
4554
4556 LiftoffRegister rhs) {
4557 return false;
4558}
4559
4561 LiftoffRegister rhs) {
4562 return false;
4563}
4564
4566 LiftoffRegister rhs) {
4567 return false;
4568}
4569
4571 LiftoffRegister rhs) {
4572 return false;
4573}
4574
4576 LiftoffRegister rhs) {
4577 return false;
4578}
4579
4581 LiftoffRegister rhs) {
4582 return false;
4583}
4584
4586 LiftoffRegister rhs) {
4587 return false;
4588}
4589
4591 LiftoffRegister rhs) {
4592 return false;
4593}
4594
4596 LiftoffRegister rhs) {
4597 return false;
4598}
4599
4601 LiftoffRegister rhs) {
4602 return false;
4603}
4604
4606 LiftoffRegister rhs) {
4607 return false;
4608}
4609
4611 LiftoffRegister rhs) {
4612 return false;
4613}
4614
4619
4624
4629
4634
4639
4644
4649
4651 LiftoffRegister src1,
4652 LiftoffRegister src2,
4653 LiftoffRegister src3) {
4654 return false;
4655}
4656
4658 LiftoffRegister src1,
4659 LiftoffRegister src2,
4660 LiftoffRegister src3) {
4661 return false;
4662}
4663
4665
4667 UseScratchRegisterScope temps(this);
4668 Register limit_address = temps.Acquire();
4670 cmp(sp, limit_address);
4671 b(ool_code, ls);
4672}
4673
4675 // Asserts unreachable within the wasm code.
4677}
4678
4680 RegList core_regs = regs.GetGpList();
4681 if (!core_regs.is_empty()) {
4682 stm(db_w, sp, core_regs);
4683 }
4684 LiftoffRegList fp_regs = regs & kFpCacheRegList;
4685 while (!fp_regs.is_empty()) {
4687 DoubleRegister first = reg.fp();
4688 DoubleRegister last = first;
4689 fp_regs.clear(reg);
4690 while (!fp_regs.is_empty()) {
4692 int code = reg.fp().code();
4693 // vstm can not push more than 16 registers. We have to make sure the
4694 // condition is met.
4695 if ((code != last.code() + 1) || ((code - first.code() + 1) > 16)) break;
4696 last = reg.fp();
4697 fp_regs.clear(reg);
4698 }
4699 vstm(db_w, sp, first, last);
4700 }
4701}
4702
4704 LiftoffRegList fp_regs = regs & kFpCacheRegList;
4705 while (!fp_regs.is_empty()) {
4706 LiftoffRegister reg = fp_regs.GetLastRegSet();
4707 DoubleRegister last = reg.fp();
4708 DoubleRegister first = last;
4709 fp_regs.clear(reg);
4710 while (!fp_regs.is_empty()) {
4711 LiftoffRegister reg = fp_regs.GetLastRegSet();
4712 int code = reg.fp().code();
4713 if ((code != first.code() - 1) || ((last.code() - code + 1) > 16)) break;
4714 first = reg.fp();
4715 fp_regs.clear(reg);
4716 }
4717 vldm(ia_w, sp, first, last);
4718 }
4719 RegList core_regs = regs.GetGpList();
4720 if (!core_regs.is_empty()) {
4721 ldm(ia_w, sp, core_regs);
4722 }
4723}
4724
4726 SafepointTableBuilder::Safepoint& safepoint, LiftoffRegList all_spills,
4727 LiftoffRegList ref_spills, int spill_offset) {
4728 LiftoffRegList fp_spills = all_spills & kFpCacheRegList;
4729 int spill_space_size = fp_spills.GetNumRegsSet() * kSimd128Size;
4730 LiftoffRegList gp_spills = all_spills & kGpCacheRegList;
4731 while (!gp_spills.is_empty()) {
4732 LiftoffRegister reg = gp_spills.GetLastRegSet();
4733 if (ref_spills.has(reg)) {
4734 safepoint.DefineTaggedStackSlot(spill_offset);
4735 }
4736 gp_spills.clear(reg);
4737 ++spill_offset;
4738 spill_space_size += kSystemPointerSize;
4739 }
4740 // Record the number of additional spill slots.
4741 RecordOolSpillSpaceSize(spill_space_size);
4742}
4743
4744void LiftoffAssembler::DropStackSlotsAndRet(uint32_t num_stack_slots) {
4745 Drop(num_stack_slots);
4746 Ret();
4747}
4748
4750 const std::initializer_list<VarState> args, const LiftoffRegister* rets,
4751 ValueKind return_kind, ValueKind out_argument_kind, int stack_bytes,
4752 ExternalReference ext_ref) {
4753 // Arguments are passed by pushing them all to the stack and then passing
4754 // a pointer to them.
4755 DCHECK(IsAligned(stack_bytes, kSystemPointerSize));
4756 // Reserve space in the stack.
4757 AllocateStackSpace(stack_bytes);
4758
4759 int arg_offset = 0;
4760 for (const VarState& arg : args) {
4761 MemOperand dst{sp, arg_offset};
4762 if (arg.is_reg()) {
4763 liftoff::Store(this, arg.reg(), dst, arg.kind());
4764 } else if (arg.is_const()) {
4765 DCHECK_EQ(kI32, arg.kind());
4766 UseScratchRegisterScope temps(this);
4767 Register src = temps.Acquire();
4768 mov(src, Operand(arg.i32_const()));
4769 str(src, dst);
4770 } else {
4771 // Stack to stack move.
4772 UseScratchRegisterScope temps(this);
4773 Register scratch = temps.Acquire();
4774 MemOperand src = liftoff::GetStackSlot(arg.offset());
4775 int words = SlotSizeForType(arg.kind()) / kSystemPointerSize;
4776 do {
4777 ldr(scratch, src);
4778 str(scratch, dst);
4779 src.set_offset(src.offset() + kSystemPointerSize);
4780 dst.set_offset(dst.offset() + kSystemPointerSize);
4781 } while (--words > 0);
4782 }
4783 arg_offset += value_kind_size(arg.kind());
4784 }
4785 DCHECK_LE(arg_offset, stack_bytes);
4786
4787 // Pass a pointer to the buffer with the arguments to the C function.
4788 mov(r0, sp);
4789
4790 // Now call the C function.
4791 constexpr int kNumCCallArgs = 1;
4792 PrepareCallCFunction(kNumCCallArgs);
4793 CallCFunction(ext_ref, kNumCCallArgs);
4794
4795 // Move return value to the right register.
4796 const LiftoffRegister* result_reg = rets;
4797 if (return_kind != kVoid) {
4798 constexpr Register kReturnReg = r0;
4799 if (kReturnReg != rets->gp()) {
4800 Move(*rets, LiftoffRegister(kReturnReg), return_kind);
4801 }
4802 result_reg++;
4803 }
4804
4805 // Load potential output value from the buffer on the stack.
4806 if (out_argument_kind != kVoid) {
4807 liftoff::Load(this, *result_reg, MemOperand{sp}, out_argument_kind);
4808 }
4809 add(sp, sp, Operand(stack_bytes));
4810}
4811
4812void LiftoffAssembler::CallC(const std::initializer_list<VarState> args,
4813 ExternalReference ext_ref) {
4814 // First, prepare the stack for the C call.
4815 int num_args = static_cast<int>(args.size());
4816 PrepareCallCFunction(num_args);
4817
4818 // Then execute the parallel register move and also move values to parameter
4819 // stack slots.
4820 int reg_args = 0;
4821 int stack_args = 0;
4822 ParallelMove parallel_move{this};
4823 for (const VarState& arg : args) {
4824 if (needs_gp_reg_pair(arg.kind())) {
4825 // All i64 arguments (currently) fully fit in the register parameters.
4826 DCHECK_LE(reg_args + 2, arraysize(kCArgRegs));
4827 parallel_move.LoadIntoRegister(
4829 kCArgRegs[reg_args + 1]),
4830 arg);
4831 reg_args += 2;
4832 continue;
4833 }
4834 if (reg_args < int{arraysize(kCArgRegs)}) {
4835 parallel_move.LoadIntoRegister(LiftoffRegister{kCArgRegs[reg_args]}, arg);
4836 ++reg_args;
4837 continue;
4838 }
4839 MemOperand dst{sp, stack_args * kSystemPointerSize};
4840 ++stack_args;
4841 if (arg.is_reg()) {
4842 liftoff::Store(this, arg.reg(), dst, arg.kind());
4843 continue;
4844 }
4845 UseScratchRegisterScope temps(this);
4846 Register scratch = temps.Acquire();
4847 if (arg.is_const()) {
4848 DCHECK_EQ(kI32, arg.kind());
4849 mov(scratch, Operand(arg.i32_const()));
4850 str(scratch, dst);
4851 } else {
4852 // Stack to stack move.
4853 MemOperand src = liftoff::GetStackSlot(arg.offset());
4854 ldr(scratch, src);
4855 str(scratch, dst);
4856 }
4857 }
4858 parallel_move.Execute();
4859
4860 // Now call the C function.
4861 CallCFunction(ext_ref, num_args);
4862}
4863
4867
4871
4873 compiler::CallDescriptor* call_descriptor,
4874 Register target) {
4875 DCHECK(target != no_reg);
4876 CallWasmCodePointer(target);
4877}
4878
4880 compiler::CallDescriptor* call_descriptor, Register target) {
4881 DCHECK(target != no_reg);
4882 CallWasmCodePointer(target, CallJumpMode::kTailCall);
4883}
4884
4886 // A direct call to a builtin. Just encode the builtin index. This will be
4887 // patched at relocation.
4888 Call(static_cast<Address>(builtin), RelocInfo::WASM_STUB_CALL);
4889}
4890
4892 AllocateStackSpace(size);
4893 mov(addr, sp);
4894}
4895
4897 add(sp, sp, Operand(size));
4898}
4899
4901
4903 DoubleRegister src,
4904 ValueKind kind) {
4905 if (kind == kF32) {
4907 VFPCompareAndSetFlags(src_f, src_f);
4908 } else {
4910 VFPCompareAndSetFlags(src, src);
4911 }
4912
4913 // Store a non-zero value if src is NaN.
4914 str(dst, MemOperand(dst), ne); // x != x iff isnan(x)
4915}
4916
4918 LiftoffRegister src,
4919 Register tmp_gp,
4920 LiftoffRegister tmp_s128,
4921 ValueKind lane_kind) {
4924 if (lane_kind == kF32) {
4925 vpadd(tmp_q.low(), src_q.low(), src_q.high());
4926 LowDwVfpRegister tmp_d =
4928 vadd(tmp_d.low(), tmp_d.low(), tmp_d.high());
4929 } else {
4930 DCHECK_EQ(lane_kind, kF64);
4931 vadd(tmp_q.low(), src_q.low(), src_q.high());
4932 }
4933 emit_store_nonzero_if_nan(dst, tmp_q.low(), lane_kind);
4934}
4935
4939
4940void LiftoffStackSlots::Construct(int param_slots) {
4941 DCHECK_LT(0, slots_.size());
4943 int last_stack_slot = param_slots;
4944 for (auto& slot : slots_) {
4945 const int stack_slot = slot.dst_slot_;
4946 int stack_decrement = (last_stack_slot - stack_slot) * kSystemPointerSize;
4947 DCHECK_LT(0, stack_decrement);
4948 last_stack_slot = stack_slot;
4949 const LiftoffAssembler::VarState& src = slot.src_;
4950 switch (src.loc()) {
4952 switch (src.kind()) {
4953 // i32 and i64 can be treated as similar cases, i64 being previously
4954 // split into two i32 registers
4955 case kI32:
4956 case kI64:
4957 case kF32:
4958 case kRef:
4959 case kRefNull: {
4960 asm_->AllocateStackSpace(stack_decrement - kSystemPointerSize);
4962 Register scratch = temps.Acquire();
4963 asm_->ldr(scratch,
4964 liftoff::GetHalfStackSlot(slot.src_offset_, slot.half_));
4965 asm_->Push(scratch);
4966 } break;
4967 case kF64: {
4968 asm_->AllocateStackSpace(stack_decrement - kDoubleSize);
4970 DwVfpRegister scratch = temps.AcquireD();
4971 asm_->vldr(scratch, liftoff::GetStackSlot(slot.src_offset_));
4972 asm_->vpush(scratch);
4973 } break;
4974 case kS128: {
4975 asm_->AllocateStackSpace(stack_decrement - kSimd128Size);
4976 MemOperand mem_op = liftoff::GetStackSlot(slot.src_offset_);
4979 asm_, &temps, mem_op.rn(), no_reg, mem_op.offset());
4980 QwNeonRegister scratch = temps.AcquireQ();
4981 asm_->vld1(Neon8, NeonListOperand(scratch), NeonMemOperand(addr));
4982 asm_->vpush(scratch);
4983 break;
4984 }
4985 default:
4986 UNREACHABLE();
4987 }
4988 break;
4989 }
4991 int pushed_bytes = SlotSizeInBytes(slot);
4992 asm_->AllocateStackSpace(stack_decrement - pushed_bytes);
4993 switch (src.kind()) {
4994 case kI64: {
4996 slot.half_ == kLowWord ? src.reg().low() : src.reg().high();
4997 asm_->push(reg.gp());
4998 break;
4999 }
5000 case kI32:
5001 case kRef:
5002 case kRefNull:
5003 asm_->push(src.reg().gp());
5004 break;
5005 case kF32:
5006 asm_->vpush(liftoff::GetFloatRegister(src.reg().fp()));
5007 break;
5008 case kF64:
5009 asm_->vpush(src.reg().fp());
5010 break;
5011 case kS128:
5013 break;
5014 default:
5015 UNREACHABLE();
5016 }
5017 break;
5018 }
5020 asm_->AllocateStackSpace(stack_decrement - kSystemPointerSize);
5021 DCHECK(src.kind() == kI32 || src.kind() == kI64);
5023 Register scratch = temps.Acquire();
5024 // The high word is the sign extension of the low word.
5025 asm_->mov(scratch,
5026 Operand(slot.half_ == kLowWord ? src.i32_const()
5027 : src.i32_const() >> 31));
5028 asm_->push(scratch);
5029 break;
5030 }
5031 }
5032 }
5033}
5034
5035} // namespace v8::internal::wasm
5036
5037#endif // V8_WASM_BASELINE_ARM_LIFTOFF_ASSEMBLER_ARM_INL_H_
Builtins::Kind kind
Definition builtins.cc:40
V8_INLINE void RecordComment(const char *comment, const SourceLocation &loc=SourceLocation::Current())
Definition assembler.h:417
void uxtb(Register dst, Register src, int rotate=0, Condition cond=al)
void vshl(NeonDataType dt, QwNeonRegister dst, QwNeonRegister src, int shift)
void vmin(QwNeonRegister dst, QwNeonRegister src1, QwNeonRegister src2)
void vceq(QwNeonRegister dst, QwNeonRegister src1, QwNeonRegister src2)
void vmovl(NeonDataType dt, QwNeonRegister dst, DwVfpRegister src)
void vcvt_u32_f64(const SwVfpRegister dst, const DwVfpRegister src, VFPConversionMode mode=kDefaultRoundToZero, const Condition cond=al)
void vqsub(NeonDataType dt, QwNeonRegister dst, QwNeonRegister src1, QwNeonRegister src2)
void vshr(NeonDataType dt, DwVfpRegister dst, DwVfpRegister src, int shift)
void and_(Register dst, Register src1, const Operand &src2, SBit s=LeaveCC, Condition cond=al)
void vneg(const DwVfpRegister dst, const DwVfpRegister src, const Condition cond=al)
void vldm(BlockAddrMode am, Register base, DwVfpRegister first, DwVfpRegister last, Condition cond=al)
void vzip(NeonSize size, DwVfpRegister src1, DwVfpRegister src2)
void strex(Register src1, Register src2, Register dst, Condition cond=al)
void mls(Register dst, Register src1, Register src2, Register srcA, Condition cond=al)
void vcge(QwNeonRegister dst, QwNeonRegister src1, QwNeonRegister src2)
void b(int branch_offset, Condition cond=al, RelocInfo::Mode rmode=RelocInfo::NO_INFO)
void stm(BlockAddrMode am, Register base, RegList src, Condition cond=al)
void vcvt_f32_u32(const SwVfpRegister dst, const SwVfpRegister src, VFPConversionMode mode=kDefaultRoundToZero, const Condition cond=al)
void sxtb(Register dst, Register src, int rotate=0, Condition cond=al)
void vpadd(DwVfpRegister dst, DwVfpRegister src1, DwVfpRegister src2)
void j(Condition cc, Label *L, Label::Distance distance=Label::kFar)
void mul(Register dst, Register src1, Register src2, SBit s=LeaveCC, Condition cond=al)
void tbl(const VRegister &vd, const VRegister &vn, const VRegister &vm)
bool ImmediateFitsAddrMode2Instruction(int32_t imm32)
void add(Register dst, Register src1, const Operand &src2, SBit s=LeaveCC, Condition cond=al)
void CheckConstPool(bool force_emit, bool require_jump)
void vmvn(QwNeonRegister dst, QwNeonRegister src)
void vmov(const SwVfpRegister dst, Float32 imm)
void vsqrt(const DwVfpRegister dst, const DwVfpRegister src, const Condition cond=al)
void vld1r(NeonSize size, const NeonListOperand &dst, const NeonMemOperand &src)
void vcvt_f64_s32(const DwVfpRegister dst, const SwVfpRegister src, VFPConversionMode mode=kDefaultRoundToZero, const Condition cond=al)
void cmp(Register src1, const Operand &src2, Condition cond=al)
void ldrexb(Register dst, Register src, Condition cond=al)
void sxth(Register dst, Register src, int rotate=0, Condition cond=al)
void vqmovn(NeonDataType dst_dt, NeonDataType src_dt, DwVfpRegister dst, QwNeonRegister src)
void lsr(Register dst, Register src1, const Operand &src2, SBit s=LeaveCC, Condition cond=al)
void vtbl(DwVfpRegister dst, const NeonListOperand &list, DwVfpRegister index)
void vpmin(NeonDataType dt, DwVfpRegister dst, DwVfpRegister src1, DwVfpRegister src2)
void vpaddl(NeonDataType dt, QwNeonRegister dst, QwNeonRegister src)
void vcvt_f64_f32(const DwVfpRegister dst, const SwVfpRegister src, VFPConversionMode mode=kDefaultRoundToZero, const Condition cond=al)
void str(Register src, const MemOperand &dst, Condition cond=al)
void vstm(BlockAddrMode am, Register base, DwVfpRegister first, DwVfpRegister last, Condition cond=al)
void ldrexd(Register dst1, Register dst2, Register src, Condition cond=al)
void ldrh(Register dst, const MemOperand &src, Condition cond=al)
void rbit(Register dst, Register src, Condition cond=al)
void vmlal(NeonDataType size, QwNeonRegister dst, DwVfpRegister src1, DwVfpRegister src2)
void clz(Register dst, Register src, Condition cond=al)
void vbsl(QwNeonRegister dst, QwNeonRegister src1, QwNeonRegister src2)
void vcvt_u32_f32(const SwVfpRegister dst, const SwVfpRegister src, VFPConversionMode mode=kDefaultRoundToZero, const Condition cond=al)
void dmb(BarrierOption option)
void sub(Register dst, Register src1, const Operand &src2, SBit s=LeaveCC, Condition cond=al)
void vorr(QwNeonRegister dst, QwNeonRegister src1, QwNeonRegister src2)
void vrintn(const SwVfpRegister dst, const SwVfpRegister src)
void udiv(Register dst, Register src1, Register src2, Condition cond=al)
void vrhadd(NeonDataType dt, QwNeonRegister dst, QwNeonRegister src1, QwNeonRegister src2)
void vcgt(QwNeonRegister dst, QwNeonRegister src1, QwNeonRegister src2)
void vld1(NeonSize size, const NeonListOperand &dst, const NeonMemOperand &src)
void vrintz(const SwVfpRegister dst, const SwVfpRegister src, const Condition cond=al)
void strb(Register src, const MemOperand &dst, Condition cond=al)
void veor(DwVfpRegister dst, DwVfpRegister src1, DwVfpRegister src2)
void lsl(Register dst, Register src1, const Operand &src2, SBit s=LeaveCC, Condition cond=al)
void vqrdmulh(NeonDataType dt, QwNeonRegister dst, QwNeonRegister src1, QwNeonRegister src2)
void ldrb(Register dst, const MemOperand &src, Condition cond=al)
void vcvt_f32_f64(const SwVfpRegister dst, const DwVfpRegister src, VFPConversionMode mode=kDefaultRoundToZero, const Condition cond=al)
void vand(QwNeonRegister dst, QwNeonRegister src1, QwNeonRegister src2)
void vpmax(NeonDataType dt, DwVfpRegister dst, DwVfpRegister src1, DwVfpRegister src2)
void mla(Register dst, Register src1, Register src2, Register srcA, SBit s=LeaveCC, Condition cond=al)
void eor(Register dst, Register src1, const Operand &src2, SBit s=LeaveCC, Condition cond=al)
void uxth(Register dst, Register src, int rotate=0, Condition cond=al)
void vabs(const DwVfpRegister dst, const DwVfpRegister src, const Condition cond=al)
void mov(Register dst, const Operand &src, SBit s=LeaveCC, Condition cond=al)
void vdiv(const DwVfpRegister dst, const DwVfpRegister src1, const DwVfpRegister src2, const Condition cond=al)
void strh(Register src, const MemOperand &dst, Condition cond=al)
void vrintp(const SwVfpRegister dst, const SwVfpRegister src)
void vcvt_f64_u32(const DwVfpRegister dst, const SwVfpRegister src, VFPConversionMode mode=kDefaultRoundToZero, const Condition cond=al)
void vrintm(const SwVfpRegister dst, const SwVfpRegister src)
void sdiv(Register dst, Register src1, Register src2, Condition cond=al)
void vcvt_s32_f64(const SwVfpRegister dst, const DwVfpRegister src, VFPConversionMode mode=kDefaultRoundToZero, const Condition cond=al)
void umull(Register dstL, Register dstH, Register src1, Register src2, SBit s=LeaveCC, Condition cond=al)
void vqadd(NeonDataType dt, QwNeonRegister dst, QwNeonRegister src1, QwNeonRegister src2)
void vdup(NeonSize size, QwNeonRegister dst, Register src)
void vcvt_s32_f32(const SwVfpRegister dst, const SwVfpRegister src, VFPConversionMode mode=kDefaultRoundToZero, const Condition cond=al)
void strexh(Register src1, Register src2, Register dst, Condition cond=al)
void vld1s(NeonSize size, const NeonListOperand &dst, uint8_t index, const NeonMemOperand &src)
void orr(Register dst, Register src1, const Operand &src2, SBit s=LeaveCC, Condition cond=al)
void ldm(BlockAddrMode am, Register base, RegList dst, Condition cond=al)
void vstr(const DwVfpRegister src, const Register base, int offset, const Condition cond=al)
void vsub(const DwVfpRegister dst, const DwVfpRegister src1, const DwVfpRegister src2, const Condition cond=al)
void tst(Register src1, const Operand &src2, Condition cond=al)
void vcvt_f32_s32(const SwVfpRegister dst, const SwVfpRegister src, VFPConversionMode mode=kDefaultRoundToZero, const Condition cond=al)
void vext(QwNeonRegister dst, QwNeonRegister src1, QwNeonRegister src2, int bytes)
void ldr(Register dst, const MemOperand &src, Condition cond=al)
void bic(Register dst, Register src1, const Operand &src2, SBit s=LeaveCC, Condition cond=al)
void vmul(const DwVfpRegister dst, const DwVfpRegister src1, const DwVfpRegister src2, const Condition cond=al)
void vcnt(QwNeonRegister dst, QwNeonRegister src)
void vtrn(NeonSize size, DwVfpRegister src1, DwVfpRegister src2)
void ldrexh(Register dst, Register src, Condition cond=al)
void vadd(const DwVfpRegister dst, const DwVfpRegister src1, const DwVfpRegister src2, const Condition cond=al)
void vbic(QwNeonRegister dst, QwNeonRegister src1, QwNeonRegister src2)
void ldrex(Register dst, Register src, Condition cond=al)
void bx(Register target, Condition cond=al)
void vst1(NeonSize size, const NeonListOperand &src, const NeonMemOperand &dst)
void strexb(Register src1, Register src2, Register dst, Condition cond=al)
void AbortedCodeGeneration() override
void stop(Condition cond=al, int32_t code=kDefaultStopCode)
void vldr(const DwVfpRegister dst, const Register base, int offset, const Condition cond=al)
void vmull(NeonDataType size, QwNeonRegister dst, DwVfpRegister src1, DwVfpRegister src2)
void vpush(QwNeonRegister src, Condition cond=al)
void vmax(QwNeonRegister dst, QwNeonRegister src1, QwNeonRegister src2)
void asr(Register dst, Register src1, const Operand &src2, SBit s=LeaveCC, Condition cond=al)
static constexpr int kFixedFrameSizeAboveFp
static bool IsSupported(CpuFeature f)
static V8_EXPORT_PRIVATE ExternalReference isolate_address()
static constexpr int kPcLoadDelta
SwVfpRegister high() const
SwVfpRegister low() const
static constexpr MachineType Uint8()
static constexpr MachineType Int32()
static constexpr MachineType Uint32()
static constexpr MachineType Uint16()
static constexpr MachineType Int16()
static constexpr MachineType Int64()
static constexpr MachineType Int8()
void LoadStackLimit(Register destination, StackLimitKind kind)
void Call(Register target, Condition cond=al)
void Drop(int count, Condition cond=al)
void mov(Register rd, Register rj)
void I64x2GtS(QwNeonRegister dst, QwNeonRegister src1, QwNeonRegister src2)
void I64x2Eq(QwNeonRegister dst, QwNeonRegister src1, QwNeonRegister src2)
void I64x2AllTrue(Register dst, QwNeonRegister src)
void LoadLane(NeonSize sz, NeonListOperand dst_list, uint8_t lane, NeonMemOperand src)
void Move(Register dst, Tagged< Smi > smi)
void I64x2Ne(QwNeonRegister dst, QwNeonRegister src1, QwNeonRegister src2)
void ReplaceLane(QwNeonRegister dst, QwNeonRegister src, Register src_lane, NeonDataType dt, int lane)
void JumpIfSmi(Register value, Label *smi_label)
void AssertUnreachable(AbortReason reason) NOOP_UNLESS_DEBUG_CODE
void LsrPair(Register dst_low, Register dst_high, Register src_low, Register src_high, Register shift)
void F64x2PromoteLowF32x4(QwNeonRegister dst, QwNeonRegister src)
void VFPCompareAndSetFlags(const SwVfpRegister src1, const SwVfpRegister src2, const Condition cond=al)
void AsrPair(Register dst_low, Register dst_high, Register src_low, Register src_high, Register shift)
void VmovLow(Register dst, DwVfpRegister src)
void PushCommonFrame(Register marker_reg=no_reg)
void LslPair(Register dst_low, Register dst_high, Register src_low, Register src_high, Register shift)
void I64x2GeS(QwNeonRegister dst, QwNeonRegister src1, QwNeonRegister src2)
void Jump(Register target, Condition cond=al)
void F64x2ConvertLowI32x4U(QwNeonRegister dst, QwNeonRegister src)
void StoreLane(NeonSize sz, NeonListOperand src_list, uint8_t lane, NeonMemOperand dst)
void CheckPageFlag(Register object, int mask, Condition cc, Label *condition_met)
int CallCFunction(ExternalReference function, int num_arguments, SetIsolateDataSlots set_isolate_data_slots=SetIsolateDataSlots::kYes, Label *return_label=nullptr)
void VmovHigh(Register dst, DwVfpRegister src)
void I64x2BitMask(Register dst, QwNeonRegister src)
void ExtractLane(Register dst, QwNeonRegister src, NeonDataType dt, int lane)
void I64x2Abs(QwNeonRegister dst, QwNeonRegister src)
void F64x2ConvertLowI32x4S(QwNeonRegister dst, QwNeonRegister src)
void AllocateStackSpace(Register bytes)
void CallRecordWriteStubSaveRegisters(Register object, Operand offset, SaveFPRegsMode fp_mode, StubCallMode mode=StubCallMode::kCallBuiltinPointer)
void PrepareCallCFunction(int num_reg_arguments, int num_double_registers=0, Register scratch=no_reg)
static constexpr MainThreadFlags kPointersToHereAreInterestingMask
static constexpr MainThreadFlags kPointersFromHereAreInterestingMask
DwVfpRegister low() const
DwVfpRegister high() const
constexpr bool is_empty() const
constexpr bool has(RegisterT reg) const
static constexpr LowDwVfpRegister from_code(int8_t code)
constexpr int8_t code() const
Safepoint DefineSafepoint(Assembler *assembler, int pc_offset=0)
static constexpr Tagged< Smi > FromInt(int value)
Definition smi.h:38
static constexpr int32_t TypeToMarker(Type type)
Definition frames.h:196
static constexpr int kFrameTypeOffset
void emit_i8x16_swizzle(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i64x2_uconvert_i32x4_low(LiftoffRegister dst, LiftoffRegister src)
bool emit_f16x8_nearest_int(LiftoffRegister dst, LiftoffRegister src)
void emit_store_nonzero_if_nan(Register dst, DoubleRegister src, ValueKind kind)
bool emit_f32x4_floor(LiftoffRegister dst, LiftoffRegister src)
void emit_f64_div(DoubleRegister dst, DoubleRegister lhs, DoubleRegister rhs)
void emit_f32x4_pmax(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i64x2_sconvert_i32x4_low(LiftoffRegister dst, LiftoffRegister src)
void emit_i64x2_shl(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_f64x2_sub(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_f64x2_lt(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i32x4_gt_s(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i16x8_add_sat_u(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i8x16_sub(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_f32x4_le(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_f64x2_div(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void AtomicXor(Register dst_addr, Register offset_reg, uintptr_t offset_imm, LiftoffRegister value, LiftoffRegister result, StoreType type, bool i64_offset)
void emit_i64_shl(LiftoffRegister dst, LiftoffRegister src, Register amount)
void emit_i64_shli(LiftoffRegister dst, LiftoffRegister src, int32_t amount)
void emit_i8x16_add_sat_s(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i32x4_relaxed_trunc_f64x2_u_zero(LiftoffRegister dst, LiftoffRegister src)
void emit_i32x4_min_s(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i16x8_relaxed_q15mulr_s(LiftoffRegister dst, LiftoffRegister src1, LiftoffRegister src2)
void emit_i8x16_extract_lane_s(LiftoffRegister dst, LiftoffRegister lhs, uint8_t imm_lane_idx)
void emit_f32_min(DoubleRegister dst, DoubleRegister lhs, DoubleRegister rhs)
void emit_i32x4_ge_u(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
bool emit_f16x8_qfms(LiftoffRegister dst, LiftoffRegister src1, LiftoffRegister src2, LiftoffRegister src3)
void emit_f32x4_div(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
bool emit_f64_ceil(DoubleRegister dst, DoubleRegister src)
void emit_i16x8_extmul_high_i8x16_s(LiftoffRegister dst, LiftoffRegister src1, LiftoffRegister src2)
void emit_f64x2_pmax(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
bool emit_f16x8_trunc(LiftoffRegister dst, LiftoffRegister src)
bool emit_f16x8_extract_lane(LiftoffRegister dst, LiftoffRegister lhs, uint8_t imm_lane_idx)
void emit_i32_rems(Register dst, Register lhs, Register rhs, Label *trap_rem_by_zero)
void emit_i64x2_extmul_high_i32x4_u(LiftoffRegister dst, LiftoffRegister src1, LiftoffRegister src2)
void emit_i32_clz(Register dst, Register src)
void emit_i8x16_min_u(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i32_shri(Register dst, Register src, int32_t amount)
void emit_f64x2_max(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_f64_mul(DoubleRegister dst, DoubleRegister lhs, DoubleRegister rhs)
void emit_i16x8_sub(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i8x16_shli(LiftoffRegister dst, LiftoffRegister lhs, int32_t rhs)
void emit_i32_eqz(Register dst, Register src)
void emit_i32x4_extadd_pairwise_i16x8_s(LiftoffRegister dst, LiftoffRegister src)
void emit_i32x4_uconvert_f32x4(LiftoffRegister dst, LiftoffRegister src)
void emit_i16x8_uconvert_i8x16_low(LiftoffRegister dst, LiftoffRegister src)
bool emit_f32_floor(DoubleRegister dst, DoubleRegister src)
void emit_f32x4_neg(LiftoffRegister dst, LiftoffRegister src)
void emit_i16x8_splat(LiftoffRegister dst, LiftoffRegister src)
void emit_f32_max(DoubleRegister dst, DoubleRegister lhs, DoubleRegister rhs)
void emit_i8x16_uconvert_i16x8(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i16x8_shri_s(LiftoffRegister dst, LiftoffRegister lhs, int32_t rhs)
void FillI64Half(Register, int offset, RegPairHalf)
bool emit_f16x8_splat(LiftoffRegister dst, LiftoffRegister src)
void emit_f32x4_demote_f64x2_zero(LiftoffRegister dst, LiftoffRegister src)
void emit_i64_sari(LiftoffRegister dst, LiftoffRegister src, int32_t amount)
void emit_f64x2_splat(LiftoffRegister dst, LiftoffRegister src)
bool emit_i64_remu(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs, Label *trap_rem_by_zero)
void CallCWithStackBuffer(const std::initializer_list< VarState > args, const LiftoffRegister *rets, ValueKind return_kind, ValueKind out_argument_kind, int stack_bytes, ExternalReference ext_ref)
void emit_f32_mul(DoubleRegister dst, DoubleRegister lhs, DoubleRegister rhs)
void emit_s128_and(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_f64x2_neg(LiftoffRegister dst, LiftoffRegister src)
void emit_i64x2_abs(LiftoffRegister dst, LiftoffRegister src)
void emit_i8x16_sub_sat_s(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_f32x4_max(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i32x4_ge_s(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
bool emit_f32_nearest_int(DoubleRegister dst, DoubleRegister src)
void emit_f64x2_replace_lane(LiftoffRegister dst, LiftoffRegister src1, LiftoffRegister src2, uint8_t imm_lane_idx)
void emit_i32x4_dot_i16x8_s(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i16x8_extmul_low_i8x16_u(LiftoffRegister dst, LiftoffRegister src1, LiftoffRegister src2)
void emit_f64x2_le(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i16x8_shr_u(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i64x2_shr_u(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i16x8_uconvert_i8x16_high(LiftoffRegister dst, LiftoffRegister src)
void emit_i16x8_eq(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_f32x4_pmin(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i16x8_alltrue(LiftoffRegister dst, LiftoffRegister src)
void emit_i16x8_dot_i8x16_i7x16_s(LiftoffRegister dst, LiftoffRegister src1, LiftoffRegister src2)
void emit_i16x8_sconvert_i8x16_high(LiftoffRegister dst, LiftoffRegister src)
void emit_f64x2_qfma(LiftoffRegister dst, LiftoffRegister src1, LiftoffRegister src2, LiftoffRegister src3)
void emit_i32_subi(Register dst, Register lhs, int32_t imm)
void emit_i32_shli(Register dst, Register src, int32_t amount)
void emit_i32x4_extmul_low_i16x8_u(LiftoffRegister dst, LiftoffRegister src1, LiftoffRegister src2)
void AtomicAdd(Register dst_addr, Register offset_reg, uintptr_t offset_imm, LiftoffRegister value, LiftoffRegister result, StoreType type, bool i64_offset)
void AtomicSub(Register dst_addr, Register offset_reg, uintptr_t offset_imm, LiftoffRegister value, LiftoffRegister result, StoreType type, bool i64_offset)
void LoadTransform(LiftoffRegister dst, Register src_addr, Register offset_reg, uintptr_t offset_imm, LoadType type, LoadTransformationKind transform, uint32_t *protected_load_pc, bool i64_offset)
void emit_i32x4_uconvert_i16x8_low(LiftoffRegister dst, LiftoffRegister src)
void emit_s128_store_nonzero_if_nan(Register dst, LiftoffRegister src, Register tmp_gp, LiftoffRegister tmp_s128, ValueKind lane_kind)
void emit_i32x4_splat(LiftoffRegister dst, LiftoffRegister src)
void emit_i32_sar(Register dst, Register src, Register amount)
void emit_i16x8_shri_u(LiftoffRegister dst, LiftoffRegister lhs, int32_t rhs)
bool emit_i16x8_sconvert_f16x8(LiftoffRegister dst, LiftoffRegister src)
void AtomicAnd(Register dst_addr, Register offset_reg, uintptr_t offset_imm, LiftoffRegister value, LiftoffRegister result, StoreType type, bool i64_offset)
void emit_i16x8_sconvert_i32x4(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i8x16_add_sat_u(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i32x4_shl(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i16x8_add_sat_s(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void AtomicCompareExchange(Register dst_addr, Register offset_reg, uintptr_t offset_imm, LiftoffRegister expected, LiftoffRegister new_value, LiftoffRegister value, StoreType type, bool i64_offset)
void emit_i8x16_shl(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_s128_not(LiftoffRegister dst, LiftoffRegister src)
void emit_i64_signextend_i16(LiftoffRegister dst, LiftoffRegister src)
void emit_i32x4_gt_u(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i32x4_dot_i8x16_i7x16_add_s(LiftoffRegister dst, LiftoffRegister src1, LiftoffRegister src2, LiftoffRegister acc)
bool emit_f16x8_neg(LiftoffRegister dst, LiftoffRegister src)
void emit_f64x2_convert_low_i32x4_u(LiftoffRegister dst, LiftoffRegister src)
void emit_f32_copysign(DoubleRegister dst, DoubleRegister lhs, DoubleRegister rhs)
void emit_f32x4_add(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
bool emit_f16x8_pmax(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i32x4_eq(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i16x8_shli(LiftoffRegister dst, LiftoffRegister lhs, int32_t rhs)
void emit_f64_min(DoubleRegister dst, DoubleRegister lhs, DoubleRegister rhs)
void emit_i64x2_shr_s(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i8x16_sconvert_i16x8(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
bool emit_f64_trunc(DoubleRegister dst, DoubleRegister src)
void emit_f64_set_cond(Condition condition, Register dst, DoubleRegister lhs, DoubleRegister rhs)
void emit_f32x4_sub(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i16x8_add(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_f64x2_abs(LiftoffRegister dst, LiftoffRegister src)
void Fill(LiftoffRegister, int offset, ValueKind)
void emit_i32_shr(Register dst, Register src, Register amount)
void emit_i64_signextend_i32(LiftoffRegister dst, LiftoffRegister src)
void emit_i64x2_extract_lane(LiftoffRegister dst, LiftoffRegister lhs, uint8_t imm_lane_idx)
void emit_f32x4_replace_lane(LiftoffRegister dst, LiftoffRegister src1, LiftoffRegister src2, uint8_t imm_lane_idx)
void LoadFullPointer(Register dst, Register src_addr, int32_t offset_imm)
void emit_i8x16_max_s(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i64_add(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i64x2_extmul_low_i32x4_u(LiftoffRegister dst, LiftoffRegister src1, LiftoffRegister src2)
void emit_i16x8_max_s(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
bool emit_f16x8_sub(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i32x4_abs(LiftoffRegister dst, LiftoffRegister src)
void emit_i32_andi(Register dst, Register lhs, int32_t imm)
bool emit_f16x8_abs(LiftoffRegister dst, LiftoffRegister src)
void emit_i8x16_ge_s(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_f64x2_convert_low_i32x4_s(LiftoffRegister dst, LiftoffRegister src)
bool emit_f16x8_replace_lane(LiftoffRegister dst, LiftoffRegister src1, LiftoffRegister src2, uint8_t imm_lane_idx)
bool emit_f32x4_promote_low_f16x8(LiftoffRegister dst, LiftoffRegister src)
void Store(Register dst_addr, Register offset_reg, uintptr_t offset_imm, LiftoffRegister src, StoreType type, LiftoffRegList pinned, uint32_t *protected_store_pc=nullptr, bool is_store_mem=false, bool i64_offset=false)
bool emit_f16x8_mul(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void Load(LiftoffRegister dst, Register src_addr, Register offset_reg, uintptr_t offset_imm, LoadType type, uint32_t *protected_load_pc=nullptr, bool is_load_mem=false, bool i64_offset=false, bool needs_shift=false)
void emit_i64x2_shri_u(LiftoffRegister dst, LiftoffRegister lhs, int32_t rhs)
bool emit_f16x8_max(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i32x4_shr_s(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_f32x4_uconvert_i32x4(LiftoffRegister dst, LiftoffRegister src)
void emit_i64x2_extmul_high_i32x4_s(LiftoffRegister dst, LiftoffRegister src1, LiftoffRegister src2)
void emit_f32x4_relaxed_min(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i64x2_shri_s(LiftoffRegister dst, LiftoffRegister lhs, int32_t rhs)
void emit_f64_sub(DoubleRegister dst, DoubleRegister lhs, DoubleRegister rhs)
void emit_i32_signextend_i16(Register dst, Register src)
void emit_i8x16_gt_s(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i32_divs(Register dst, Register lhs, Register rhs, Label *trap_div_by_zero, Label *trap_div_unrepresentable)
void emit_f32_neg(DoubleRegister dst, DoubleRegister src)
void emit_f64x2_promote_low_f32x4(LiftoffRegister dst, LiftoffRegister src)
void emit_i64_addi(LiftoffRegister dst, LiftoffRegister lhs, int64_t imm)
void LoadLane(LiftoffRegister dst, LiftoffRegister src, Register addr, Register offset_reg, uintptr_t offset_imm, LoadType type, uint8_t lane, uint32_t *protected_load_pc, bool i64_offset)
void emit_i16x8_sub_sat_u(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i32x4_bitmask(LiftoffRegister dst, LiftoffRegister src)
void emit_i32x4_alltrue(LiftoffRegister dst, LiftoffRegister src)
void emit_i32_and(Register dst, Register lhs, Register rhs)
void emit_i16x8_extract_lane_s(LiftoffRegister dst, LiftoffRegister lhs, uint8_t imm_lane_idx)
void emit_f64_copysign(DoubleRegister dst, DoubleRegister lhs, DoubleRegister rhs)
bool emit_f64x2_trunc(LiftoffRegister dst, LiftoffRegister src)
void emit_i8x16_rounding_average_u(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i8x16_ne(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i8x16_relaxed_swizzle(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i16x8_abs(LiftoffRegister dst, LiftoffRegister src)
bool emit_f16x8_add(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void CallFrameSetupStub(int declared_function_index)
void emit_i64x2_uconvert_i32x4_high(LiftoffRegister dst, LiftoffRegister src)
void emit_f32_abs(DoubleRegister dst, DoubleRegister src)
void emit_i32_remu(Register dst, Register lhs, Register rhs, Label *trap_rem_by_zero)
bool emit_f64x2_ceil(LiftoffRegister dst, LiftoffRegister src)
bool emit_f16x8_eq(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void LoadSpillAddress(Register dst, int offset, ValueKind kind)
void emit_i16x8_extmul_high_i8x16_u(LiftoffRegister dst, LiftoffRegister src1, LiftoffRegister src2)
void emit_f32x4_qfms(LiftoffRegister dst, LiftoffRegister src1, LiftoffRegister src2, LiftoffRegister src3)
void emit_f64_neg(DoubleRegister dst, DoubleRegister src)
void emit_i64x2_replace_lane(LiftoffRegister dst, LiftoffRegister src1, LiftoffRegister src2, uint8_t imm_lane_idx)
void emit_i32_ctz(Register dst, Register src)
void StoreCallerFrameSlot(LiftoffRegister, uint32_t caller_slot_idx, ValueKind, Register frame_pointer)
void emit_i32x4_shli(LiftoffRegister dst, LiftoffRegister lhs, int32_t rhs)
void AtomicExchange(Register dst_addr, Register offset_reg, uintptr_t offset_imm, LiftoffRegister value, LiftoffRegister result, StoreType type, bool i64_offset)
void emit_i32x4_min_u(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i64x2_mul(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i32_xor(Register dst, Register lhs, Register rhs)
void emit_s128_select(LiftoffRegister dst, LiftoffRegister src1, LiftoffRegister src2, LiftoffRegister mask)
bool emit_i64_rems(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs, Label *trap_rem_by_zero)
void emit_i8x16_replace_lane(LiftoffRegister dst, LiftoffRegister src1, LiftoffRegister src2, uint8_t imm_lane_idx)
bool emit_f16x8_ne(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i64x2_sconvert_i32x4_high(LiftoffRegister dst, LiftoffRegister src)
void CallC(const std::initializer_list< VarState > args, ExternalReference ext_ref)
void emit_i32x4_trunc_sat_f64x2_s_zero(LiftoffRegister dst, LiftoffRegister src)
void emit_i32_or(Register dst, Register lhs, Register rhs)
bool emit_f16x8_floor(LiftoffRegister dst, LiftoffRegister src)
void emit_i64_signextend_i8(LiftoffRegister dst, LiftoffRegister src)
void emit_i16x8_extadd_pairwise_i8x16_s(LiftoffRegister dst, LiftoffRegister src)
void emit_i64x2_bitmask(LiftoffRegister dst, LiftoffRegister src)
void emit_i64x2_extmul_low_i32x4_s(LiftoffRegister dst, LiftoffRegister src1, LiftoffRegister src2)
bool emit_i64_divu(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs, Label *trap_div_by_zero)
bool emit_f16x8_demote_f32x4_zero(LiftoffRegister dst, LiftoffRegister src)
void emit_f32x4_qfma(LiftoffRegister dst, LiftoffRegister src1, LiftoffRegister src2, LiftoffRegister src3)
void emit_f32x4_ne(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i8x16_gt_u(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_f64x2_min(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i32x4_shri_s(LiftoffRegister dst, LiftoffRegister lhs, int32_t rhs)
bool emit_f64x2_nearest_int(LiftoffRegister dst, LiftoffRegister src)
void DropStackSlotsAndRet(uint32_t num_stack_slots)
void emit_f32x4_abs(LiftoffRegister dst, LiftoffRegister src)
void emit_s128_and_not(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_s128_relaxed_laneselect(LiftoffRegister dst, LiftoffRegister src1, LiftoffRegister src2, LiftoffRegister mask, int lane_width)
void emit_i32x4_shri_u(LiftoffRegister dst, LiftoffRegister lhs, int32_t rhs)
void emit_f64x2_extract_lane(LiftoffRegister dst, LiftoffRegister lhs, uint8_t imm_lane_idx)
void emit_i16x8_replace_lane(LiftoffRegister dst, LiftoffRegister src1, LiftoffRegister src2, uint8_t imm_lane_idx)
void emit_i32x4_sconvert_f32x4(LiftoffRegister dst, LiftoffRegister src)
void emit_i8x16_shri_u(LiftoffRegister dst, LiftoffRegister lhs, int32_t rhs)
void emit_i32_sari(Register dst, Register src, int32_t amount)
void LoadConstant(LiftoffRegister, WasmValue)
void emit_i32x4_uconvert_i16x8_high(LiftoffRegister dst, LiftoffRegister src)
void emit_i16x8_sub_sat_s(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i16x8_max_u(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
bool emit_f32x4_trunc(LiftoffRegister dst, LiftoffRegister src)
void emit_i32x4_max_u(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i32x4_ne(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void PrepareTailCall(int num_callee_stack_params, int stack_param_delta)
void emit_f32x4_relaxed_max(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
bool emit_f64_nearest_int(DoubleRegister dst, DoubleRegister src)
bool emit_f16x8_ceil(LiftoffRegister dst, LiftoffRegister src)
void emit_i32_divu(Register dst, Register lhs, Register rhs, Label *trap_div_by_zero)
void emit_cond_jump(Condition, Label *, ValueKind value, Register lhs, Register rhs, const FreezeCacheState &frozen)
void LoadFromInstance(Register dst, Register instance, int offset, int size)
void emit_i8x16_min_s(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i16x8_ne(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_smi_check(Register obj, Label *target, SmiCheckMode mode, const FreezeCacheState &frozen)
void emit_f64_max(DoubleRegister dst, DoubleRegister lhs, DoubleRegister rhs)
void emit_f32x4_extract_lane(LiftoffRegister dst, LiftoffRegister lhs, uint8_t imm_lane_idx)
void LoadProtectedPointer(Register dst, Register src_addr, int32_t offset)
void emit_i32x4_extmul_low_i16x8_s(LiftoffRegister dst, LiftoffRegister src1, LiftoffRegister src2)
void emit_f64_add(DoubleRegister dst, DoubleRegister lhs, DoubleRegister rhs)
void emit_i64_sar(LiftoffRegister dst, LiftoffRegister src, Register amount)
void emit_i16x8_shr_s(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i32_ori(Register dst, Register lhs, int32_t imm)
void emit_f64x2_qfms(LiftoffRegister dst, LiftoffRegister src1, LiftoffRegister src2, LiftoffRegister src3)
bool emit_f32x4_ceil(LiftoffRegister dst, LiftoffRegister src)
bool emit_i64_popcnt(LiftoffRegister dst, LiftoffRegister src)
void emit_i32x4_trunc_sat_f64x2_u_zero(LiftoffRegister dst, LiftoffRegister src)
void emit_i32_mul(Register dst, Register lhs, Register rhs)
void emit_i8x16_extract_lane_u(LiftoffRegister dst, LiftoffRegister lhs, uint8_t imm_lane_idx)
bool emit_f64_floor(DoubleRegister dst, DoubleRegister src)
void emit_f32_set_cond(Condition condition, Register dst, DoubleRegister lhs, DoubleRegister rhs)
bool emit_f16x8_uconvert_i16x8(LiftoffRegister dst, LiftoffRegister src)
void emit_i16x8_min_s(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i32x4_mul(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
bool emit_f16x8_demote_f64x2_zero(LiftoffRegister dst, LiftoffRegister src)
bool emit_f16x8_div(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i16x8_gt_u(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
bool emit_f32x4_nearest_int(LiftoffRegister dst, LiftoffRegister src)
void emit_i64_shri(LiftoffRegister dst, LiftoffRegister src, int32_t amount)
void emit_i32x4_sconvert_i16x8_high(LiftoffRegister dst, LiftoffRegister src)
void emit_i8x16_shri_s(LiftoffRegister dst, LiftoffRegister lhs, int32_t rhs)
void emit_i64_set_cond(Condition condition, Register dst, LiftoffRegister lhs, LiftoffRegister rhs)
bool emit_f64x2_floor(LiftoffRegister dst, LiftoffRegister src)
void emit_i8x16_sub_sat_u(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i16x8_neg(LiftoffRegister dst, LiftoffRegister src)
void emit_i8x16_add(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i32x4_sconvert_i16x8_low(LiftoffRegister dst, LiftoffRegister src)
void emit_f64x2_ne(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
bool emit_f16x8_le(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
bool emit_i32_popcnt(Register dst, Register src)
void ParallelRegisterMove(base::Vector< const ParallelRegisterMoveTuple >)
void emit_i16x8_rounding_average_u(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i32x4_replace_lane(LiftoffRegister dst, LiftoffRegister src1, LiftoffRegister src2, uint8_t imm_lane_idx)
void MoveStackValue(uint32_t dst_offset, uint32_t src_offset, ValueKind)
void emit_i64x2_gt_s(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i16x8_min_u(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i64x2_ge_s(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void Move(LiftoffRegister dst, LiftoffRegister src, ValueKind)
void emit_i8x16_ge_u(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i64x2_sub(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i16x8_uconvert_i32x4(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
bool emit_f16x8_lt(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i8x16_popcnt(LiftoffRegister dst, LiftoffRegister src)
void AtomicStore(Register dst_addr, Register offset_reg, uintptr_t offset_imm, LiftoffRegister src, StoreType type, LiftoffRegList pinned, bool i64_offset)
void emit_f64_abs(DoubleRegister dst, DoubleRegister src)
void emit_i64_sub(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
bool emit_f16x8_pmin(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void PatchPrepareStackFrame(int offset, SafepointTableBuilder *, bool feedback_vector_slot, size_t stack_param_slots)
void emit_f32x4_sconvert_i32x4(LiftoffRegister dst, LiftoffRegister src)
void LoadCallerFrameSlot(LiftoffRegister, uint32_t caller_slot_idx, ValueKind)
void emit_i64x2_ne(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_f32_sqrt(DoubleRegister dst, DoubleRegister src)
void emit_i64x2_alltrue(LiftoffRegister dst, LiftoffRegister src)
void emit_f32_sub(DoubleRegister dst, DoubleRegister lhs, DoubleRegister rhs)
void emit_i16x8_ge_s(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_f64x2_add(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i32_xori(Register dst, Register lhs, int32_t imm)
void emit_i64_ctz(LiftoffRegister dst, LiftoffRegister src)
void emit_i32_set_cond(Condition, Register dst, Register lhs, Register rhs)
void emit_f32x4_min(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
bool emit_i64_divs(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs, Label *trap_div_by_zero, Label *trap_div_unrepresentable)
void emit_i16x8_sconvert_i8x16_low(LiftoffRegister dst, LiftoffRegister src)
void emit_i32_sub(Register dst, Register lhs, Register rhs)
void TailCallIndirect(compiler::CallDescriptor *call_descriptor, Register target)
void emit_i16x8_q15mulr_sat_s(LiftoffRegister dst, LiftoffRegister src1, LiftoffRegister src2)
void emit_i64x2_neg(LiftoffRegister dst, LiftoffRegister src)
void emit_i64x2_shli(LiftoffRegister dst, LiftoffRegister lhs, int32_t rhs)
void emit_i64_shr(LiftoffRegister dst, LiftoffRegister src, Register amount)
void emit_i16x8_bitmask(LiftoffRegister dst, LiftoffRegister src)
void emit_i32x4_sub(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i32x4_max_s(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_f32_div(DoubleRegister dst, DoubleRegister lhs, DoubleRegister rhs)
bool emit_f16x8_min(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i32x4_relaxed_trunc_f32x4_s(LiftoffRegister dst, LiftoffRegister src)
void emit_i64x2_splat(LiftoffRegister dst, LiftoffRegister src)
void emit_f32x4_sqrt(LiftoffRegister dst, LiftoffRegister src)
void emit_i16x8_mul(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i8x16_shr_u(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i32x4_add(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i16x8_extmul_low_i8x16_s(LiftoffRegister dst, LiftoffRegister src1, LiftoffRegister src2)
void emit_f64x2_mul(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void AllocateStackSlot(Register addr, uint32_t size)
void emit_i32x4_extract_lane(LiftoffRegister dst, LiftoffRegister lhs, uint8_t imm_lane_idx)
void emit_s128_or(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void LoadTaggedPointer(Register dst, Register src_addr, Register offset_reg, int32_t offset_imm, uint32_t *protected_load_pc=nullptr, bool offset_reg_needs_shift=false)
void emit_f64x2_eq(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i8x16_alltrue(LiftoffRegister dst, LiftoffRegister src)
void emit_i32x4_shr_u(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i8x16_abs(LiftoffRegister dst, LiftoffRegister src)
void emit_f32x4_splat(LiftoffRegister dst, LiftoffRegister src)
void emit_i8x16_shr_s(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i32_cond_jumpi(Condition, Label *, Register lhs, int imm, const FreezeCacheState &frozen)
void emit_i64_eqz(Register dst, LiftoffRegister src)
void StoreTaggedPointer(Register dst_addr, Register offset_reg, int32_t offset_imm, Register src, LiftoffRegList pinned, uint32_t *protected_store_pc=nullptr, SkipWriteBarrier=kNoSkipWriteBarrier)
void emit_i64_clz(LiftoffRegister dst, LiftoffRegister src)
void bailout(LiftoffBailoutReason reason, const char *detail)
void emit_f32x4_mul(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void IncrementSmi(LiftoffRegister dst, int offset)
LiftoffRegister GetUnusedRegister(RegClass rc, std::initializer_list< LiftoffRegister > try_first, LiftoffRegList pinned)
void emit_i16x8_gt_s(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i16x8_shl(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i8x16_splat(LiftoffRegister dst, LiftoffRegister src)
bool emit_f16x8_sconvert_i16x8(LiftoffRegister dst, LiftoffRegister src)
void emit_f64x2_sqrt(LiftoffRegister dst, LiftoffRegister src)
void emit_i8x16_shuffle(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs, const uint8_t shuffle[16], bool is_swizzle)
void emit_i32x4_extadd_pairwise_i16x8_u(LiftoffRegister dst, LiftoffRegister src)
bool emit_f32_ceil(DoubleRegister dst, DoubleRegister src)
void emit_i32_addi(Register dst, Register lhs, int32_t imm)
void emit_i16x8_extadd_pairwise_i8x16_u(LiftoffRegister dst, LiftoffRegister src)
void emit_i32x4_extmul_high_i16x8_s(LiftoffRegister dst, LiftoffRegister src1, LiftoffRegister src2)
void emit_i64x2_add(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i64_mul(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i8x16_max_u(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void AtomicOr(Register dst_addr, Register offset_reg, uintptr_t offset_imm, LiftoffRegister value, LiftoffRegister result, StoreType type, bool i64_offset)
bool emit_select(LiftoffRegister dst, Register condition, LiftoffRegister true_value, LiftoffRegister false_value, ValueKind kind)
bool emit_i16x8_uconvert_f16x8(LiftoffRegister dst, LiftoffRegister src)
bool emit_type_conversion(WasmOpcode opcode, LiftoffRegister dst, LiftoffRegister src, Label *trap=nullptr)
void emit_i8x16_neg(LiftoffRegister dst, LiftoffRegister src)
void AtomicLoad(LiftoffRegister dst, Register src_addr, Register offset_reg, uintptr_t offset_imm, LoadType type, LiftoffRegList pinned, bool i64_offset)
void emit_i64x2_eq(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void LoadReturnStackSlot(LiftoffRegister, int offset, ValueKind)
void emit_i32x4_neg(LiftoffRegister dst, LiftoffRegister src)
void emit_s128_xor(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i16x8_ge_u(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i32x4_relaxed_trunc_f32x4_u(LiftoffRegister dst, LiftoffRegister src)
bool emit_f32_trunc(DoubleRegister dst, DoubleRegister src)
void LoadTaggedPointerFromInstance(Register dst, Register instance, int offset)
void emit_v128_anytrue(LiftoffRegister dst, LiftoffRegister src)
void emit_i32_shl(Register dst, Register src, Register amount)
void emit_i32x4_extmul_high_i16x8_u(LiftoffRegister dst, LiftoffRegister src1, LiftoffRegister src2)
void emit_f32x4_lt(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_s128_const(LiftoffRegister dst, const uint8_t imms[16])
void emit_f64_sqrt(DoubleRegister dst, DoubleRegister src)
void StoreLane(Register dst, Register offset, uintptr_t offset_imm, LiftoffRegister src, StoreType type, uint8_t lane, uint32_t *protected_store_pc, bool i64_offset)
void CheckTierUp(int declared_func_index, int budget_used, Label *ool_label, const FreezeCacheState &frozen)
void emit_i16x8_extract_lane_u(LiftoffRegister dst, LiftoffRegister lhs, uint8_t imm_lane_idx)
void emit_f64x2_relaxed_min(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void CallIndirect(const ValueKindSig *sig, compiler::CallDescriptor *call_descriptor, Register target)
void RecordSpillsInSafepoint(SafepointTableBuilder::Safepoint &safepoint, LiftoffRegList all_spills, LiftoffRegList ref_spills, int spill_offset)
void emit_f64x2_relaxed_max(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
bool emit_f16x8_qfma(LiftoffRegister dst, LiftoffRegister src1, LiftoffRegister src2, LiftoffRegister src3)
void emit_i8x16_eq(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
V8_NOINLINE V8_PRESERVE_MOST void SpillRegister(LiftoffRegister)
void emit_f64x2_pmin(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_f32_add(DoubleRegister dst, DoubleRegister lhs, DoubleRegister rhs)
bool emit_f16x8_sqrt(LiftoffRegister dst, LiftoffRegister src)
void LoadTrustedPointer(Register dst, Register src_addr, int offset, IndirectPointerTag tag)
void emit_i32_add(Register dst, Register lhs, Register rhs)
void emit_i32x4_relaxed_trunc_f64x2_s_zero(LiftoffRegister dst, LiftoffRegister src)
void emit_i32_muli(Register dst, Register lhs, int32_t imm)
void emit_f32x4_eq(LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void emit_i32_signextend_i8(Register dst, Register src)
void emit_i8x16_bitmask(LiftoffRegister dst, LiftoffRegister src)
constexpr Register set(Register reg)
LiftoffRegister GetLastRegSet() const
constexpr LiftoffRegister clear(LiftoffRegister reg)
bool has(LiftoffRegister reg) const
constexpr unsigned GetNumRegsSet() const
LiftoffRegister GetFirstRegSet() const
constexpr DoubleRegister fp() const
bool overlaps(const LiftoffRegister other) const
static LiftoffRegister ForPair(Register low, Register high)
base::SmallVector< Slot, 8 > slots_
static int SlotSizeInBytes(const Slot &slot)
static constexpr int ToTagged(int offset)
CacheStatePreservingTempRegisters(LiftoffAssembler *assm, LiftoffRegList pinned={})
#define COMPRESS_POINTERS_BOOL
Definition globals.h:99
#define V8_ENABLE_SANDBOX_BOOL
Definition globals.h:160
int start
base::Vector< const DirectHandle< Object > > args
Definition execution.cc:74
Label label
int32_t offset
ZoneVector< RpoNumber > & result
LiftoffRegister reg
MovableLabel continuation
Register tmp
int pc_offset
LiftoffRegList regs_to_save
std::optional< OolTrapLabel > trap
uint32_t const mask
constexpr bool IsPowerOfTwo(T value)
Definition bits.h:187
constexpr int WhichPowerOfTwo(T value)
Definition bits.h:195
FloatWithBits< 32 > Float32
Definition index.h:233
void EmitSimdShiftImmediate(LiftoffAssembler *assm, LiftoffRegister dst, LiftoffRegister lhs, int32_t rhs)
constexpr int MaskFromNeonDataType(NeonDataType dt)
void Store(LiftoffAssembler *assm, LiftoffRegister src, MemOperand dst, ValueKind kind)
Condition MakeUnsigned(Condition cond)
void Or(LiftoffAssembler *lasm, Register dst, Register lhs, Register rhs)
void I64Shiftop(LiftoffAssembler *assm, LiftoffRegister dst, LiftoffRegister src, Register amount)
Register CalculateActualAddress(LiftoffAssembler *assm, UseScratchRegisterScope *temps, Register addr_reg, Register offset_reg, uintptr_t offset_imm, Register result_reg=no_reg)
void EmitFloatMinOrMax(LiftoffAssembler *assm, RegisterType dst, RegisterType lhs, RegisterType rhs, MinOrMax min_or_max)
MemOperand GetHalfStackSlot(int offset, RegPairHalf half)
void Xor(LiftoffAssembler *lasm, Register dst, Register lhs, Register rhs)
void EmitAnyTrue(LiftoffAssembler *assm, LiftoffRegister dst, LiftoffRegister src)
void AtomicOp32(LiftoffAssembler *lasm, Register dst_addr, Register offset_reg, uint32_t offset_imm, LiftoffRegister value, LiftoffRegister result, LiftoffRegList pinned, void(Assembler::*load)(Register, Register, Condition), void(Assembler::*store)(Register, Register, Register, Condition), void(*op)(LiftoffAssembler *, Register, Register, Register))
void Add(LiftoffAssembler *lasm, Register dst, Register lhs, Register rhs)
void I64BinopI(LiftoffAssembler *assm, LiftoffRegister dst, LiftoffRegister lhs, int64_t imm)
void F64x2Compare(LiftoffAssembler *assm, LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs, Condition cond)
Register EnsureNoAlias(Assembler *assm, Register reg, Register must_not_alias, UseScratchRegisterScope *temps)
void AtomicOp64(LiftoffAssembler *lasm, Register dst_addr, Register offset_reg, uint32_t offset_imm, LiftoffRegister value, std::optional< LiftoffRegister > result, void(*op)(LiftoffAssembler *, LiftoffRegister, LiftoffRegister, LiftoffRegister))
void EmitSimdShift(LiftoffAssembler *assm, LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void Load(LiftoffAssembler *assm, LiftoffRegister dst, MemOperand src, ValueKind kind)
Simd128Register GetSimd128Register(DoubleRegister reg)
FloatRegister GetFloatRegister(DoubleRegister reg)
void LoadInternal(LiftoffAssembler *lasm, LiftoffRegister dst, Register src_addr, Register offset_reg, int32_t offset_imm, LoadType type, uint32_t *protected_load_pc=nullptr, bool needs_shift=false)
void And(LiftoffAssembler *lasm, Register dst, Register lhs, Register rhs)
void Exchange(LiftoffAssembler *lasm, Register dst, Register lhs, Register rhs)
MemOperand GetMemOp(LiftoffAssembler *assm, UseScratchRegisterScope *temps, Register addr, Register offset, int32_t offset_imm, unsigned shift_amount=0)
void I64Binop(LiftoffAssembler *assm, LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void AtomicBinop32(LiftoffAssembler *lasm, Register dst_addr, Register offset_reg, uint32_t offset_imm, LiftoffRegister value, LiftoffRegister result, StoreType type, void(*op)(LiftoffAssembler *, Register, Register, Register))
void Sub(LiftoffAssembler *lasm, Register dst, Register lhs, Register rhs)
void I64Store(LiftoffAssembler *lasm, LiftoffRegister dst, LiftoffRegister, LiftoffRegister src)
void S128NarrowOp(LiftoffAssembler *assm, NeonDataType dt, NeonDataType sdt, LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs)
void AtomicI64CompareExchange(LiftoffAssembler *lasm, Register dst_addr_reg, Register offset_reg, uint32_t offset_imm, LiftoffRegister expected, LiftoffRegister new_value, LiftoffRegister result)
void GeneratePopCnt(Assembler *assm, Register dst, Register src, Register scratch1, Register scratch2)
constexpr DoubleRegister kFpReturnRegisters[]
constexpr Register kGpParamRegisters[]
constexpr DoubleRegister kFpParamRegisters[]
static constexpr bool needs_gp_reg_pair(ValueKind kind)
constexpr DoubleRegList kLiftoffAssemblerFpCacheRegs
uint32_t WasmInterpreterRuntime int64_t r0
constexpr RegList kLiftoffAssemblerGpCacheRegs
constexpr Register kGpReturnRegisters[]
int declared_function_index(const WasmModule *module, int func_index)
typedef void(VECTORCALL PWasmOp)(const uint8_t *code
constexpr int value_kind_size(ValueKind kind)
static constexpr LiftoffRegList kGpCacheRegList
static constexpr LiftoffRegList kFpCacheRegList
constexpr bool is_reference(ValueKind kind)
constexpr IndependentValueType kWasmI64
constexpr Register no_reg
constexpr int kMinInt
Definition globals.h:375
constexpr int kTaggedSize
Definition globals.h:542
constexpr int kSimd128Size
Definition globals.h:706
constexpr NeonDataType NeonS16
constexpr NeonSize Neon32
constexpr BlockAddrMode ia_w
constexpr NeonSize Neon8
constexpr ShiftOp LSR
constexpr BlockAddrMode db_w
constexpr NeonSize Neon64
constexpr NeonDataType NeonS8
constexpr ShiftOp ASR
kWasmInternalFunctionIndirectPointerTag instance_data
constexpr ShiftOp LSL
too high values may cause the compiler to set high thresholds for inlining to as much as possible avoid inlined allocation of objects that cannot escape trace load stores from virtual maglev objects use TurboFan fast string builder analyze liveness of environment slots and zap dead values trace TurboFan load elimination emit data about basic block usage in builtins to this enable builtin reordering when run mksnapshot flag for emit warnings when applying builtin profile data verify register allocation in TurboFan randomly schedule instructions to stress dependency tracking enable store store elimination in TurboFan rewrite far to near simulate GC compiler thread race related to allow float parameters to be passed in simulator mode JS Wasm Run additional turbo_optimize_inlined_js_wasm_wrappers enable experimental feedback collection in generic lowering enable Turboshaft s WasmLoadElimination enable Turboshaft s low level load elimination for JS enable Turboshaft s escape analysis for string concatenation use enable Turbolev features that we want to ship in the not too far future trace individual Turboshaft reduction steps trace intermediate Turboshaft reduction steps invocation count threshold for early optimization Enables optimizations which favor memory size over execution speed Enables sampling allocation profiler with X as a sample interval min size of a semi the new space consists of two semi spaces max size of the Collect garbage after Collect garbage after keeps maps alive for< n > old space garbage collections print one detailed trace line in allocation gc speed threshold for starting incremental marking via a task in percent of available threshold for starting incremental marking immediately in percent of available Use a single schedule for determining a marking schedule between JS and C objects schedules the minor GC task with kUserVisible priority max worker number of concurrent for NumberOfWorkerThreads start background threads that allocate memory concurrent_array_buffer_sweeping use parallel threads to clear weak refs in the atomic pause trace progress of the incremental marking trace object counts and memory usage report a tick only when allocated zone memory changes by this amount TracingFlags::gc_stats store(v8::tracing::TracingCategoryObserver::ENABLED_BY_NATIVE)) DEFINE_GENERIC_IMPLICATION(trace_gc_object_stats
constexpr SBit LeaveCC
kWasmInternalFunctionIndirectPointerTag kProtectedInstanceDataOffset sig
constexpr NeonSize Neon16
constexpr int kSystemPointerSize
Definition globals.h:410
constexpr int kRegSizeInBitsLog2
constexpr NeonDataType NeonU8
constexpr Register kReturnRegister0
constexpr BarrierOption ISH
Condition NegateCondition(Condition cond)
constexpr int kInt32Size
Definition globals.h:401
V8_EXPORT_PRIVATE bool AreAliased(const CPURegister &reg1, const CPURegister &reg2, const CPURegister &reg3=NoReg, const CPURegister &reg4=NoReg, const CPURegister &reg5=NoReg, const CPURegister &reg6=NoReg, const CPURegister &reg7=NoReg, const CPURegister &reg8=NoReg)
constexpr NeonDataType NeonU16
constexpr NeonDataType NeonS32
constexpr LowDwVfpRegister kDoubleRegZero
V8_EXPORT_PRIVATE FlagValues v8_flags
constexpr NeonDataType NeonU32
constexpr AddrMode PostIndex
const intptr_t kSmiTagMask
Definition v8-internal.h:88
constexpr SBit SetCC
return value
Definition map-inl.h:893
constexpr NeonDataType NeonU64
constexpr uint8_t kInstrSize
constexpr NeonDataType NeonS64
constexpr Register kCArgRegs[]
constexpr int kDoubleSize
Definition globals.h:407
#define DCHECK_LE(v1, v2)
Definition logging.h:490
#define DCHECK_IMPLIES(v1, v2)
Definition logging.h:493
#define DCHECK_NE(v1, v2)
Definition logging.h:486
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_LT(v1, v2)
Definition logging.h:489
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
constexpr bool IsAligned(T value, U alignment)
Definition macros.h:403
#define arraysize(array)
Definition macros.h:67
#define V8_LIKELY(condition)
Definition v8config.h:661