1 // Copyright 2012 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <limits.h> // For LONG_MIN, LONG_MAX.
6
7 #if V8_TARGET_ARCH_ARM
8
9 #include "src/base/bits.h"
10 #include "src/base/division-by-constant.h"
11 #include "src/base/numbers/double.h"
12 #include "src/base/utils/random-number-generator.h"
13 #include "src/codegen/assembler-inl.h"
14 #include "src/codegen/callable.h"
15 #include "src/codegen/code-factory.h"
16 #include "src/codegen/external-reference-table.h"
17 #include "src/codegen/interface-descriptors-inl.h"
18 #include "src/codegen/macro-assembler.h"
19 #include "src/codegen/register-configuration.h"
20 #include "src/debug/debug.h"
21 #include "src/deoptimizer/deoptimizer.h"
22 #include "src/execution/frames-inl.h"
23 #include "src/heap/memory-chunk.h"
24 #include "src/init/bootstrapper.h"
25 #include "src/logging/counters.h"
26 #include "src/objects/objects-inl.h"
27 #include "src/runtime/runtime.h"
28 #include "src/snapshot/snapshot.h"
29
30 #if V8_ENABLE_WEBASSEMBLY
31 #include "src/wasm/wasm-code-manager.h"
32 #endif // V8_ENABLE_WEBASSEMBLY
33
34 // Satisfy cpplint check, but don't include platform-specific header. It is
35 // included recursively via macro-assembler.h.
36 #if 0
37 #include "src/codegen/arm/macro-assembler-arm.h"
38 #endif
39
40 namespace v8 {
41 namespace internal {
42
RequiredStackSizeForCallerSaved(SaveFPRegsMode fp_mode, Register exclusion1, Register exclusion2, Register exclusion3) const43 int TurboAssembler::RequiredStackSizeForCallerSaved(SaveFPRegsMode fp_mode,
44 Register exclusion1,
45 Register exclusion2,
46 Register exclusion3) const {
47 int bytes = 0;
48 RegList exclusions = {exclusion1, exclusion2, exclusion3};
49 RegList list = (kCallerSaved | lr) - exclusions;
50
51 bytes += list.Count() * kPointerSize;
52
53 if (fp_mode == SaveFPRegsMode::kSave) {
54 bytes += DwVfpRegister::kNumRegisters * DwVfpRegister::kSizeInBytes;
55 }
56
57 return bytes;
58 }
59
PushCallerSaved(SaveFPRegsMode fp_mode, Register exclusion1, Register exclusion2, Register exclusion3)60 int TurboAssembler::PushCallerSaved(SaveFPRegsMode fp_mode, Register exclusion1,
61 Register exclusion2, Register exclusion3) {
62 ASM_CODE_COMMENT(this);
63 int bytes = 0;
64 RegList exclusions = {exclusion1, exclusion2, exclusion3};
65 RegList list = (kCallerSaved | lr) - exclusions;
66 stm(db_w, sp, list);
67
68 bytes += list.Count() * kPointerSize;
69
70 if (fp_mode == SaveFPRegsMode::kSave) {
71 SaveFPRegs(sp, lr);
72 bytes += DwVfpRegister::kNumRegisters * DwVfpRegister::kSizeInBytes;
73 }
74
75 return bytes;
76 }
77
PopCallerSaved(SaveFPRegsMode fp_mode, Register exclusion1, Register exclusion2, Register exclusion3)78 int TurboAssembler::PopCallerSaved(SaveFPRegsMode fp_mode, Register exclusion1,
79 Register exclusion2, Register exclusion3) {
80 ASM_CODE_COMMENT(this);
81 int bytes = 0;
82 if (fp_mode == SaveFPRegsMode::kSave) {
83 RestoreFPRegs(sp, lr);
84 bytes += DwVfpRegister::kNumRegisters * DwVfpRegister::kSizeInBytes;
85 }
86
87 RegList exclusions = {exclusion1, exclusion2, exclusion3};
88 RegList list = (kCallerSaved | lr) - exclusions;
89 ldm(ia_w, sp, list);
90
91 bytes += list.Count() * kPointerSize;
92
93 return bytes;
94 }
95
LoadFromConstantsTable(Register destination, int constant_index)96 void TurboAssembler::LoadFromConstantsTable(Register destination,
97 int constant_index) {
98 DCHECK(RootsTable::IsImmortalImmovable(RootIndex::kBuiltinsConstantsTable));
99
100 const uint32_t offset =
101 FixedArray::kHeaderSize + constant_index * kPointerSize - kHeapObjectTag;
102
103 LoadRoot(destination, RootIndex::kBuiltinsConstantsTable);
104 ldr(destination, MemOperand(destination, offset));
105 }
106
LoadRootRelative(Register destination, int32_t offset)107 void TurboAssembler::LoadRootRelative(Register destination, int32_t offset) {
108 ldr(destination, MemOperand(kRootRegister, offset));
109 }
110
LoadRootRegisterOffset(Register destination, intptr_t offset)111 void TurboAssembler::LoadRootRegisterOffset(Register destination,
112 intptr_t offset) {
113 if (offset == 0) {
114 Move(destination, kRootRegister);
115 } else {
116 add(destination, kRootRegister, Operand(offset));
117 }
118 }
119
Jump(Register target, Condition cond)120 void TurboAssembler::Jump(Register target, Condition cond) { bx(target, cond); }
121
Jump(intptr_t target, RelocInfo::Mode rmode, Condition cond)122 void TurboAssembler::Jump(intptr_t target, RelocInfo::Mode rmode,
123 Condition cond) {
124 mov(pc, Operand(target, rmode), LeaveCC, cond);
125 }
126
Jump(Address target, RelocInfo::Mode rmode, Condition cond)127 void TurboAssembler::Jump(Address target, RelocInfo::Mode rmode,
128 Condition cond) {
129 DCHECK(!RelocInfo::IsCodeTarget(rmode));
130 Jump(static_cast<intptr_t>(target), rmode, cond);
131 }
132
Jump(Handle<Code> code, RelocInfo::Mode rmode, Condition cond)133 void TurboAssembler::Jump(Handle<Code> code, RelocInfo::Mode rmode,
134 Condition cond) {
135 DCHECK(RelocInfo::IsCodeTarget(rmode));
136 DCHECK_IMPLIES(options().isolate_independent_code,
137 Builtins::IsIsolateIndependentBuiltin(*code));
138 DCHECK_IMPLIES(options().use_pc_relative_calls_and_jumps,
139 Builtins::IsIsolateIndependentBuiltin(*code));
140
141 Builtin builtin = Builtin::kNoBuiltinId;
142 bool target_is_builtin =
143 isolate()->builtins()->IsBuiltinHandle(code, &builtin);
144
145 if (options().use_pc_relative_calls_and_jumps && target_is_builtin) {
146 int32_t code_target_index = AddCodeTarget(code);
147 b(code_target_index * kInstrSize, cond, RelocInfo::RELATIVE_CODE_TARGET);
148 return;
149 } else if (root_array_available_ && options().isolate_independent_code) {
150 // This branch is taken only for specific cctests, where we force isolate
151 // creation at runtime. At this point, Code space isn't restricted to a
152 // size s.t. pc-relative calls may be used.
153 UseScratchRegisterScope temps(this);
154 Register scratch = temps.Acquire();
155 int offset = IsolateData::BuiltinEntrySlotOffset(code->builtin_id());
156 ldr(scratch, MemOperand(kRootRegister, offset));
157 Jump(scratch, cond);
158 return;
159 } else if (options().inline_offheap_trampolines && target_is_builtin) {
160 // Inline the trampoline.
161 RecordCommentForOffHeapTrampoline(builtin);
162 // Use ip directly instead of using UseScratchRegisterScope, as we do not
163 // preserve scratch registers across calls.
164 mov(ip, Operand(BuiltinEntry(builtin), RelocInfo::OFF_HEAP_TARGET));
165 Jump(ip, cond);
166 return;
167 }
168
169 // 'code' is always generated ARM code, never THUMB code
170 Jump(static_cast<intptr_t>(code.address()), rmode, cond);
171 }
172
Jump(const ExternalReference& reference)173 void TurboAssembler::Jump(const ExternalReference& reference) {
174 UseScratchRegisterScope temps(this);
175 Register scratch = temps.Acquire();
176 Move(scratch, reference);
177 Jump(scratch);
178 }
179
Call(Register target, Condition cond)180 void TurboAssembler::Call(Register target, Condition cond) {
181 // Block constant pool for the call instruction sequence.
182 BlockConstPoolScope block_const_pool(this);
183 blx(target, cond);
184 }
185
Call(Address target, RelocInfo::Mode rmode, Condition cond, TargetAddressStorageMode mode, bool check_constant_pool)186 void TurboAssembler::Call(Address target, RelocInfo::Mode rmode, Condition cond,
187 TargetAddressStorageMode mode,
188 bool check_constant_pool) {
189 // Check if we have to emit the constant pool before we block it.
190 if (check_constant_pool) MaybeCheckConstPool();
191 // Block constant pool for the call instruction sequence.
192 BlockConstPoolScope block_const_pool(this);
193
194 bool old_predictable_code_size = predictable_code_size();
195 if (mode == NEVER_INLINE_TARGET_ADDRESS) {
196 set_predictable_code_size(true);
197 }
198
199 // Use ip directly instead of using UseScratchRegisterScope, as we do not
200 // preserve scratch registers across calls.
201
202 // Call sequence on V7 or later may be :
203 // movw ip, #... @ call address low 16
204 // movt ip, #... @ call address high 16
205 // blx ip
206 // @ return address
207 // Or for pre-V7 or values that may be back-patched
208 // to avoid ICache flushes:
209 // ldr ip, [pc, #...] @ call address
210 // blx ip
211 // @ return address
212
213 mov(ip, Operand(target, rmode));
214 blx(ip, cond);
215
216 if (mode == NEVER_INLINE_TARGET_ADDRESS) {
217 set_predictable_code_size(old_predictable_code_size);
218 }
219 }
220
Call(Handle<Code> code, RelocInfo::Mode rmode, Condition cond, TargetAddressStorageMode mode, bool check_constant_pool)221 void TurboAssembler::Call(Handle<Code> code, RelocInfo::Mode rmode,
222 Condition cond, TargetAddressStorageMode mode,
223 bool check_constant_pool) {
224 DCHECK(RelocInfo::IsCodeTarget(rmode));
225 DCHECK_IMPLIES(options().isolate_independent_code,
226 Builtins::IsIsolateIndependentBuiltin(*code));
227 DCHECK_IMPLIES(options().use_pc_relative_calls_and_jumps,
228 Builtins::IsIsolateIndependentBuiltin(*code));
229
230 Builtin builtin = Builtin::kNoBuiltinId;
231 bool target_is_builtin =
232 isolate()->builtins()->IsBuiltinHandle(code, &builtin);
233
234 if (target_is_builtin && options().use_pc_relative_calls_and_jumps) {
235 int32_t code_target_index = AddCodeTarget(code);
236 bl(code_target_index * kInstrSize, cond, RelocInfo::RELATIVE_CODE_TARGET);
237 return;
238 } else if (root_array_available_ && options().isolate_independent_code) {
239 // This branch is taken only for specific cctests, where we force isolate
240 // creation at runtime. At this point, Code space isn't restricted to a
241 // size s.t. pc-relative calls may be used.
242 int offset = IsolateData::BuiltinEntrySlotOffset(code->builtin_id());
243 ldr(ip, MemOperand(kRootRegister, offset));
244 Call(ip, cond);
245 return;
246 } else if (target_is_builtin && options().inline_offheap_trampolines) {
247 // Inline the trampoline.
248 CallBuiltin(builtin);
249 return;
250 }
251
252 // 'code' is always generated ARM code, never THUMB code
253 DCHECK(code->IsExecutable());
254 Call(code.address(), rmode, cond, mode);
255 }
256
LoadEntryFromBuiltinIndex(Register builtin_index)257 void TurboAssembler::LoadEntryFromBuiltinIndex(Register builtin_index) {
258 ASM_CODE_COMMENT(this);
259 STATIC_ASSERT(kSystemPointerSize == 4);
260 STATIC_ASSERT(kSmiShiftSize == 0);
261 STATIC_ASSERT(kSmiTagSize == 1);
262 STATIC_ASSERT(kSmiTag == 0);
263
264 // The builtin_index register contains the builtin index as a Smi.
265 // Untagging is folded into the indexing operand below.
266 mov(builtin_index,
267 Operand(builtin_index, LSL, kSystemPointerSizeLog2 - kSmiTagSize));
268 add(builtin_index, builtin_index,
269 Operand(IsolateData::builtin_entry_table_offset()));
270 ldr(builtin_index, MemOperand(kRootRegister, builtin_index));
271 }
272
CallBuiltinByIndex(Register builtin_index)273 void TurboAssembler::CallBuiltinByIndex(Register builtin_index) {
274 LoadEntryFromBuiltinIndex(builtin_index);
275 Call(builtin_index);
276 }
277
LoadEntryFromBuiltin(Builtin builtin, Register destination)278 void TurboAssembler::LoadEntryFromBuiltin(Builtin builtin,
279 Register destination) {
280 ASM_CODE_COMMENT(this);
281 ldr(destination, EntryFromBuiltinAsOperand(builtin));
282 }
283
EntryFromBuiltinAsOperand(Builtin builtin)284 MemOperand TurboAssembler::EntryFromBuiltinAsOperand(Builtin builtin) {
285 ASM_CODE_COMMENT(this);
286 DCHECK(root_array_available());
287 return MemOperand(kRootRegister,
288 IsolateData::BuiltinEntrySlotOffset(builtin));
289 }
290
CallBuiltin(Builtin builtin, Condition cond)291 void TurboAssembler::CallBuiltin(Builtin builtin, Condition cond) {
292 ASM_CODE_COMMENT_STRING(this, CommentForOffHeapTrampoline("call", builtin));
293 DCHECK(Builtins::IsBuiltinId(builtin));
294 // Use ip directly instead of using UseScratchRegisterScope, as we do not
295 // preserve scratch registers across calls.
296 mov(ip, Operand(BuiltinEntry(builtin), RelocInfo::OFF_HEAP_TARGET));
297 Call(ip, cond);
298 }
299
LoadCodeObjectEntry(Register destination, Register code_object)300 void TurboAssembler::LoadCodeObjectEntry(Register destination,
301 Register code_object) {
302 ASM_CODE_COMMENT(this);
303 // Code objects are called differently depending on whether we are generating
304 // builtin code (which will later be embedded into the binary) or compiling
305 // user JS code at runtime.
306 // * Builtin code runs in --jitless mode and thus must not call into on-heap
307 // Code targets. Instead, we dispatch through the builtins entry table.
308 // * Codegen at runtime does not have this restriction and we can use the
309 // shorter, branchless instruction sequence. The assumption here is that
310 // targets are usually generated code and not builtin Code objects.
311
312 if (options().isolate_independent_code) {
313 DCHECK(root_array_available());
314 Label if_code_is_off_heap, out;
315
316 {
317 UseScratchRegisterScope temps(this);
318 Register scratch = temps.Acquire();
319
320 DCHECK(!AreAliased(destination, scratch));
321 DCHECK(!AreAliased(code_object, scratch));
322
323 // Check whether the Code object is an off-heap trampoline. If so, call
324 // its (off-heap) entry point directly without going through the (on-heap)
325 // trampoline. Otherwise, just call the Code object as always.
326 ldr(scratch, FieldMemOperand(code_object, Code::kFlagsOffset));
327 tst(scratch, Operand(Code::IsOffHeapTrampoline::kMask));
328 b(ne, &if_code_is_off_heap);
329
330 // Not an off-heap trampoline, the entry point is at
331 // Code::raw_instruction_start().
332 add(destination, code_object,
333 Operand(Code::kHeaderSize - kHeapObjectTag));
334 jmp(&out);
335
336 // An off-heap trampoline, the entry point is loaded from the builtin
337 // entry table.
338 bind(&if_code_is_off_heap);
339 ldr(scratch, FieldMemOperand(code_object, Code::kBuiltinIndexOffset));
340 lsl(destination, scratch, Operand(kSystemPointerSizeLog2));
341 }
342 add(destination, destination, kRootRegister);
343 ldr(destination,
344 MemOperand(destination, IsolateData::builtin_entry_table_offset()));
345
346 bind(&out);
347 } else {
348 add(destination, code_object, Operand(Code::kHeaderSize - kHeapObjectTag));
349 }
350 }
351
CallCodeObject(Register code_object)352 void TurboAssembler::CallCodeObject(Register code_object) {
353 ASM_CODE_COMMENT(this);
354 LoadCodeObjectEntry(code_object, code_object);
355 Call(code_object);
356 }
357
JumpCodeObject(Register code_object, JumpMode jump_mode)358 void TurboAssembler::JumpCodeObject(Register code_object, JumpMode jump_mode) {
359 ASM_CODE_COMMENT(this);
360 DCHECK_EQ(JumpMode::kJump, jump_mode);
361 LoadCodeObjectEntry(code_object, code_object);
362 Jump(code_object);
363 }
364
StoreReturnAddressAndCall(Register target)365 void TurboAssembler::StoreReturnAddressAndCall(Register target) {
366 ASM_CODE_COMMENT(this);
367 // This generates the final instruction sequence for calls to C functions
368 // once an exit frame has been constructed.
369 //
370 // Note that this assumes the caller code (i.e. the Code object currently
371 // being generated) is immovable or that the callee function cannot trigger
372 // GC, since the callee function will return to it.
373
374 // Compute the return address in lr to return to after the jump below. The pc
375 // is already at '+ 8' from the current instruction; but return is after three
376 // instructions, so add another 4 to pc to get the return address.
377 Assembler::BlockConstPoolScope block_const_pool(this);
378 add(lr, pc, Operand(4));
379 str(lr, MemOperand(sp));
380 Call(target);
381 }
382
Ret(Condition cond)383 void TurboAssembler::Ret(Condition cond) { bx(lr, cond); }
384
Drop(int count, Condition cond)385 void TurboAssembler::Drop(int count, Condition cond) {
386 if (count > 0) {
387 add(sp, sp, Operand(count * kPointerSize), LeaveCC, cond);
388 }
389 }
390
Drop(Register count, Condition cond)391 void TurboAssembler::Drop(Register count, Condition cond) {
392 add(sp, sp, Operand(count, LSL, kPointerSizeLog2), LeaveCC, cond);
393 }
394
Ret(int drop, Condition cond)395 void TurboAssembler::Ret(int drop, Condition cond) {
396 Drop(drop, cond);
397 Ret(cond);
398 }
399
Call(Label* target)400 void TurboAssembler::Call(Label* target) { bl(target); }
401
Push(Handle<HeapObject> handle)402 void TurboAssembler::Push(Handle<HeapObject> handle) {
403 UseScratchRegisterScope temps(this);
404 Register scratch = temps.Acquire();
405 mov(scratch, Operand(handle));
406 push(scratch);
407 }
408
Push(Smi smi)409 void TurboAssembler::Push(Smi smi) {
410 UseScratchRegisterScope temps(this);
411 Register scratch = temps.Acquire();
412 mov(scratch, Operand(smi));
413 push(scratch);
414 }
415
PushArray(Register array, Register size, Register scratch, PushArrayOrder order)416 void TurboAssembler::PushArray(Register array, Register size, Register scratch,
417 PushArrayOrder order) {
418 ASM_CODE_COMMENT(this);
419 UseScratchRegisterScope temps(this);
420 Register counter = scratch;
421 Register tmp = temps.Acquire();
422 DCHECK(!AreAliased(array, size, counter, tmp));
423 Label loop, entry;
424 if (order == PushArrayOrder::kReverse) {
425 mov(counter, Operand(0));
426 b(&entry);
427 bind(&loop);
428 ldr(tmp, MemOperand(array, counter, LSL, kSystemPointerSizeLog2));
429 push(tmp);
430 add(counter, counter, Operand(1));
431 bind(&entry);
432 cmp(counter, size);
433 b(lt, &loop);
434 } else {
435 mov(counter, size);
436 b(&entry);
437 bind(&loop);
438 ldr(tmp, MemOperand(array, counter, LSL, kSystemPointerSizeLog2));
439 push(tmp);
440 bind(&entry);
441 sub(counter, counter, Operand(1), SetCC);
442 b(ge, &loop);
443 }
444 }
445
Move(Register dst, Smi smi)446 void TurboAssembler::Move(Register dst, Smi smi) { mov(dst, Operand(smi)); }
447
Move(Register dst, Handle<HeapObject> value)448 void TurboAssembler::Move(Register dst, Handle<HeapObject> value) {
449 // TODO(jgruber,v8:8887): Also consider a root-relative load when generating
450 // non-isolate-independent code. In many cases it might be cheaper than
451 // embedding the relocatable value.
452 if (root_array_available_ && options().isolate_independent_code) {
453 IndirectLoadConstant(dst, value);
454 return;
455 }
456 mov(dst, Operand(value));
457 }
458
Move(Register dst, ExternalReference reference)459 void TurboAssembler::Move(Register dst, ExternalReference reference) {
460 // TODO(jgruber,v8:8887): Also consider a root-relative load when generating
461 // non-isolate-independent code. In many cases it might be cheaper than
462 // embedding the relocatable value.
463 if (root_array_available_ && options().isolate_independent_code) {
464 IndirectLoadExternalReference(dst, reference);
465 return;
466 }
467 mov(dst, Operand(reference));
468 }
469
Move(Register dst, Register src, Condition cond)470 void TurboAssembler::Move(Register dst, Register src, Condition cond) {
471 if (dst != src) {
472 mov(dst, src, LeaveCC, cond);
473 }
474 }
475
Move(SwVfpRegister dst, SwVfpRegister src, Condition cond)476 void TurboAssembler::Move(SwVfpRegister dst, SwVfpRegister src,
477 Condition cond) {
478 if (dst != src) {
479 vmov(dst, src, cond);
480 }
481 }
482
Move(DwVfpRegister dst, DwVfpRegister src, Condition cond)483 void TurboAssembler::Move(DwVfpRegister dst, DwVfpRegister src,
484 Condition cond) {
485 if (dst != src) {
486 vmov(dst, src, cond);
487 }
488 }
489
Move(QwNeonRegister dst, QwNeonRegister src)490 void TurboAssembler::Move(QwNeonRegister dst, QwNeonRegister src) {
491 if (dst != src) {
492 vmov(dst, src);
493 }
494 }
495
MovePair(Register dst0, Register src0, Register dst1, Register src1)496 void TurboAssembler::MovePair(Register dst0, Register src0, Register dst1,
497 Register src1) {
498 DCHECK_NE(dst0, dst1);
499 if (dst0 != src1) {
500 Move(dst0, src0);
501 Move(dst1, src1);
502 } else if (dst1 != src0) {
503 // Swap the order of the moves to resolve the overlap.
504 Move(dst1, src1);
505 Move(dst0, src0);
506 } else {
507 // Worse case scenario, this is a swap.
508 Swap(dst0, src0);
509 }
510 }
511
Swap(Register srcdst0, Register srcdst1)512 void TurboAssembler::Swap(Register srcdst0, Register srcdst1) {
513 DCHECK(srcdst0 != srcdst1);
514 UseScratchRegisterScope temps(this);
515 Register scratch = temps.Acquire();
516 mov(scratch, srcdst0);
517 mov(srcdst0, srcdst1);
518 mov(srcdst1, scratch);
519 }
520
Swap(DwVfpRegister srcdst0, DwVfpRegister srcdst1)521 void TurboAssembler::Swap(DwVfpRegister srcdst0, DwVfpRegister srcdst1) {
522 DCHECK(srcdst0 != srcdst1);
523 DCHECK(VfpRegisterIsAvailable(srcdst0));
524 DCHECK(VfpRegisterIsAvailable(srcdst1));
525
526 if (CpuFeatures::IsSupported(NEON)) {
527 vswp(srcdst0, srcdst1);
528 } else {
529 UseScratchRegisterScope temps(this);
530 DwVfpRegister scratch = temps.AcquireD();
531 vmov(scratch, srcdst0);
532 vmov(srcdst0, srcdst1);
533 vmov(srcdst1, scratch);
534 }
535 }
536
Swap(QwNeonRegister srcdst0, QwNeonRegister srcdst1)537 void TurboAssembler::Swap(QwNeonRegister srcdst0, QwNeonRegister srcdst1) {
538 DCHECK(srcdst0 != srcdst1);
539 vswp(srcdst0, srcdst1);
540 }
541
Mls(Register dst, Register src1, Register src2, Register srcA, Condition cond)542 void MacroAssembler::Mls(Register dst, Register src1, Register src2,
543 Register srcA, Condition cond) {
544 if (CpuFeatures::IsSupported(ARMv7)) {
545 CpuFeatureScope scope(this, ARMv7);
546 mls(dst, src1, src2, srcA, cond);
547 } else {
548 UseScratchRegisterScope temps(this);
549 Register scratch = temps.Acquire();
550 DCHECK(srcA != scratch);
551 mul(scratch, src1, src2, LeaveCC, cond);
552 sub(dst, srcA, scratch, LeaveCC, cond);
553 }
554 }
555
And(Register dst, Register src1, const Operand& src2, Condition cond)556 void MacroAssembler::And(Register dst, Register src1, const Operand& src2,
557 Condition cond) {
558 if (!src2.IsRegister() && !src2.MustOutputRelocInfo(this) &&
559 src2.immediate() == 0) {
560 mov(dst, Operand::Zero(), LeaveCC, cond);
561 } else if (!(src2.InstructionsRequired(this) == 1) &&
562 !src2.MustOutputRelocInfo(this) &&
563 CpuFeatures::IsSupported(ARMv7) &&
564 base::bits::IsPowerOfTwo(src2.immediate() + 1)) {
565 CpuFeatureScope scope(this, ARMv7);
566 ubfx(dst, src1, 0,
567 base::bits::WhichPowerOfTwo(static_cast<uint32_t>(src2.immediate()) +
568 1),
569 cond);
570 } else {
571 and_(dst, src1, src2, LeaveCC, cond);
572 }
573 }
574
Ubfx(Register dst, Register src1, int lsb, int width, Condition cond)575 void MacroAssembler::Ubfx(Register dst, Register src1, int lsb, int width,
576 Condition cond) {
577 DCHECK_LT(lsb, 32);
578 if (!CpuFeatures::IsSupported(ARMv7) || predictable_code_size()) {
579 int mask = (1u << (width + lsb)) - 1u - ((1u << lsb) - 1u);
580 and_(dst, src1, Operand(mask), LeaveCC, cond);
581 if (lsb != 0) {
582 mov(dst, Operand(dst, LSR, lsb), LeaveCC, cond);
583 }
584 } else {
585 CpuFeatureScope scope(this, ARMv7);
586 ubfx(dst, src1, lsb, width, cond);
587 }
588 }
589
Sbfx(Register dst, Register src1, int lsb, int width, Condition cond)590 void MacroAssembler::Sbfx(Register dst, Register src1, int lsb, int width,
591 Condition cond) {
592 DCHECK_LT(lsb, 32);
593 if (!CpuFeatures::IsSupported(ARMv7) || predictable_code_size()) {
594 int mask = (1 << (width + lsb)) - 1 - ((1 << lsb) - 1);
595 and_(dst, src1, Operand(mask), LeaveCC, cond);
596 int shift_up = 32 - lsb - width;
597 int shift_down = lsb + shift_up;
598 if (shift_up != 0) {
599 mov(dst, Operand(dst, LSL, shift_up), LeaveCC, cond);
600 }
601 if (shift_down != 0) {
602 mov(dst, Operand(dst, ASR, shift_down), LeaveCC, cond);
603 }
604 } else {
605 CpuFeatureScope scope(this, ARMv7);
606 sbfx(dst, src1, lsb, width, cond);
607 }
608 }
609
Bfc(Register dst, Register src, int lsb, int width, Condition cond)610 void TurboAssembler::Bfc(Register dst, Register src, int lsb, int width,
611 Condition cond) {
612 DCHECK_LT(lsb, 32);
613 if (!CpuFeatures::IsSupported(ARMv7) || predictable_code_size()) {
614 int mask = (1 << (width + lsb)) - 1 - ((1 << lsb) - 1);
615 bic(dst, src, Operand(mask));
616 } else {
617 CpuFeatureScope scope(this, ARMv7);
618 Move(dst, src, cond);
619 bfc(dst, lsb, width, cond);
620 }
621 }
622
LoadRoot(Register destination, RootIndex index, Condition cond)623 void TurboAssembler::LoadRoot(Register destination, RootIndex index,
624 Condition cond) {
625 ldr(destination,
626 MemOperand(kRootRegister, RootRegisterOffsetForRootIndex(index)), cond);
627 }
628
RecordWriteField(Register object, int offset, Register value, LinkRegisterStatus lr_status, SaveFPRegsMode save_fp, RememberedSetAction remembered_set_action, SmiCheck smi_check)629 void MacroAssembler::RecordWriteField(Register object, int offset,
630 Register value,
631 LinkRegisterStatus lr_status,
632 SaveFPRegsMode save_fp,
633 RememberedSetAction remembered_set_action,
634 SmiCheck smi_check) {
635 ASM_CODE_COMMENT(this);
636 // First, check if a write barrier is even needed. The tests below
637 // catch stores of Smis.
638 Label done;
639
640 // Skip barrier if writing a smi.
641 if (smi_check == SmiCheck::kInline) {
642 JumpIfSmi(value, &done);
643 }
644
645 // Although the object register is tagged, the offset is relative to the start
646 // of the object, so so offset must be a multiple of kPointerSize.
647 DCHECK(IsAligned(offset, kPointerSize));
648
649 if (FLAG_debug_code) {
650 ASM_CODE_COMMENT_STRING(this, "Verify slot_address");
651 Label ok;
652 UseScratchRegisterScope temps(this);
653 Register scratch = temps.Acquire();
654 DCHECK(!AreAliased(object, value, scratch));
655 add(scratch, object, Operand(offset - kHeapObjectTag));
656 tst(scratch, Operand(kPointerSize - 1));
657 b(eq, &ok);
658 stop();
659 bind(&ok);
660 }
661
662 RecordWrite(object, Operand(offset - kHeapObjectTag), value, lr_status,
663 save_fp, remembered_set_action, SmiCheck::kOmit);
664
665 bind(&done);
666 }
667
MaybeSaveRegisters(RegList registers)668 void TurboAssembler::MaybeSaveRegisters(RegList registers) {
669 if (registers.is_empty()) return;
670 ASM_CODE_COMMENT(this);
671 stm(db_w, sp, registers);
672 }
673
MaybeRestoreRegisters(RegList registers)674 void TurboAssembler::MaybeRestoreRegisters(RegList registers) {
675 if (registers.is_empty()) return;
676 ASM_CODE_COMMENT(this);
677 ldm(ia_w, sp, registers);
678 }
679
CallEphemeronKeyBarrier(Register object, Operand offset, SaveFPRegsMode fp_mode)680 void TurboAssembler::CallEphemeronKeyBarrier(Register object, Operand offset,
681 SaveFPRegsMode fp_mode) {
682 ASM_CODE_COMMENT(this);
683 RegList registers = WriteBarrierDescriptor::ComputeSavedRegisters(object);
684 MaybeSaveRegisters(registers);
685
686 Register object_parameter = WriteBarrierDescriptor::ObjectRegister();
687 Register slot_address_parameter =
688 WriteBarrierDescriptor::SlotAddressRegister();
689 MoveObjectAndSlot(object_parameter, slot_address_parameter, object, offset);
690
691 Call(isolate()->builtins()->code_handle(
692 Builtins::GetEphemeronKeyBarrierStub(fp_mode)),
693 RelocInfo::CODE_TARGET);
694 MaybeRestoreRegisters(registers);
695 }
696
CallRecordWriteStubSaveRegisters( Register object, Operand offset, RememberedSetAction remembered_set_action, SaveFPRegsMode fp_mode, StubCallMode mode)697 void TurboAssembler::CallRecordWriteStubSaveRegisters(
698 Register object, Operand offset, RememberedSetAction remembered_set_action,
699 SaveFPRegsMode fp_mode, StubCallMode mode) {
700 ASM_CODE_COMMENT(this);
701 RegList registers = WriteBarrierDescriptor::ComputeSavedRegisters(object);
702 MaybeSaveRegisters(registers);
703
704 Register object_parameter = WriteBarrierDescriptor::ObjectRegister();
705 Register slot_address_parameter =
706 WriteBarrierDescriptor::SlotAddressRegister();
707 MoveObjectAndSlot(object_parameter, slot_address_parameter, object, offset);
708
709 CallRecordWriteStub(object_parameter, slot_address_parameter,
710 remembered_set_action, fp_mode, mode);
711
712 MaybeRestoreRegisters(registers);
713 }
714
CallRecordWriteStub( Register object, Register slot_address, RememberedSetAction remembered_set_action, SaveFPRegsMode fp_mode, StubCallMode mode)715 void TurboAssembler::CallRecordWriteStub(
716 Register object, Register slot_address,
717 RememberedSetAction remembered_set_action, SaveFPRegsMode fp_mode,
718 StubCallMode mode) {
719 ASM_CODE_COMMENT(this);
720 DCHECK_EQ(WriteBarrierDescriptor::ObjectRegister(), object);
721 DCHECK_EQ(WriteBarrierDescriptor::SlotAddressRegister(), slot_address);
722 #if V8_ENABLE_WEBASSEMBLY
723 if (mode == StubCallMode::kCallWasmRuntimeStub) {
724 auto wasm_target =
725 wasm::WasmCode::GetRecordWriteStub(remembered_set_action, fp_mode);
726 Call(wasm_target, RelocInfo::WASM_STUB_CALL);
727 #else
728 if (false) {
729 #endif
730 } else {
731 Builtin builtin =
732 Builtins::GetRecordWriteStub(remembered_set_action, fp_mode);
733 if (options().inline_offheap_trampolines) {
734 CallBuiltin(builtin);
735 } else {
736 Handle<Code> code_target = isolate()->builtins()->code_handle(builtin);
737 Call(code_target, RelocInfo::CODE_TARGET);
738 }
739 }
740 }
741
742 void TurboAssembler::MoveObjectAndSlot(Register dst_object, Register dst_slot,
743 Register object, Operand offset) {
744 DCHECK_NE(dst_object, dst_slot);
745 DCHECK(offset.IsRegister() || offset.IsImmediate());
746 // If `offset` is a register, it cannot overlap with `object`.
747 DCHECK_IMPLIES(offset.IsRegister(), offset.rm() != object);
748
749 // If the slot register does not overlap with the object register, we can
750 // overwrite it.
751 if (dst_slot != object) {
752 add(dst_slot, object, offset);
753 Move(dst_object, object);
754 return;
755 }
756
757 DCHECK_EQ(dst_slot, object);
758
759 // If the destination object register does not overlap with the offset
760 // register, we can overwrite it.
761 if (!offset.IsRegister() || (offset.rm() != dst_object)) {
762 Move(dst_object, dst_slot);
763 add(dst_slot, dst_slot, offset);
764 return;
765 }
766
767 DCHECK_EQ(dst_object, offset.rm());
768
769 // We only have `dst_slot` and `dst_object` left as distinct registers so we
770 // have to swap them. We write this as a add+sub sequence to avoid using a
771 // scratch register.
772 add(dst_slot, dst_slot, dst_object);
773 sub(dst_object, dst_slot, dst_object);
774 }
775
776 // The register 'object' contains a heap object pointer. The heap object tag is
777 // shifted away. A scratch register also needs to be available.
778 void MacroAssembler::RecordWrite(Register object, Operand offset,
779 Register value, LinkRegisterStatus lr_status,
780 SaveFPRegsMode fp_mode,
781 RememberedSetAction remembered_set_action,
782 SmiCheck smi_check) {
783 DCHECK(!AreAliased(object, value));
784 if (FLAG_debug_code) {
785 ASM_CODE_COMMENT_STRING(this, "Verify slot_address");
786 UseScratchRegisterScope temps(this);
787 Register scratch = temps.Acquire();
788 DCHECK(!AreAliased(object, value, scratch));
789 add(scratch, object, offset);
790 ldr(scratch, MemOperand(scratch));
791 cmp(scratch, value);
792 Check(eq, AbortReason::kWrongAddressOrValuePassedToRecordWrite);
793 }
794
795 if ((remembered_set_action == RememberedSetAction::kOmit &&
796 !FLAG_incremental_marking) ||
797 FLAG_disable_write_barriers) {
798 return;
799 }
800
801 // First, check if a write barrier is even needed. The tests below
802 // catch stores of smis and stores into the young generation.
803 Label done;
804
805 if (smi_check == SmiCheck::kInline) {
806 JumpIfSmi(value, &done);
807 }
808
809 CheckPageFlag(value, MemoryChunk::kPointersToHereAreInterestingMask, eq,
810 &done);
811 CheckPageFlag(object, MemoryChunk::kPointersFromHereAreInterestingMask, eq,
812 &done);
813
814 // Record the actual write.
815 if (lr_status == kLRHasNotBeenSaved) {
816 push(lr);
817 }
818
819 Register slot_address = WriteBarrierDescriptor::SlotAddressRegister();
820 DCHECK(!AreAliased(object, value, slot_address));
821 DCHECK(!offset.IsRegister());
822 add(slot_address, object, offset);
823 CallRecordWriteStub(object, slot_address, remembered_set_action, fp_mode);
824 if (lr_status == kLRHasNotBeenSaved) {
825 pop(lr);
826 }
827
828 if (FLAG_debug_code) Move(slot_address, Operand(kZapValue));
829
830 bind(&done);
831 }
832
833 void TurboAssembler::PushCommonFrame(Register marker_reg) {
834 ASM_CODE_COMMENT(this);
835 if (marker_reg.is_valid()) {
836 if (marker_reg.code() > fp.code()) {
837 stm(db_w, sp, {fp, lr});
838 mov(fp, Operand(sp));
839 Push(marker_reg);
840 } else {
841 stm(db_w, sp, {marker_reg, fp, lr});
842 add(fp, sp, Operand(kPointerSize));
843 }
844 } else {
845 stm(db_w, sp, {fp, lr});
846 mov(fp, sp);
847 }
848 }
849
850 void TurboAssembler::PushStandardFrame(Register function_reg) {
851 ASM_CODE_COMMENT(this);
852 DCHECK(!function_reg.is_valid() || function_reg.code() < cp.code());
853 stm(db_w, sp, {function_reg, cp, fp, lr});
854 int offset = -StandardFrameConstants::kContextOffset;
855 offset += function_reg.is_valid() ? kPointerSize : 0;
856 add(fp, sp, Operand(offset));
857 Push(kJavaScriptCallArgCountRegister);
858 }
859
860 void TurboAssembler::VFPCanonicalizeNaN(const DwVfpRegister dst,
861 const DwVfpRegister src,
862 const Condition cond) {
863 // Subtracting 0.0 preserves all inputs except for signalling NaNs, which
864 // become quiet NaNs. We use vsub rather than vadd because vsub preserves -0.0
865 // inputs: -0.0 + 0.0 = 0.0, but -0.0 - 0.0 = -0.0.
866 vsub(dst, src, kDoubleRegZero, cond);
867 }
868
869 void TurboAssembler::VFPCompareAndSetFlags(const SwVfpRegister src1,
870 const SwVfpRegister src2,
871 const Condition cond) {
872 // Compare and move FPSCR flags to the normal condition flags.
873 VFPCompareAndLoadFlags(src1, src2, pc, cond);
874 }
875
876 void TurboAssembler::VFPCompareAndSetFlags(const SwVfpRegister src1,
877 const float src2,
878 const Condition cond) {
879 // Compare and move FPSCR flags to the normal condition flags.
880 VFPCompareAndLoadFlags(src1, src2, pc, cond);
881 }
882
883 void TurboAssembler::VFPCompareAndSetFlags(const DwVfpRegister src1,
884 const DwVfpRegister src2,
885 const Condition cond) {
886 // Compare and move FPSCR flags to the normal condition flags.
887 VFPCompareAndLoadFlags(src1, src2, pc, cond);
888 }
889
890 void TurboAssembler::VFPCompareAndSetFlags(const DwVfpRegister src1,
891 const double src2,
892 const Condition cond) {
893 // Compare and move FPSCR flags to the normal condition flags.
894 VFPCompareAndLoadFlags(src1, src2, pc, cond);
895 }
896
897 void TurboAssembler::VFPCompareAndLoadFlags(const SwVfpRegister src1,
898 const SwVfpRegister src2,
899 const Register fpscr_flags,
900 const Condition cond) {
901 // Compare and load FPSCR.
902 vcmp(src1, src2, cond);
903 vmrs(fpscr_flags, cond);
904 }
905
906 void TurboAssembler::VFPCompareAndLoadFlags(const SwVfpRegister src1,
907 const float src2,
908 const Register fpscr_flags,
909 const Condition cond) {
910 // Compare and load FPSCR.
911 vcmp(src1, src2, cond);
912 vmrs(fpscr_flags, cond);
913 }
914
915 void TurboAssembler::VFPCompareAndLoadFlags(const DwVfpRegister src1,
916 const DwVfpRegister src2,
917 const Register fpscr_flags,
918 const Condition cond) {
919 // Compare and load FPSCR.
920 vcmp(src1, src2, cond);
921 vmrs(fpscr_flags, cond);
922 }
923
924 void TurboAssembler::VFPCompareAndLoadFlags(const DwVfpRegister src1,
925 const double src2,
926 const Register fpscr_flags,
927 const Condition cond) {
928 // Compare and load FPSCR.
929 vcmp(src1, src2, cond);
930 vmrs(fpscr_flags, cond);
931 }
932
933 void TurboAssembler::VmovHigh(Register dst, DwVfpRegister src) {
934 if (src.code() < 16) {
935 const LowDwVfpRegister loc = LowDwVfpRegister::from_code(src.code());
936 vmov(dst, loc.high());
937 } else {
938 vmov(NeonS32, dst, src, 1);
939 }
940 }
941
942 void TurboAssembler::VmovHigh(DwVfpRegister dst, Register src) {
943 if (dst.code() < 16) {
944 const LowDwVfpRegister loc = LowDwVfpRegister::from_code(dst.code());
945 vmov(loc.high(), src);
946 } else {
947 vmov(NeonS32, dst, 1, src);
948 }
949 }
950
951 void TurboAssembler::VmovLow(Register dst, DwVfpRegister src) {
952 if (src.code() < 16) {
953 const LowDwVfpRegister loc = LowDwVfpRegister::from_code(src.code());
954 vmov(dst, loc.low());
955 } else {
956 vmov(NeonS32, dst, src, 0);
957 }
958 }
959
960 void TurboAssembler::VmovLow(DwVfpRegister dst, Register src) {
961 if (dst.code() < 16) {
962 const LowDwVfpRegister loc = LowDwVfpRegister::from_code(dst.code());
963 vmov(loc.low(), src);
964 } else {
965 vmov(NeonS32, dst, 0, src);
966 }
967 }
968
969 void TurboAssembler::VmovExtended(Register dst, int src_code) {
970 DCHECK_LE(SwVfpRegister::kNumRegisters, src_code);
971 DCHECK_GT(SwVfpRegister::kNumRegisters * 2, src_code);
972 if (src_code & 0x1) {
973 VmovHigh(dst, DwVfpRegister::from_code(src_code / 2));
974 } else {
975 VmovLow(dst, DwVfpRegister::from_code(src_code / 2));
976 }
977 }
978
979 void TurboAssembler::VmovExtended(int dst_code, Register src) {
980 DCHECK_LE(SwVfpRegister::kNumRegisters, dst_code);
981 DCHECK_GT(SwVfpRegister::kNumRegisters * 2, dst_code);
982 if (dst_code & 0x1) {
983 VmovHigh(DwVfpRegister::from_code(dst_code / 2), src);
984 } else {
985 VmovLow(DwVfpRegister::from_code(dst_code / 2), src);
986 }
987 }
988
989 void TurboAssembler::VmovExtended(int dst_code, int src_code) {
990 if (src_code == dst_code) return;
991
992 if (src_code < SwVfpRegister::kNumRegisters &&
993 dst_code < SwVfpRegister::kNumRegisters) {
994 // src and dst are both s-registers.
995 vmov(SwVfpRegister::from_code(dst_code),
996 SwVfpRegister::from_code(src_code));
997 return;
998 }
999 DwVfpRegister dst_d_reg = DwVfpRegister::from_code(dst_code / 2);
1000 DwVfpRegister src_d_reg = DwVfpRegister::from_code(src_code / 2);
1001 int dst_offset = dst_code & 1;
1002 int src_offset = src_code & 1;
1003 if (CpuFeatures::IsSupported(NEON)) {
1004 UseScratchRegisterScope temps(this);
1005 DwVfpRegister scratch = temps.AcquireD();
1006 // On Neon we can shift and insert from d-registers.
1007 if (src_offset == dst_offset) {
1008 // Offsets are the same, use vdup to copy the source to the opposite lane.
1009 vdup(Neon32, scratch, src_d_reg, src_offset);
1010 // Here we are extending the lifetime of scratch.
1011 src_d_reg = scratch;
1012 src_offset = dst_offset ^ 1;
1013 }
1014 if (dst_offset) {
1015 if (dst_d_reg == src_d_reg) {
1016 vdup(Neon32, dst_d_reg, src_d_reg, 0);
1017 } else {
1018 vsli(Neon64, dst_d_reg, src_d_reg, 32);
1019 }
1020 } else {
1021 if (dst_d_reg == src_d_reg) {
1022 vdup(Neon32, dst_d_reg, src_d_reg, 1);
1023 } else {
1024 vsri(Neon64, dst_d_reg, src_d_reg, 32);
1025 }
1026 }
1027 return;
1028 }
1029
1030 // Without Neon, use the scratch registers to move src and/or dst into
1031 // s-registers.
1032 UseScratchRegisterScope temps(this);
1033 LowDwVfpRegister d_scratch = temps.AcquireLowD();
1034 LowDwVfpRegister d_scratch2 = temps.AcquireLowD();
1035 int s_scratch_code = d_scratch.low().code();
1036 int s_scratch_code2 = d_scratch2.low().code();
1037 if (src_code < SwVfpRegister::kNumRegisters) {
1038 // src is an s-register, dst is not.
1039 vmov(d_scratch, dst_d_reg);
1040 vmov(SwVfpRegister::from_code(s_scratch_code + dst_offset),
1041 SwVfpRegister::from_code(src_code));
1042 vmov(dst_d_reg, d_scratch);
1043 } else if (dst_code < SwVfpRegister::kNumRegisters) {
1044 // dst is an s-register, src is not.
1045 vmov(d_scratch, src_d_reg);
1046 vmov(SwVfpRegister::from_code(dst_code),
1047 SwVfpRegister::from_code(s_scratch_code + src_offset));
1048 } else {
1049 // Neither src or dst are s-registers. Both scratch double registers are
1050 // available when there are 32 VFP registers.
1051 vmov(d_scratch, src_d_reg);
1052 vmov(d_scratch2, dst_d_reg);
1053 vmov(SwVfpRegister::from_code(s_scratch_code + dst_offset),
1054 SwVfpRegister::from_code(s_scratch_code2 + src_offset));
1055 vmov(dst_d_reg, d_scratch2);
1056 }
1057 }
1058
1059 void TurboAssembler::VmovExtended(int dst_code, const MemOperand& src) {
1060 if (dst_code < SwVfpRegister::kNumRegisters) {
1061 vldr(SwVfpRegister::from_code(dst_code), src);
1062 } else {
1063 UseScratchRegisterScope temps(this);
1064 LowDwVfpRegister scratch = temps.AcquireLowD();
1065 // TODO(bbudge) If Neon supported, use load single lane form of vld1.
1066 int dst_s_code = scratch.low().code() + (dst_code & 1);
1067 vmov(scratch, DwVfpRegister::from_code(dst_code / 2));
1068 vldr(SwVfpRegister::from_code(dst_s_code), src);
1069 vmov(DwVfpRegister::from_code(dst_code / 2), scratch);
1070 }
1071 }
1072
1073 void TurboAssembler::VmovExtended(const MemOperand& dst, int src_code) {
1074 if (src_code < SwVfpRegister::kNumRegisters) {
1075 vstr(SwVfpRegister::from_code(src_code), dst);
1076 } else {
1077 // TODO(bbudge) If Neon supported, use store single lane form of vst1.
1078 UseScratchRegisterScope temps(this);
1079 LowDwVfpRegister scratch = temps.AcquireLowD();
1080 int src_s_code = scratch.low().code() + (src_code & 1);
1081 vmov(scratch, DwVfpRegister::from_code(src_code / 2));
1082 vstr(SwVfpRegister::from_code(src_s_code), dst);
1083 }
1084 }
1085
1086 void TurboAssembler::ExtractLane(Register dst, QwNeonRegister src,
1087 NeonDataType dt, int lane) {
1088 int size = NeonSz(dt); // 0, 1, 2
1089 int byte = lane << size;
1090 int double_word = byte >> kDoubleSizeLog2;
1091 int double_byte = byte & (kDoubleSize - 1);
1092 int double_lane = double_byte >> size;
1093 DwVfpRegister double_source =
1094 DwVfpRegister::from_code(src.code() * 2 + double_word);
1095 vmov(dt, dst, double_source, double_lane);
1096 }
1097
1098 void TurboAssembler::ExtractLane(Register dst, DwVfpRegister src,
1099 NeonDataType dt, int lane) {
1100 int size = NeonSz(dt); // 0, 1, 2
1101 int byte = lane << size;
1102 int double_byte = byte & (kDoubleSize - 1);
1103 int double_lane = double_byte >> size;
1104 vmov(dt, dst, src, double_lane);
1105 }
1106
1107 void TurboAssembler::ExtractLane(SwVfpRegister dst, QwNeonRegister src,
1108 int lane) {
1109 int s_code = src.code() * 4 + lane;
1110 VmovExtended(dst.code(), s_code);
1111 }
1112
1113 void TurboAssembler::ExtractLane(DwVfpRegister dst, QwNeonRegister src,
1114 int lane) {
1115 DwVfpRegister double_dst = DwVfpRegister::from_code(src.code() * 2 + lane);
1116 vmov(dst, double_dst);
1117 }
1118
1119 void TurboAssembler::ReplaceLane(QwNeonRegister dst, QwNeonRegister src,
1120 Register src_lane, NeonDataType dt, int lane) {
1121 Move(dst, src);
1122 int size = NeonSz(dt); // 0, 1, 2
1123 int byte = lane << size;
1124 int double_word = byte >> kDoubleSizeLog2;
1125 int double_byte = byte & (kDoubleSize - 1);
1126 int double_lane = double_byte >> size;
1127 DwVfpRegister double_dst =
1128 DwVfpRegister::from_code(dst.code() * 2 + double_word);
1129 vmov(dt, double_dst, double_lane, src_lane);
1130 }
1131
1132 void TurboAssembler::ReplaceLane(QwNeonRegister dst, QwNeonRegister src,
1133 SwVfpRegister src_lane, int lane) {
1134 Move(dst, src);
1135 int s_code = dst.code() * 4 + lane;
1136 VmovExtended(s_code, src_lane.code());
1137 }
1138
1139 void TurboAssembler::ReplaceLane(QwNeonRegister dst, QwNeonRegister src,
1140 DwVfpRegister src_lane, int lane) {
1141 Move(dst, src);
1142 DwVfpRegister double_dst = DwVfpRegister::from_code(dst.code() * 2 + lane);
1143 vmov(double_dst, src_lane);
1144 }
1145
1146 void TurboAssembler::LoadLane(NeonSize sz, NeonListOperand dst_list,
1147 uint8_t lane, NeonMemOperand src) {
1148 if (sz == Neon64) {
1149 // vld1s is not valid for Neon64.
1150 vld1(Neon64, dst_list, src);
1151 } else {
1152 vld1s(sz, dst_list, lane, src);
1153 }
1154 }
1155
1156 void TurboAssembler::StoreLane(NeonSize sz, NeonListOperand src_list,
1157 uint8_t lane, NeonMemOperand dst) {
1158 if (sz == Neon64) {
1159 // vst1s is not valid for Neon64.
1160 vst1(Neon64, src_list, dst);
1161 } else {
1162 vst1s(sz, src_list, lane, dst);
1163 }
1164 }
1165
1166 void TurboAssembler::LslPair(Register dst_low, Register dst_high,
1167 Register src_low, Register src_high,
1168 Register shift) {
1169 DCHECK(!AreAliased(dst_high, src_low));
1170 DCHECK(!AreAliased(dst_high, shift));
1171 UseScratchRegisterScope temps(this);
1172 Register scratch = temps.Acquire();
1173
1174 Label less_than_32;
1175 Label done;
1176 rsb(scratch, shift, Operand(32), SetCC);
1177 b(gt, &less_than_32);
1178 // If shift >= 32
1179 and_(scratch, shift, Operand(0x1F));
1180 lsl(dst_high, src_low, Operand(scratch));
1181 mov(dst_low, Operand(0));
1182 jmp(&done);
1183 bind(&less_than_32);
1184 // If shift < 32
1185 lsl(dst_high, src_high, Operand(shift));
1186 orr(dst_high, dst_high, Operand(src_low, LSR, scratch));
1187 lsl(dst_low, src_low, Operand(shift));
1188 bind(&done);
1189 }
1190
1191 void TurboAssembler::LslPair(Register dst_low, Register dst_high,
1192 Register src_low, Register src_high,
1193 uint32_t shift) {
1194 DCHECK_GE(63, shift);
1195 DCHECK(!AreAliased(dst_high, src_low));
1196
1197 if (shift == 0) {
1198 Move(dst_high, src_high);
1199 Move(dst_low, src_low);
1200 } else if (shift == 32) {
1201 Move(dst_high, src_low);
1202 Move(dst_low, Operand(0));
1203 } else if (shift >= 32) {
1204 shift &= 0x1F;
1205 lsl(dst_high, src_low, Operand(shift));
1206 mov(dst_low, Operand(0));
1207 } else {
1208 lsl(dst_high, src_high, Operand(shift));
1209 orr(dst_high, dst_high, Operand(src_low, LSR, 32 - shift));
1210 lsl(dst_low, src_low, Operand(shift));
1211 }
1212 }
1213
1214 void TurboAssembler::LsrPair(Register dst_low, Register dst_high,
1215 Register src_low, Register src_high,
1216 Register shift) {
1217 DCHECK(!AreAliased(dst_low, src_high));
1218 DCHECK(!AreAliased(dst_low, shift));
1219 UseScratchRegisterScope temps(this);
1220 Register scratch = temps.Acquire();
1221
1222 Label less_than_32;
1223 Label done;
1224 rsb(scratch, shift, Operand(32), SetCC);
1225 b(gt, &less_than_32);
1226 // If shift >= 32
1227 and_(scratch, shift, Operand(0x1F));
1228 lsr(dst_low, src_high, Operand(scratch));
1229 mov(dst_high, Operand(0));
1230 jmp(&done);
1231 bind(&less_than_32);
1232 // If shift < 32
1233
1234 lsr(dst_low, src_low, Operand(shift));
1235 orr(dst_low, dst_low, Operand(src_high, LSL, scratch));
1236 lsr(dst_high, src_high, Operand(shift));
1237 bind(&done);
1238 }
1239
1240 void TurboAssembler::LsrPair(Register dst_low, Register dst_high,
1241 Register src_low, Register src_high,
1242 uint32_t shift) {
1243 DCHECK_GE(63, shift);
1244 DCHECK(!AreAliased(dst_low, src_high));
1245
1246 if (shift == 32) {
1247 mov(dst_low, src_high);
1248 mov(dst_high, Operand(0));
1249 } else if (shift > 32) {
1250 shift &= 0x1F;
1251 lsr(dst_low, src_high, Operand(shift));
1252 mov(dst_high, Operand(0));
1253 } else if (shift == 0) {
1254 Move(dst_low, src_low);
1255 Move(dst_high, src_high);
1256 } else {
1257 lsr(dst_low, src_low, Operand(shift));
1258 orr(dst_low, dst_low, Operand(src_high, LSL, 32 - shift));
1259 lsr(dst_high, src_high, Operand(shift));
1260 }
1261 }
1262
1263 void TurboAssembler::AsrPair(Register dst_low, Register dst_high,
1264 Register src_low, Register src_high,
1265 Register shift) {
1266 DCHECK(!AreAliased(dst_low, src_high));
1267 DCHECK(!AreAliased(dst_low, shift));
1268 UseScratchRegisterScope temps(this);
1269 Register scratch = temps.Acquire();
1270
1271 Label less_than_32;
1272 Label done;
1273 rsb(scratch, shift, Operand(32), SetCC);
1274 b(gt, &less_than_32);
1275 // If shift >= 32
1276 and_(scratch, shift, Operand(0x1F));
1277 asr(dst_low, src_high, Operand(scratch));
1278 asr(dst_high, src_high, Operand(31));
1279 jmp(&done);
1280 bind(&less_than_32);
1281 // If shift < 32
1282 lsr(dst_low, src_low, Operand(shift));
1283 orr(dst_low, dst_low, Operand(src_high, LSL, scratch));
1284 asr(dst_high, src_high, Operand(shift));
1285 bind(&done);
1286 }
1287
1288 void TurboAssembler::AsrPair(Register dst_low, Register dst_high,
1289 Register src_low, Register src_high,
1290 uint32_t shift) {
1291 DCHECK_GE(63, shift);
1292 DCHECK(!AreAliased(dst_low, src_high));
1293
1294 if (shift == 32) {
1295 mov(dst_low, src_high);
1296 asr(dst_high, src_high, Operand(31));
1297 } else if (shift > 32) {
1298 shift &= 0x1F;
1299 asr(dst_low, src_high, Operand(shift));
1300 asr(dst_high, src_high, Operand(31));
1301 } else if (shift == 0) {
1302 Move(dst_low, src_low);
1303 Move(dst_high, src_high);
1304 } else {
1305 lsr(dst_low, src_low, Operand(shift));
1306 orr(dst_low, dst_low, Operand(src_high, LSL, 32 - shift));
1307 asr(dst_high, src_high, Operand(shift));
1308 }
1309 }
1310
1311 void TurboAssembler::StubPrologue(StackFrame::Type type) {
1312 ASM_CODE_COMMENT(this);
1313 UseScratchRegisterScope temps(this);
1314 Register scratch = temps.Acquire();
1315 mov(scratch, Operand(StackFrame::TypeToMarker(type)));
1316 PushCommonFrame(scratch);
1317 }
1318
1319 void TurboAssembler::Prologue() { PushStandardFrame(r1); }
1320
1321 void TurboAssembler::DropArguments(Register count, ArgumentsCountType type,
1322 ArgumentsCountMode mode) {
1323 int receiver_bytes = (mode == kCountExcludesReceiver) ? kPointerSize : 0;
1324 switch (type) {
1325 case kCountIsInteger: {
1326 add(sp, sp, Operand(count, LSL, kPointerSizeLog2), LeaveCC);
1327 break;
1328 }
1329 case kCountIsSmi: {
1330 STATIC_ASSERT(kSmiTagSize == 1 && kSmiTag == 0);
1331 add(sp, sp, Operand(count, LSL, kPointerSizeLog2 - kSmiTagSize), LeaveCC);
1332 break;
1333 }
1334 case kCountIsBytes: {
1335 add(sp, sp, count, LeaveCC);
1336 break;
1337 }
1338 }
1339 if (receiver_bytes != 0) {
1340 add(sp, sp, Operand(receiver_bytes), LeaveCC);
1341 }
1342 }
1343
1344 void TurboAssembler::DropArgumentsAndPushNewReceiver(Register argc,
1345 Register receiver,
1346 ArgumentsCountType type,
1347 ArgumentsCountMode mode) {
1348 DCHECK(!AreAliased(argc, receiver));
1349 if (mode == kCountExcludesReceiver) {
1350 // Drop arguments without receiver and override old receiver.
1351 DropArguments(argc, type, kCountIncludesReceiver);
1352 str(receiver, MemOperand(sp, 0));
1353 } else {
1354 DropArguments(argc, type, mode);
1355 push(receiver);
1356 }
1357 }
1358
1359 void TurboAssembler::EnterFrame(StackFrame::Type type,
1360 bool load_constant_pool_pointer_reg) {
1361 ASM_CODE_COMMENT(this);
1362 // r0-r3: preserved
1363 UseScratchRegisterScope temps(this);
1364 Register scratch = no_reg;
1365 if (!StackFrame::IsJavaScript(type)) {
1366 scratch = temps.Acquire();
1367 mov(scratch, Operand(StackFrame::TypeToMarker(type)));
1368 }
1369 PushCommonFrame(scratch);
1370 #if V8_ENABLE_WEBASSEMBLY
1371 if (type == StackFrame::WASM) Push(kWasmInstanceRegister);
1372 #endif // V8_ENABLE_WEBASSEMBLY
1373 }
1374
1375 int TurboAssembler::LeaveFrame(StackFrame::Type type) {
1376 ASM_CODE_COMMENT(this);
1377 // r0: preserved
1378 // r1: preserved
1379 // r2: preserved
1380
1381 // Drop the execution stack down to the frame pointer and restore
1382 // the caller frame pointer and return address.
1383 mov(sp, fp);
1384 int frame_ends = pc_offset();
1385 ldm(ia_w, sp, {fp, lr});
1386 return frame_ends;
1387 }
1388
1389 #ifdef V8_OS_WIN
1390 void TurboAssembler::AllocateStackSpace(Register bytes_scratch) {
1391 // "Functions that allocate 4 KB or more on the stack must ensure that each
1392 // page prior to the final page is touched in order." Source:
1393 // https://docs.microsoft.com/en-us/cpp/build/overview-of-arm-abi-conventions?view=vs-2019#stack
1394 ASM_CODE_COMMENT(this);
1395 UseScratchRegisterScope temps(this);
1396 DwVfpRegister scratch = temps.AcquireD();
1397 Label check_offset;
1398 Label touch_next_page;
1399 jmp(&check_offset);
1400 bind(&touch_next_page);
1401 sub(sp, sp, Operand(kStackPageSize));
1402 // Just to touch the page, before we increment further.
1403 vldr(scratch, MemOperand(sp));
1404 sub(bytes_scratch, bytes_scratch, Operand(kStackPageSize));
1405
1406 bind(&check_offset);
1407 cmp(bytes_scratch, Operand(kStackPageSize));
1408 b(gt, &touch_next_page);
1409
1410 sub(sp, sp, bytes_scratch);
1411 }
1412
1413 void TurboAssembler::AllocateStackSpace(int bytes) {
1414 ASM_CODE_COMMENT(this);
1415 DCHECK_GE(bytes, 0);
1416 UseScratchRegisterScope temps(this);
1417 DwVfpRegister scratch = no_dreg;
1418 while (bytes > kStackPageSize) {
1419 if (scratch == no_dreg) {
1420 scratch = temps.AcquireD();
1421 }
1422 sub(sp, sp, Operand(kStackPageSize));
1423 vldr(scratch, MemOperand(sp));
1424 bytes -= kStackPageSize;
1425 }
1426 if (bytes == 0) return;
1427 sub(sp, sp, Operand(bytes));
1428 }
1429 #endif
1430
1431 void MacroAssembler::EnterExitFrame(bool save_doubles, int stack_space,
1432 StackFrame::Type frame_type) {
1433 ASM_CODE_COMMENT(this);
1434 DCHECK(frame_type == StackFrame::EXIT ||
1435 frame_type == StackFrame::BUILTIN_EXIT);
1436 UseScratchRegisterScope temps(this);
1437 Register scratch = temps.Acquire();
1438
1439 // Set up the frame structure on the stack.
1440 DCHECK_EQ(2 * kPointerSize, ExitFrameConstants::kCallerSPDisplacement);
1441 DCHECK_EQ(1 * kPointerSize, ExitFrameConstants::kCallerPCOffset);
1442 DCHECK_EQ(0 * kPointerSize, ExitFrameConstants::kCallerFPOffset);
1443 mov(scratch, Operand(StackFrame::TypeToMarker(frame_type)));
1444 PushCommonFrame(scratch);
1445 // Reserve room for saved entry sp.
1446 sub(sp, fp, Operand(ExitFrameConstants::kFixedFrameSizeFromFp));
1447 if (FLAG_debug_code) {
1448 mov(scratch, Operand::Zero());
1449 str(scratch, MemOperand(fp, ExitFrameConstants::kSPOffset));
1450 }
1451
1452 // Save the frame pointer and the context in top.
1453 Move(scratch, ExternalReference::Create(IsolateAddressId::kCEntryFPAddress,
1454 isolate()));
1455 str(fp, MemOperand(scratch));
1456 Move(scratch,
1457 ExternalReference::Create(IsolateAddressId::kContextAddress, isolate()));
1458 str(cp, MemOperand(scratch));
1459
1460 // Optionally save all double registers.
1461 if (save_doubles) {
1462 SaveFPRegs(sp, scratch);
1463 // Note that d0 will be accessible at
1464 // fp - ExitFrameConstants::kFrameSize -
1465 // DwVfpRegister::kNumRegisters * kDoubleSize,
1466 // since the sp slot and code slot were pushed after the fp.
1467 }
1468
1469 // Reserve place for the return address and stack space and align the frame
1470 // preparing for calling the runtime function.
1471 const int frame_alignment = MacroAssembler::ActivationFrameAlignment();
1472 AllocateStackSpace((stack_space + 1) * kPointerSize);
1473 if (frame_alignment > 0) {
1474 DCHECK(base::bits::IsPowerOfTwo(frame_alignment));
1475 and_(sp, sp, Operand(-frame_alignment));
1476 }
1477
1478 // Set the exit frame sp value to point just before the return address
1479 // location.
1480 add(scratch, sp, Operand(kPointerSize));
1481 str(scratch, MemOperand(fp, ExitFrameConstants::kSPOffset));
1482 }
1483
1484 int TurboAssembler::ActivationFrameAlignment() {
1485 #if V8_HOST_ARCH_ARM
1486 // Running on the real platform. Use the alignment as mandated by the local
1487 // environment.
1488 // Note: This will break if we ever start generating snapshots on one ARM
1489 // platform for another ARM platform with a different alignment.
1490 return base::OS::ActivationFrameAlignment();
1491 #else // V8_HOST_ARCH_ARM
1492 // If we are using the simulator then we should always align to the expected
1493 // alignment. As the simulator is used to generate snapshots we do not know
1494 // if the target platform will need alignment, so this is controlled from a
1495 // flag.
1496 return FLAG_sim_stack_alignment;
1497 #endif // V8_HOST_ARCH_ARM
1498 }
1499
1500 void MacroAssembler::LeaveExitFrame(bool save_doubles, Register argument_count,
1501 bool argument_count_is_length) {
1502 ASM_CODE_COMMENT(this);
1503 ConstantPoolUnavailableScope constant_pool_unavailable(this);
1504 UseScratchRegisterScope temps(this);
1505 Register scratch = temps.Acquire();
1506
1507 // Optionally restore all double registers.
1508 if (save_doubles) {
1509 // Calculate the stack location of the saved doubles and restore them.
1510 const int offset = ExitFrameConstants::kFixedFrameSizeFromFp;
1511 sub(r3, fp, Operand(offset + DwVfpRegister::kNumRegisters * kDoubleSize));
1512 RestoreFPRegs(r3, scratch);
1513 }
1514
1515 // Clear top frame.
1516 mov(r3, Operand::Zero());
1517 Move(scratch, ExternalReference::Create(IsolateAddressId::kCEntryFPAddress,
1518 isolate()));
1519 str(r3, MemOperand(scratch));
1520
1521 // Restore current context from top and clear it in debug mode.
1522 Move(scratch,
1523 ExternalReference::Create(IsolateAddressId::kContextAddress, isolate()));
1524 ldr(cp, MemOperand(scratch));
1525 #ifdef DEBUG
1526 mov(r3, Operand(Context::kInvalidContext));
1527 Move(scratch,
1528 ExternalReference::Create(IsolateAddressId::kContextAddress, isolate()));
1529 str(r3, MemOperand(scratch));
1530 #endif
1531
1532 // Tear down the exit frame, pop the arguments, and return.
1533 mov(sp, Operand(fp));
1534 ldm(ia_w, sp, {fp, lr});
1535 if (argument_count.is_valid()) {
1536 if (argument_count_is_length) {
1537 add(sp, sp, argument_count);
1538 } else {
1539 add(sp, sp, Operand(argument_count, LSL, kPointerSizeLog2));
1540 }
1541 }
1542 }
1543
1544 void TurboAssembler::MovFromFloatResult(const DwVfpRegister dst) {
1545 if (use_eabi_hardfloat()) {
1546 Move(dst, d0);
1547 } else {
1548 vmov(dst, r0, r1);
1549 }
1550 }
1551
1552 // On ARM this is just a synonym to make the purpose clear.
1553 void TurboAssembler::MovFromFloatParameter(DwVfpRegister dst) {
1554 MovFromFloatResult(dst);
1555 }
1556
1557 void MacroAssembler::LoadStackLimit(Register destination, StackLimitKind kind) {
1558 ASM_CODE_COMMENT(this);
1559 DCHECK(root_array_available());
1560 Isolate* isolate = this->isolate();
1561 ExternalReference limit =
1562 kind == StackLimitKind::kRealStackLimit
1563 ? ExternalReference::address_of_real_jslimit(isolate)
1564 : ExternalReference::address_of_jslimit(isolate);
1565 DCHECK(TurboAssembler::IsAddressableThroughRootRegister(isolate, limit));
1566
1567 intptr_t offset =
1568 TurboAssembler::RootRegisterOffsetForExternalReference(isolate, limit);
1569 CHECK(is_int32(offset));
1570 ldr(destination, MemOperand(kRootRegister, offset));
1571 }
1572
1573 void MacroAssembler::StackOverflowCheck(Register num_args, Register scratch,
1574 Label* stack_overflow) {
1575 ASM_CODE_COMMENT(this);
1576 // Check the stack for overflow. We are not trying to catch
1577 // interruptions (e.g. debug break and preemption) here, so the "real stack
1578 // limit" is checked.
1579 LoadStackLimit(scratch, StackLimitKind::kRealStackLimit);
1580 // Make scratch the space we have left. The stack might already be overflowed
1581 // here which will cause scratch to become negative.
1582 sub(scratch, sp, scratch);
1583 // Check if the arguments will overflow the stack.
1584 cmp(scratch, Operand(num_args, LSL, kPointerSizeLog2));
1585 b(le, stack_overflow); // Signed comparison.
1586 }
1587
1588 void MacroAssembler::InvokePrologue(Register expected_parameter_count,
1589 Register actual_parameter_count,
1590 Label* done, InvokeType type) {
1591 ASM_CODE_COMMENT(this);
1592 Label regular_invoke;
1593 // r0: actual arguments count
1594 // r1: function (passed through to callee)
1595 // r2: expected arguments count
1596 DCHECK_EQ(actual_parameter_count, r0);
1597 DCHECK_EQ(expected_parameter_count, r2);
1598
1599 // If the expected parameter count is equal to the adaptor sentinel, no need
1600 // to push undefined value as arguments.
1601 if (kDontAdaptArgumentsSentinel != 0) {
1602 cmp(expected_parameter_count, Operand(kDontAdaptArgumentsSentinel));
1603 b(eq, ®ular_invoke);
1604 }
1605
1606 // If overapplication or if the actual argument count is equal to the
1607 // formal parameter count, no need to push extra undefined values.
1608 sub(expected_parameter_count, expected_parameter_count,
1609 actual_parameter_count, SetCC);
1610 b(le, ®ular_invoke);
1611
1612 Label stack_overflow;
1613 Register scratch = r4;
1614 StackOverflowCheck(expected_parameter_count, scratch, &stack_overflow);
1615
1616 // Underapplication. Move the arguments already in the stack, including the
1617 // receiver and the return address.
1618 {
1619 Label copy, check;
1620 Register num = r5, src = r6, dest = r9; // r7 and r8 are context and root.
1621 mov(src, sp);
1622 // Update stack pointer.
1623 lsl(scratch, expected_parameter_count, Operand(kSystemPointerSizeLog2));
1624 AllocateStackSpace(scratch);
1625 mov(dest, sp);
1626 mov(num, actual_parameter_count);
1627 b(&check);
1628 bind(©);
1629 ldr(scratch, MemOperand(src, kSystemPointerSize, PostIndex));
1630 str(scratch, MemOperand(dest, kSystemPointerSize, PostIndex));
1631 sub(num, num, Operand(1), SetCC);
1632 bind(&check);
1633 b(gt, ©);
1634 }
1635
1636 // Fill remaining expected arguments with undefined values.
1637 LoadRoot(scratch, RootIndex::kUndefinedValue);
1638 {
1639 Label loop;
1640 bind(&loop);
1641 str(scratch, MemOperand(r9, kSystemPointerSize, PostIndex));
1642 sub(expected_parameter_count, expected_parameter_count, Operand(1), SetCC);
1643 b(gt, &loop);
1644 }
1645 b(®ular_invoke);
1646
1647 bind(&stack_overflow);
1648 {
1649 FrameScope frame(
1650 this, has_frame() ? StackFrame::NO_FRAME_TYPE : StackFrame::INTERNAL);
1651 CallRuntime(Runtime::kThrowStackOverflow);
1652 bkpt(0);
1653 }
1654
1655 bind(®ular_invoke);
1656 }
1657
1658 void MacroAssembler::CallDebugOnFunctionCall(Register fun, Register new_target,
1659 Register expected_parameter_count,
1660 Register actual_parameter_count) {
1661 ASM_CODE_COMMENT(this);
1662 // Load receiver to pass it later to DebugOnFunctionCall hook.
1663 ldr(r4, ReceiverOperand(actual_parameter_count));
1664 FrameScope frame(
1665 this, has_frame() ? StackFrame::NO_FRAME_TYPE : StackFrame::INTERNAL);
1666
1667 SmiTag(expected_parameter_count);
1668 Push(expected_parameter_count);
1669
1670 SmiTag(actual_parameter_count);
1671 Push(actual_parameter_count);
1672
1673 if (new_target.is_valid()) {
1674 Push(new_target);
1675 }
1676 Push(fun);
1677 Push(fun);
1678 Push(r4);
1679 CallRuntime(Runtime::kDebugOnFunctionCall);
1680 Pop(fun);
1681 if (new_target.is_valid()) {
1682 Pop(new_target);
1683 }
1684
1685 Pop(actual_parameter_count);
1686 SmiUntag(actual_parameter_count);
1687
1688 Pop(expected_parameter_count);
1689 SmiUntag(expected_parameter_count);
1690 }
1691
1692 void MacroAssembler::InvokeFunctionCode(Register function, Register new_target,
1693 Register expected_parameter_count,
1694 Register actual_parameter_count,
1695 InvokeType type) {
1696 ASM_CODE_COMMENT(this);
1697 // You can't call a function without a valid frame.
1698 DCHECK_IMPLIES(type == InvokeType::kCall, has_frame());
1699 DCHECK_EQ(function, r1);
1700 DCHECK_IMPLIES(new_target.is_valid(), new_target == r3);
1701
1702 // On function call, call into the debugger if necessary.
1703 Label debug_hook, continue_after_hook;
1704 {
1705 ExternalReference debug_hook_active =
1706 ExternalReference::debug_hook_on_function_call_address(isolate());
1707 Move(r4, debug_hook_active);
1708 ldrsb(r4, MemOperand(r4));
1709 cmp(r4, Operand(0));
1710 b(ne, &debug_hook);
1711 }
1712 bind(&continue_after_hook);
1713
1714 // Clear the new.target register if not given.
1715 if (!new_target.is_valid()) {
1716 LoadRoot(r3, RootIndex::kUndefinedValue);
1717 }
1718
1719 Label done;
1720 InvokePrologue(expected_parameter_count, actual_parameter_count, &done, type);
1721 // We call indirectly through the code field in the function to
1722 // allow recompilation to take effect without changing any of the
1723 // call sites.
1724 Register code = kJavaScriptCallCodeStartRegister;
1725 ldr(code, FieldMemOperand(function, JSFunction::kCodeOffset));
1726 switch (type) {
1727 case InvokeType::kCall:
1728 CallCodeObject(code);
1729 break;
1730 case InvokeType::kJump:
1731 JumpCodeObject(code);
1732 break;
1733 }
1734 b(&done);
1735
1736 // Deferred debug hook.
1737 bind(&debug_hook);
1738 CallDebugOnFunctionCall(function, new_target, expected_parameter_count,
1739 actual_parameter_count);
1740 b(&continue_after_hook);
1741
1742 // Continue here if InvokePrologue does handle the invocation due to
1743 // mismatched parameter counts.
1744 bind(&done);
1745 }
1746
1747 void MacroAssembler::InvokeFunctionWithNewTarget(
1748 Register fun, Register new_target, Register actual_parameter_count,
1749 InvokeType type) {
1750 ASM_CODE_COMMENT(this);
1751 // You can't call a function without a valid frame.
1752 DCHECK_IMPLIES(type == InvokeType::kCall, has_frame());
1753
1754 // Contract with called JS functions requires that function is passed in r1.
1755 DCHECK_EQ(fun, r1);
1756
1757 Register expected_reg = r2;
1758 Register temp_reg = r4;
1759
1760 ldr(temp_reg, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset));
1761 ldr(cp, FieldMemOperand(r1, JSFunction::kContextOffset));
1762 ldrh(expected_reg,
1763 FieldMemOperand(temp_reg,
1764 SharedFunctionInfo::kFormalParameterCountOffset));
1765
1766 InvokeFunctionCode(fun, new_target, expected_reg, actual_parameter_count,
1767 type);
1768 }
1769
1770 void MacroAssembler::InvokeFunction(Register function,
1771 Register expected_parameter_count,
1772 Register actual_parameter_count,
1773 InvokeType type) {
1774 ASM_CODE_COMMENT(this);
1775 // You can't call a function without a valid frame.
1776 DCHECK_IMPLIES(type == InvokeType::kCall, has_frame());
1777
1778 // Contract with called JS functions requires that function is passed in r1.
1779 DCHECK_EQ(function, r1);
1780
1781 // Get the function and setup the context.
1782 ldr(cp, FieldMemOperand(r1, JSFunction::kContextOffset));
1783
1784 InvokeFunctionCode(r1, no_reg, expected_parameter_count,
1785 actual_parameter_count, type);
1786 }
1787
1788 void MacroAssembler::PushStackHandler() {
1789 ASM_CODE_COMMENT(this);
1790 // Adjust this code if not the case.
1791 STATIC_ASSERT(StackHandlerConstants::kSize == 2 * kPointerSize);
1792 STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0 * kPointerSize);
1793
1794 Push(Smi::zero()); // Padding.
1795 // Link the current handler as the next handler.
1796 Move(r6,
1797 ExternalReference::Create(IsolateAddressId::kHandlerAddress, isolate()));
1798 ldr(r5, MemOperand(r6));
1799 push(r5);
1800 // Set this new handler as the current one.
1801 str(sp, MemOperand(r6));
1802 }
1803
1804 void MacroAssembler::PopStackHandler() {
1805 ASM_CODE_COMMENT(this);
1806 UseScratchRegisterScope temps(this);
1807 Register scratch = temps.Acquire();
1808 STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0);
1809 pop(r1);
1810 Move(scratch,
1811 ExternalReference::Create(IsolateAddressId::kHandlerAddress, isolate()));
1812 str(r1, MemOperand(scratch));
1813 add(sp, sp, Operand(StackHandlerConstants::kSize - kPointerSize));
1814 }
1815
1816 void MacroAssembler::CompareObjectType(Register object, Register map,
1817 Register type_reg, InstanceType type) {
1818 ASM_CODE_COMMENT(this);
1819 UseScratchRegisterScope temps(this);
1820 const Register temp = type_reg == no_reg ? temps.Acquire() : type_reg;
1821
1822 LoadMap(map, object);
1823 CompareInstanceType(map, temp, type);
1824 }
1825
1826 void MacroAssembler::CompareInstanceType(Register map, Register type_reg,
1827 InstanceType type) {
1828 ldrh(type_reg, FieldMemOperand(map, Map::kInstanceTypeOffset));
1829 cmp(type_reg, Operand(type));
1830 }
1831
1832 void MacroAssembler::CompareRange(Register value, unsigned lower_limit,
1833 unsigned higher_limit) {
1834 ASM_CODE_COMMENT(this);
1835 DCHECK_LT(lower_limit, higher_limit);
1836 if (lower_limit != 0) {
1837 UseScratchRegisterScope temps(this);
1838 Register scratch = temps.Acquire();
1839 sub(scratch, value, Operand(lower_limit));
1840 cmp(scratch, Operand(higher_limit - lower_limit));
1841 } else {
1842 cmp(value, Operand(higher_limit));
1843 }
1844 }
1845 void MacroAssembler::CompareInstanceTypeRange(Register map, Register type_reg,
1846 InstanceType lower_limit,
1847 InstanceType higher_limit) {
1848 ASM_CODE_COMMENT(this);
1849 DCHECK_LT(lower_limit, higher_limit);
1850 ldrh(type_reg, FieldMemOperand(map, Map::kInstanceTypeOffset));
1851 CompareRange(type_reg, lower_limit, higher_limit);
1852 }
1853
1854 void MacroAssembler::CompareRoot(Register obj, RootIndex index) {
1855 UseScratchRegisterScope temps(this);
1856 Register scratch = temps.Acquire();
1857 DCHECK(obj != scratch);
1858 LoadRoot(scratch, index);
1859 cmp(obj, scratch);
1860 }
1861
1862 void MacroAssembler::JumpIfIsInRange(Register value, unsigned lower_limit,
1863 unsigned higher_limit,
1864 Label* on_in_range) {
1865 ASM_CODE_COMMENT(this);
1866 CompareRange(value, lower_limit, higher_limit);
1867 b(ls, on_in_range);
1868 }
1869
1870 void TurboAssembler::TryInlineTruncateDoubleToI(Register result,
1871 DwVfpRegister double_input,
1872 Label* done) {
1873 ASM_CODE_COMMENT(this);
1874 UseScratchRegisterScope temps(this);
1875 SwVfpRegister single_scratch = SwVfpRegister::no_reg();
1876 if (temps.CanAcquireVfp<SwVfpRegister>()) {
1877 single_scratch = temps.AcquireS();
1878 } else {
1879 // Re-use the input as a scratch register. However, we can only do this if
1880 // the input register is d0-d15 as there are no s32+ registers.
1881 DCHECK_LT(double_input.code(), LowDwVfpRegister::kNumRegisters);
1882 LowDwVfpRegister double_scratch =
1883 LowDwVfpRegister::from_code(double_input.code());
1884 single_scratch = double_scratch.low();
1885 }
1886 vcvt_s32_f64(single_scratch, double_input);
1887 vmov(result, single_scratch);
1888
1889 Register scratch = temps.Acquire();
1890 // If result is not saturated (0x7FFFFFFF or 0x80000000), we are done.
1891 sub(scratch, result, Operand(1));
1892 cmp(scratch, Operand(0x7FFFFFFE));
1893 b(lt, done);
1894 }
1895
1896 void TurboAssembler::TruncateDoubleToI(Isolate* isolate, Zone* zone,
1897 Register result,
1898 DwVfpRegister double_input,
1899 StubCallMode stub_mode) {
1900 ASM_CODE_COMMENT(this);
1901 Label done;
1902
1903 TryInlineTruncateDoubleToI(result, double_input, &done);
1904
1905 // If we fell through then inline version didn't succeed - call stub instead.
1906 push(lr);
1907 AllocateStackSpace(kDoubleSize); // Put input on stack.
1908 vstr(double_input, MemOperand(sp, 0));
1909
1910 #if V8_ENABLE_WEBASSEMBLY
1911 if (stub_mode == StubCallMode::kCallWasmRuntimeStub) {
1912 Call(wasm::WasmCode::kDoubleToI, RelocInfo::WASM_STUB_CALL);
1913 #else
1914 // For balance.
1915 if (false) {
1916 #endif // V8_ENABLE_WEBASSEMBLY
1917 } else if (options().inline_offheap_trampolines) {
1918 CallBuiltin(Builtin::kDoubleToI);
1919 } else {
1920 Call(BUILTIN_CODE(isolate, DoubleToI), RelocInfo::CODE_TARGET);
1921 }
1922 ldr(result, MemOperand(sp, 0));
1923
1924 add(sp, sp, Operand(kDoubleSize));
1925 pop(lr);
1926
1927 bind(&done);
1928 }
1929
1930 void MacroAssembler::CallRuntime(const Runtime::Function* f, int num_arguments,
1931 SaveFPRegsMode save_doubles) {
1932 ASM_CODE_COMMENT(this);
1933 // All parameters are on the stack. r0 has the return value after call.
1934
1935 // If the expected number of arguments of the runtime function is
1936 // constant, we check that the actual number of arguments match the
1937 // expectation.
1938 CHECK(f->nargs < 0 || f->nargs == num_arguments);
1939
1940 // TODO(1236192): Most runtime routines don't need the number of
1941 // arguments passed in because it is constant. At some point we
1942 // should remove this need and make the runtime routine entry code
1943 // smarter.
1944 mov(r0, Operand(num_arguments));
1945 Move(r1, ExternalReference::Create(f));
1946 Handle<Code> code =
1947 CodeFactory::CEntry(isolate(), f->result_size, save_doubles);
1948 Call(code, RelocInfo::CODE_TARGET);
1949 }
1950
1951 void MacroAssembler::TailCallRuntime(Runtime::FunctionId fid) {
1952 ASM_CODE_COMMENT(this);
1953 const Runtime::Function* function = Runtime::FunctionForId(fid);
1954 DCHECK_EQ(1, function->result_size);
1955 if (function->nargs >= 0) {
1956 // TODO(1236192): Most runtime routines don't need the number of
1957 // arguments passed in because it is constant. At some point we
1958 // should remove this need and make the runtime routine entry code
1959 // smarter.
1960 mov(r0, Operand(function->nargs));
1961 }
1962 JumpToExternalReference(ExternalReference::Create(fid));
1963 }
1964
1965 void MacroAssembler::JumpToExternalReference(const ExternalReference& builtin,
1966 bool builtin_exit_frame) {
1967 #if defined(__thumb__)
1968 // Thumb mode builtin.
1969 DCHECK_EQ(builtin.address() & 1, 1);
1970 #endif
1971 Move(r1, builtin);
1972 Handle<Code> code = CodeFactory::CEntry(isolate(), 1, SaveFPRegsMode::kIgnore,
1973 ArgvMode::kStack, builtin_exit_frame);
1974 Jump(code, RelocInfo::CODE_TARGET);
1975 }
1976
1977 void MacroAssembler::JumpToOffHeapInstructionStream(Address entry) {
1978 mov(kOffHeapTrampolineRegister, Operand(entry, RelocInfo::OFF_HEAP_TARGET));
1979 Jump(kOffHeapTrampolineRegister);
1980 }
1981
1982 void MacroAssembler::LoadWeakValue(Register out, Register in,
1983 Label* target_if_cleared) {
1984 cmp(in, Operand(kClearedWeakHeapObjectLower32));
1985 b(eq, target_if_cleared);
1986
1987 and_(out, in, Operand(~kWeakHeapObjectMask));
1988 }
1989
1990 void MacroAssembler::EmitIncrementCounter(StatsCounter* counter, int value,
1991 Register scratch1,
1992 Register scratch2) {
1993 DCHECK_GT(value, 0);
1994 if (FLAG_native_code_counters && counter->Enabled()) {
1995 ASM_CODE_COMMENT(this);
1996 Move(scratch2, ExternalReference::Create(counter));
1997 ldr(scratch1, MemOperand(scratch2));
1998 add(scratch1, scratch1, Operand(value));
1999 str(scratch1, MemOperand(scratch2));
2000 }
2001 }
2002
2003 void MacroAssembler::EmitDecrementCounter(StatsCounter* counter, int value,
2004 Register scratch1,
2005 Register scratch2) {
2006 DCHECK_GT(value, 0);
2007 if (FLAG_native_code_counters && counter->Enabled()) {
2008 ASM_CODE_COMMENT(this);
2009 Move(scratch2, ExternalReference::Create(counter));
2010 ldr(scratch1, MemOperand(scratch2));
2011 sub(scratch1, scratch1, Operand(value));
2012 str(scratch1, MemOperand(scratch2));
2013 }
2014 }
2015
2016 void TurboAssembler::Assert(Condition cond, AbortReason reason) {
2017 if (FLAG_debug_code) Check(cond, reason);
2018 }
2019
2020 void TurboAssembler::AssertUnreachable(AbortReason reason) {
2021 if (FLAG_debug_code) Abort(reason);
2022 }
2023
2024 void TurboAssembler::Check(Condition cond, AbortReason reason) {
2025 Label L;
2026 b(cond, &L);
2027 Abort(reason);
2028 // will not return here
2029 bind(&L);
2030 }
2031
2032 void TurboAssembler::Abort(AbortReason reason) {
2033 ASM_CODE_COMMENT(this);
2034 Label abort_start;
2035 bind(&abort_start);
2036 if (FLAG_code_comments) {
2037 const char* msg = GetAbortReason(reason);
2038 RecordComment("Abort message: ");
2039 RecordComment(msg);
2040 }
2041
2042 // Avoid emitting call to builtin if requested.
2043 if (trap_on_abort()) {
2044 stop();
2045 return;
2046 }
2047
2048 if (should_abort_hard()) {
2049 // We don't care if we constructed a frame. Just pretend we did.
2050 FrameScope assume_frame(this, StackFrame::NO_FRAME_TYPE);
2051 Move32BitImmediate(r0, Operand(static_cast<int>(reason)));
2052 PrepareCallCFunction(1, 0, r1);
2053 Move(r1, ExternalReference::abort_with_reason());
2054 // Use Call directly to avoid any unneeded overhead. The function won't
2055 // return anyway.
2056 Call(r1);
2057 return;
2058 }
2059
2060 Move(r1, Smi::FromInt(static_cast<int>(reason)));
2061
2062 // Disable stub call restrictions to always allow calls to abort.
2063 if (!has_frame()) {
2064 // We don't actually want to generate a pile of code for this, so just
2065 // claim there is a stack frame, without generating one.
2066 FrameScope scope(this, StackFrame::NO_FRAME_TYPE);
2067 Call(BUILTIN_CODE(isolate(), Abort), RelocInfo::CODE_TARGET);
2068 } else {
2069 Call(BUILTIN_CODE(isolate(), Abort), RelocInfo::CODE_TARGET);
2070 }
2071 // will not return here
2072 }
2073
2074 void TurboAssembler::LoadMap(Register destination, Register object) {
2075 ldr(destination, FieldMemOperand(object, HeapObject::kMapOffset));
2076 }
2077
2078 void MacroAssembler::LoadGlobalProxy(Register dst) {
2079 ASM_CODE_COMMENT(this);
2080 LoadNativeContextSlot(dst, Context::GLOBAL_PROXY_INDEX);
2081 }
2082
2083 void MacroAssembler::LoadNativeContextSlot(Register dst, int index) {
2084 ASM_CODE_COMMENT(this);
2085 LoadMap(dst, cp);
2086 ldr(dst, FieldMemOperand(
2087 dst, Map::kConstructorOrBackPointerOrNativeContextOffset));
2088 ldr(dst, MemOperand(dst, Context::SlotOffset(index)));
2089 }
2090
2091 void TurboAssembler::InitializeRootRegister() {
2092 ASM_CODE_COMMENT(this);
2093 ExternalReference isolate_root = ExternalReference::isolate_root(isolate());
2094 mov(kRootRegister, Operand(isolate_root));
2095 }
2096
2097 void MacroAssembler::SmiTag(Register reg, SBit s) {
2098 add(reg, reg, Operand(reg), s);
2099 }
2100
2101 void MacroAssembler::SmiTag(Register dst, Register src, SBit s) {
2102 add(dst, src, Operand(src), s);
2103 }
2104
2105 void MacroAssembler::SmiTst(Register value) {
2106 tst(value, Operand(kSmiTagMask));
2107 }
2108
2109 void TurboAssembler::JumpIfSmi(Register value, Label* smi_label) {
2110 tst(value, Operand(kSmiTagMask));
2111 b(eq, smi_label);
2112 }
2113
2114 void TurboAssembler::JumpIfEqual(Register x, int32_t y, Label* dest) {
2115 cmp(x, Operand(y));
2116 b(eq, dest);
2117 }
2118
2119 void TurboAssembler::JumpIfLessThan(Register x, int32_t y, Label* dest) {
2120 cmp(x, Operand(y));
2121 b(lt, dest);
2122 }
2123
2124 void MacroAssembler::JumpIfNotSmi(Register value, Label* not_smi_label) {
2125 tst(value, Operand(kSmiTagMask));
2126 b(ne, not_smi_label);
2127 }
2128
2129 void MacroAssembler::AssertNotSmi(Register object) {
2130 if (!FLAG_debug_code) return;
2131 ASM_CODE_COMMENT(this);
2132 STATIC_ASSERT(kSmiTag == 0);
2133 tst(object, Operand(kSmiTagMask));
2134 Check(ne, AbortReason::kOperandIsASmi);
2135 }
2136
2137 void MacroAssembler::AssertSmi(Register object) {
2138 if (!FLAG_debug_code) return;
2139 ASM_CODE_COMMENT(this);
2140 STATIC_ASSERT(kSmiTag == 0);
2141 tst(object, Operand(kSmiTagMask));
2142 Check(eq, AbortReason::kOperandIsNotASmi);
2143 }
2144
2145 void MacroAssembler::AssertConstructor(Register object) {
2146 if (!FLAG_debug_code) return;
2147 ASM_CODE_COMMENT(this);
2148 STATIC_ASSERT(kSmiTag == 0);
2149 tst(object, Operand(kSmiTagMask));
2150 Check(ne, AbortReason::kOperandIsASmiAndNotAConstructor);
2151 push(object);
2152 LoadMap(object, object);
2153 ldrb(object, FieldMemOperand(object, Map::kBitFieldOffset));
2154 tst(object, Operand(Map::Bits1::IsConstructorBit::kMask));
2155 pop(object);
2156 Check(ne, AbortReason::kOperandIsNotAConstructor);
2157 }
2158
2159 void MacroAssembler::AssertFunction(Register object) {
2160 if (!FLAG_debug_code) return;
2161 ASM_CODE_COMMENT(this);
2162 STATIC_ASSERT(kSmiTag == 0);
2163 tst(object, Operand(kSmiTagMask));
2164 Check(ne, AbortReason::kOperandIsASmiAndNotAFunction);
2165 push(object);
2166 LoadMap(object, object);
2167 CompareInstanceTypeRange(object, object, FIRST_JS_FUNCTION_TYPE,
2168 LAST_JS_FUNCTION_TYPE);
2169 pop(object);
2170 Check(ls, AbortReason::kOperandIsNotAFunction);
2171 }
2172
2173 void MacroAssembler::AssertCallableFunction(Register object) {
2174 if (!FLAG_debug_code) return;
2175 ASM_CODE_COMMENT(this);
2176 STATIC_ASSERT(kSmiTag == 0);
2177 tst(object, Operand(kSmiTagMask));
2178 Check(ne, AbortReason::kOperandIsASmiAndNotAFunction);
2179 push(object);
2180 LoadMap(object, object);
2181 CompareInstanceTypeRange(object, object, FIRST_CALLABLE_JS_FUNCTION_TYPE,
2182 LAST_CALLABLE_JS_FUNCTION_TYPE);
2183 pop(object);
2184 Check(ls, AbortReason::kOperandIsNotACallableFunction);
2185 }
2186
2187 void MacroAssembler::AssertBoundFunction(Register object) {
2188 if (!FLAG_debug_code) return;
2189 ASM_CODE_COMMENT(this);
2190 STATIC_ASSERT(kSmiTag == 0);
2191 tst(object, Operand(kSmiTagMask));
2192 Check(ne, AbortReason::kOperandIsASmiAndNotABoundFunction);
2193 push(object);
2194 CompareObjectType(object, object, object, JS_BOUND_FUNCTION_TYPE);
2195 pop(object);
2196 Check(eq, AbortReason::kOperandIsNotABoundFunction);
2197 }
2198
2199 void MacroAssembler::AssertGeneratorObject(Register object) {
2200 if (!FLAG_debug_code) return;
2201 ASM_CODE_COMMENT(this);
2202 tst(object, Operand(kSmiTagMask));
2203 Check(ne, AbortReason::kOperandIsASmiAndNotAGeneratorObject);
2204
2205 // Load map
2206 Register map = object;
2207 push(object);
2208 LoadMap(map, object);
2209
2210 // Check if JSGeneratorObject
2211 Label do_check;
2212 Register instance_type = object;
2213 CompareInstanceType(map, instance_type, JS_GENERATOR_OBJECT_TYPE);
2214 b(eq, &do_check);
2215
2216 // Check if JSAsyncFunctionObject (See MacroAssembler::CompareInstanceType)
2217 cmp(instance_type, Operand(JS_ASYNC_FUNCTION_OBJECT_TYPE));
2218 b(eq, &do_check);
2219
2220 // Check if JSAsyncGeneratorObject (See MacroAssembler::CompareInstanceType)
2221 cmp(instance_type, Operand(JS_ASYNC_GENERATOR_OBJECT_TYPE));
2222
2223 bind(&do_check);
2224 // Restore generator object to register and perform assertion
2225 pop(object);
2226 Check(eq, AbortReason::kOperandIsNotAGeneratorObject);
2227 }
2228
2229 void MacroAssembler::AssertUndefinedOrAllocationSite(Register object,
2230 Register scratch) {
2231 if (!FLAG_debug_code) return;
2232 ASM_CODE_COMMENT(this);
2233 Label done_checking;
2234 AssertNotSmi(object);
2235 CompareRoot(object, RootIndex::kUndefinedValue);
2236 b(eq, &done_checking);
2237 LoadMap(scratch, object);
2238 CompareInstanceType(scratch, scratch, ALLOCATION_SITE_TYPE);
2239 Assert(eq, AbortReason::kExpectedUndefinedOrCell);
2240 bind(&done_checking);
2241 }
2242
2243 void TurboAssembler::CheckFor32DRegs(Register scratch) {
2244 ASM_CODE_COMMENT(this);
2245 Move(scratch, ExternalReference::cpu_features());
2246 ldr(scratch, MemOperand(scratch));
2247 tst(scratch, Operand(1u << VFP32DREGS));
2248 }
2249
2250 void TurboAssembler::SaveFPRegs(Register location, Register scratch) {
2251 ASM_CODE_COMMENT(this);
2252 CpuFeatureScope scope(this, VFP32DREGS, CpuFeatureScope::kDontCheckSupported);
2253 CheckFor32DRegs(scratch);
2254 vstm(db_w, location, d16, d31, ne);
2255 sub(location, location, Operand(16 * kDoubleSize), LeaveCC, eq);
2256 vstm(db_w, location, d0, d15);
2257 }
2258
2259 void TurboAssembler::RestoreFPRegs(Register location, Register scratch) {
2260 ASM_CODE_COMMENT(this);
2261 CpuFeatureScope scope(this, VFP32DREGS, CpuFeatureScope::kDontCheckSupported);
2262 CheckFor32DRegs(scratch);
2263 vldm(ia_w, location, d0, d15);
2264 vldm(ia_w, location, d16, d31, ne);
2265 add(location, location, Operand(16 * kDoubleSize), LeaveCC, eq);
2266 }
2267
2268 void TurboAssembler::SaveFPRegsToHeap(Register location, Register scratch) {
2269 ASM_CODE_COMMENT(this);
2270 CpuFeatureScope scope(this, VFP32DREGS, CpuFeatureScope::kDontCheckSupported);
2271 CheckFor32DRegs(scratch);
2272 vstm(ia_w, location, d0, d15);
2273 vstm(ia_w, location, d16, d31, ne);
2274 add(location, location, Operand(16 * kDoubleSize), LeaveCC, eq);
2275 }
2276
2277 void TurboAssembler::RestoreFPRegsFromHeap(Register location,
2278 Register scratch) {
2279 ASM_CODE_COMMENT(this);
2280 CpuFeatureScope scope(this, VFP32DREGS, CpuFeatureScope::kDontCheckSupported);
2281 CheckFor32DRegs(scratch);
2282 vldm(ia_w, location, d0, d15);
2283 vldm(ia_w, location, d16, d31, ne);
2284 add(location, location, Operand(16 * kDoubleSize), LeaveCC, eq);
2285 }
2286
2287 template <typename T>
2288 void TurboAssembler::FloatMaxHelper(T result, T left, T right,
2289 Label* out_of_line) {
2290 // This trivial case is caught sooner, so that the out-of-line code can be
2291 // completely avoided.
2292 DCHECK(left != right);
2293
2294 if (CpuFeatures::IsSupported(ARMv8)) {
2295 CpuFeatureScope scope(this, ARMv8);
2296 VFPCompareAndSetFlags(left, right);
2297 b(vs, out_of_line);
2298 vmaxnm(result, left, right);
2299 } else {
2300 Label done;
2301 VFPCompareAndSetFlags(left, right);
2302 b(vs, out_of_line);
2303 // Avoid a conditional instruction if the result register is unique.
2304 bool aliased_result_reg = result == left || result == right;
2305 Move(result, right, aliased_result_reg ? mi : al);
2306 Move(result, left, gt);
2307 b(ne, &done);
2308 // Left and right are equal, but check for +/-0.
2309 VFPCompareAndSetFlags(left, 0.0);
2310 b(eq, out_of_line);
2311 // The arguments are equal and not zero, so it doesn't matter which input we
2312 // pick. We have already moved one input into the result (if it didn't
2313 // already alias) so there's nothing more to do.
2314 bind(&done);
2315 }
2316 }
2317
2318 template <typename T>
2319 void TurboAssembler::FloatMaxOutOfLineHelper(T result, T left, T right) {
2320 DCHECK(left != right);
2321
2322 // ARMv8: At least one of left and right is a NaN.
2323 // Anything else: At least one of left and right is a NaN, or both left and
2324 // right are zeroes with unknown sign.
2325
2326 // If left and right are +/-0, select the one with the most positive sign.
2327 // If left or right are NaN, vadd propagates the appropriate one.
2328 vadd(result, left, right);
2329 }
2330
2331 template <typename T>
2332 void TurboAssembler::FloatMinHelper(T result, T left, T right,
2333 Label* out_of_line) {
2334 // This trivial case is caught sooner, so that the out-of-line code can be
2335 // completely avoided.
2336 DCHECK(left != right);
2337
2338 if (CpuFeatures::IsSupported(ARMv8)) {
2339 CpuFeatureScope scope(this, ARMv8);
2340 VFPCompareAndSetFlags(left, right);
2341 b(vs, out_of_line);
2342 vminnm(result, left, right);
2343 } else {
2344 Label done;
2345 VFPCompareAndSetFlags(left, right);
2346 b(vs, out_of_line);
2347 // Avoid a conditional instruction if the result register is unique.
2348 bool aliased_result_reg = result == left || result == right;
2349 Move(result, left, aliased_result_reg ? mi : al);
2350 Move(result, right, gt);
2351 b(ne, &done);
2352 // Left and right are equal, but check for +/-0.
2353 VFPCompareAndSetFlags(left, 0.0);
2354 // If the arguments are equal and not zero, it doesn't matter which input we
2355 // pick. We have already moved one input into the result (if it didn't
2356 // already alias) so there's nothing more to do.
2357 b(ne, &done);
2358 // At this point, both left and right are either 0 or -0.
2359 // We could use a single 'vorr' instruction here if we had NEON support.
2360 // The algorithm used is -((-L) + (-R)), which is most efficiently expressed
2361 // as -((-L) - R).
2362 if (left == result) {
2363 DCHECK(right != result);
2364 vneg(result, left);
2365 vsub(result, result, right);
2366 vneg(result, result);
2367 } else {
2368 DCHECK(left != result);
2369 vneg(result, right);
2370 vsub(result, result, left);
2371 vneg(result, result);
2372 }
2373 bind(&done);
2374 }
2375 }
2376
2377 template <typename T>
2378 void TurboAssembler::FloatMinOutOfLineHelper(T result, T left, T right) {
2379 DCHECK(left != right);
2380
2381 // At least one of left and right is a NaN. Use vadd to propagate the NaN
2382 // appropriately. +/-0 is handled inline.
2383 vadd(result, left, right);
2384 }
2385
2386 void TurboAssembler::FloatMax(SwVfpRegister result, SwVfpRegister left,
2387 SwVfpRegister right, Label* out_of_line) {
2388 FloatMaxHelper(result, left, right, out_of_line);
2389 }
2390
2391 void TurboAssembler::FloatMin(SwVfpRegister result, SwVfpRegister left,
2392 SwVfpRegister right, Label* out_of_line) {
2393 FloatMinHelper(result, left, right, out_of_line);
2394 }
2395
2396 void TurboAssembler::FloatMax(DwVfpRegister result, DwVfpRegister left,
2397 DwVfpRegister right, Label* out_of_line) {
2398 FloatMaxHelper(result, left, right, out_of_line);
2399 }
2400
2401 void TurboAssembler::FloatMin(DwVfpRegister result, DwVfpRegister left,
2402 DwVfpRegister right, Label* out_of_line) {
2403 FloatMinHelper(result, left, right, out_of_line);
2404 }
2405
2406 void TurboAssembler::FloatMaxOutOfLine(SwVfpRegister result, SwVfpRegister left,
2407 SwVfpRegister right) {
2408 FloatMaxOutOfLineHelper(result, left, right);
2409 }
2410
2411 void TurboAssembler::FloatMinOutOfLine(SwVfpRegister result, SwVfpRegister left,
2412 SwVfpRegister right) {
2413 FloatMinOutOfLineHelper(result, left, right);
2414 }
2415
2416 void TurboAssembler::FloatMaxOutOfLine(DwVfpRegister result, DwVfpRegister left,
2417 DwVfpRegister right) {
2418 FloatMaxOutOfLineHelper(result, left, right);
2419 }
2420
2421 void TurboAssembler::FloatMinOutOfLine(DwVfpRegister result, DwVfpRegister left,
2422 DwVfpRegister right) {
2423 FloatMinOutOfLineHelper(result, left, right);
2424 }
2425
2426 static const int kRegisterPassedArguments = 4;
2427 // The hardfloat calling convention passes double arguments in registers d0-d7.
2428 static const int kDoubleRegisterPassedArguments = 8;
2429
2430 int TurboAssembler::CalculateStackPassedWords(int num_reg_arguments,
2431 int num_double_arguments) {
2432 int stack_passed_words = 0;
2433 if (use_eabi_hardfloat()) {
2434 // In the hard floating point calling convention, we can use the first 8
2435 // registers to pass doubles.
2436 if (num_double_arguments > kDoubleRegisterPassedArguments) {
2437 stack_passed_words +=
2438 2 * (num_double_arguments - kDoubleRegisterPassedArguments);
2439 }
2440 } else {
2441 // In the soft floating point calling convention, every double
2442 // argument is passed using two registers.
2443 num_reg_arguments += 2 * num_double_arguments;
2444 }
2445 // Up to four simple arguments are passed in registers r0..r3.
2446 if (num_reg_arguments > kRegisterPassedArguments) {
2447 stack_passed_words += num_reg_arguments - kRegisterPassedArguments;
2448 }
2449 return stack_passed_words;
2450 }
2451
2452 void TurboAssembler::PrepareCallCFunction(int num_reg_arguments,
2453 int num_double_arguments,
2454 Register scratch) {
2455 ASM_CODE_COMMENT(this);
2456 int frame_alignment = ActivationFrameAlignment();
2457 int stack_passed_arguments =
2458 CalculateStackPassedWords(num_reg_arguments, num_double_arguments);
2459 if (frame_alignment > kPointerSize) {
2460 UseScratchRegisterScope temps(this);
2461 if (!scratch.is_valid()) scratch = temps.Acquire();
2462 // Make stack end at alignment and make room for num_arguments - 4 words
2463 // and the original value of sp.
2464 mov(scratch, sp);
2465 AllocateStackSpace((stack_passed_arguments + 1) * kPointerSize);
2466 DCHECK(base::bits::IsPowerOfTwo(frame_alignment));
2467 and_(sp, sp, Operand(-frame_alignment));
2468 str(scratch, MemOperand(sp, stack_passed_arguments * kPointerSize));
2469 } else if (stack_passed_arguments > 0) {
2470 AllocateStackSpace(stack_passed_arguments * kPointerSize);
2471 }
2472 }
2473
2474 void TurboAssembler::MovToFloatParameter(DwVfpRegister src) {
2475 DCHECK(src == d0);
2476 if (!use_eabi_hardfloat()) {
2477 vmov(r0, r1, src);
2478 }
2479 }
2480
2481 // On ARM this is just a synonym to make the purpose clear.
2482 void TurboAssembler::MovToFloatResult(DwVfpRegister src) {
2483 MovToFloatParameter(src);
2484 }
2485
2486 void TurboAssembler::MovToFloatParameters(DwVfpRegister src1,
2487 DwVfpRegister src2) {
2488 DCHECK(src1 == d0);
2489 DCHECK(src2 == d1);
2490 if (!use_eabi_hardfloat()) {
2491 vmov(r0, r1, src1);
2492 vmov(r2, r3, src2);
2493 }
2494 }
2495
2496 void TurboAssembler::CallCFunction(ExternalReference function,
2497 int num_reg_arguments,
2498 int num_double_arguments) {
2499 UseScratchRegisterScope temps(this);
2500 Register scratch = temps.Acquire();
2501 Move(scratch, function);
2502 CallCFunctionHelper(scratch, num_reg_arguments, num_double_arguments);
2503 }
2504
2505 void TurboAssembler::CallCFunction(Register function, int num_reg_arguments,
2506 int num_double_arguments) {
2507 CallCFunctionHelper(function, num_reg_arguments, num_double_arguments);
2508 }
2509
2510 void TurboAssembler::CallCFunction(ExternalReference function,
2511 int num_arguments) {
2512 CallCFunction(function, num_arguments, 0);
2513 }
2514
2515 void TurboAssembler::CallCFunction(Register function, int num_arguments) {
2516 CallCFunction(function, num_arguments, 0);
2517 }
2518
2519 void TurboAssembler::CallCFunctionHelper(Register function,
2520 int num_reg_arguments,
2521 int num_double_arguments) {
2522 ASM_CODE_COMMENT(this);
2523 DCHECK_LE(num_reg_arguments + num_double_arguments, kMaxCParameters);
2524 DCHECK(has_frame());
2525 // Make sure that the stack is aligned before calling a C function unless
2526 // running in the simulator. The simulator has its own alignment check which
2527 // provides more information.
2528 #if V8_HOST_ARCH_ARM
2529 if (FLAG_debug_code) {
2530 int frame_alignment = base::OS::ActivationFrameAlignment();
2531 int frame_alignment_mask = frame_alignment - 1;
2532 if (frame_alignment > kPointerSize) {
2533 ASM_CODE_COMMENT_STRING(this, "Check stack alignment");
2534 DCHECK(base::bits::IsPowerOfTwo(frame_alignment));
2535 Label alignment_as_expected;
2536 tst(sp, Operand(frame_alignment_mask));
2537 b(eq, &alignment_as_expected);
2538 // Don't use Check here, as it will call Runtime_Abort possibly
2539 // re-entering here.
2540 stop();
2541 bind(&alignment_as_expected);
2542 }
2543 }
2544 #endif
2545
2546 // Save the frame pointer and PC so that the stack layout remains iterable,
2547 // even without an ExitFrame which normally exists between JS and C frames.
2548 Register addr_scratch = r4;
2549 // See x64 code for reasoning about how to address the isolate data fields.
2550 if (root_array_available()) {
2551 str(pc,
2552 MemOperand(kRootRegister, IsolateData::fast_c_call_caller_pc_offset()));
2553 str(fp,
2554 MemOperand(kRootRegister, IsolateData::fast_c_call_caller_fp_offset()));
2555 } else {
2556 DCHECK_NOT_NULL(isolate());
2557 Push(addr_scratch);
2558
2559 Move(addr_scratch,
2560 ExternalReference::fast_c_call_caller_pc_address(isolate()));
2561 str(pc, MemOperand(addr_scratch));
2562 Move(addr_scratch,
2563 ExternalReference::fast_c_call_caller_fp_address(isolate()));
2564 str(fp, MemOperand(addr_scratch));
2565
2566 Pop(addr_scratch);
2567 }
2568
2569 // Just call directly. The function called cannot cause a GC, or
2570 // allow preemption, so the return address in the link register
2571 // stays correct.
2572 Call(function);
2573
2574 // We don't unset the PC; the FP is the source of truth.
2575 Register zero_scratch = r5;
2576 Push(zero_scratch);
2577 mov(zero_scratch, Operand::Zero());
2578
2579 if (root_array_available()) {
2580 str(zero_scratch,
2581 MemOperand(kRootRegister, IsolateData::fast_c_call_caller_fp_offset()));
2582 } else {
2583 DCHECK_NOT_NULL(isolate());
2584 Push(addr_scratch);
2585 Move(addr_scratch,
2586 ExternalReference::fast_c_call_caller_fp_address(isolate()));
2587 str(zero_scratch, MemOperand(addr_scratch));
2588 Pop(addr_scratch);
2589 }
2590
2591 Pop(zero_scratch);
2592
2593 int stack_passed_arguments =
2594 CalculateStackPassedWords(num_reg_arguments, num_double_arguments);
2595 if (ActivationFrameAlignment() > kPointerSize) {
2596 ldr(sp, MemOperand(sp, stack_passed_arguments * kPointerSize));
2597 } else {
2598 add(sp, sp, Operand(stack_passed_arguments * kPointerSize));
2599 }
2600 }
2601
2602 void TurboAssembler::CheckPageFlag(Register object, int mask, Condition cc,
2603 Label* condition_met) {
2604 ASM_CODE_COMMENT(this);
2605 UseScratchRegisterScope temps(this);
2606 Register scratch = temps.Acquire();
2607 DCHECK(!AreAliased(object, scratch));
2608 DCHECK(cc == eq || cc == ne);
2609 Bfc(scratch, object, 0, kPageSizeBits);
2610 ldr(scratch, MemOperand(scratch, BasicMemoryChunk::kFlagsOffset));
2611 tst(scratch, Operand(mask));
2612 b(cc, condition_met);
2613 }
2614
2615 Register GetRegisterThatIsNotOneOf(Register reg1, Register reg2, Register reg3,
2616 Register reg4, Register reg5,
2617 Register reg6) {
2618 RegList regs = {reg1, reg2, reg3, reg4, reg5, reg6};
2619
2620 const RegisterConfiguration* config = RegisterConfiguration::Default();
2621 for (int i = 0; i < config->num_allocatable_general_registers(); ++i) {
2622 int code = config->GetAllocatableGeneralCode(i);
2623 Register candidate = Register::from_code(code);
2624 if (regs.has(candidate)) continue;
2625 return candidate;
2626 }
2627 UNREACHABLE();
2628 }
2629
2630 void TurboAssembler::ComputeCodeStartAddress(Register dst) {
2631 ASM_CODE_COMMENT(this);
2632 // We can use the register pc - 8 for the address of the current instruction.
2633 sub(dst, pc, Operand(pc_offset() + Instruction::kPcLoadDelta));
2634 }
2635
2636 void TurboAssembler::CallForDeoptimization(Builtin target, int, Label* exit,
2637 DeoptimizeKind kind, Label* ret,
2638 Label*) {
2639 ASM_CODE_COMMENT(this);
2640
2641 // All constants should have been emitted prior to deoptimization exit
2642 // emission. See PrepareForDeoptimizationExits.
2643 DCHECK(!has_pending_constants());
2644 BlockConstPoolScope block_const_pool(this);
2645
2646 CHECK_LE(target, Builtins::kLastTier0);
2647 ldr(ip,
2648 MemOperand(kRootRegister, IsolateData::BuiltinEntrySlotOffset(target)));
2649 Call(ip);
2650 DCHECK_EQ(SizeOfCodeGeneratedSince(exit),
2651 (kind == DeoptimizeKind::kLazy) ? Deoptimizer::kLazyDeoptExitSize
2652 : Deoptimizer::kEagerDeoptExitSize);
2653
2654 // The above code must not emit constants either.
2655 DCHECK(!has_pending_constants());
2656 }
2657
2658 void TurboAssembler::Trap() { stop(); }
2659 void TurboAssembler::DebugBreak() { stop(); }
2660
2661 void TurboAssembler::I64x2BitMask(Register dst, QwNeonRegister src) {
2662 UseScratchRegisterScope temps(this);
2663 QwNeonRegister tmp1 = temps.AcquireQ();
2664 Register tmp = temps.Acquire();
2665
2666 vshr(NeonU64, tmp1, src, 63);
2667 vmov(NeonU32, dst, tmp1.low(), 0);
2668 vmov(NeonU32, tmp, tmp1.high(), 0);
2669 add(dst, dst, Operand(tmp, LSL, 1));
2670 }
2671
2672 void TurboAssembler::I64x2Eq(QwNeonRegister dst, QwNeonRegister src1,
2673 QwNeonRegister src2) {
2674 UseScratchRegisterScope temps(this);
2675 Simd128Register scratch = temps.AcquireQ();
2676 vceq(Neon32, dst, src1, src2);
2677 vrev64(Neon32, scratch, dst);
2678 vand(dst, dst, scratch);
2679 }
2680
2681 void TurboAssembler::I64x2Ne(QwNeonRegister dst, QwNeonRegister src1,
2682 QwNeonRegister src2) {
2683 UseScratchRegisterScope temps(this);
2684 Simd128Register tmp = temps.AcquireQ();
2685 vceq(Neon32, dst, src1, src2);
2686 vrev64(Neon32, tmp, dst);
2687 vmvn(dst, dst);
2688 vorn(dst, dst, tmp);
2689 }
2690
2691 void TurboAssembler::I64x2GtS(QwNeonRegister dst, QwNeonRegister src1,
2692 QwNeonRegister src2) {
2693 ASM_CODE_COMMENT(this);
2694 vqsub(NeonS64, dst, src2, src1);
2695 vshr(NeonS64, dst, dst, 63);
2696 }
2697
2698 void TurboAssembler::I64x2GeS(QwNeonRegister dst, QwNeonRegister src1,
2699 QwNeonRegister src2) {
2700 ASM_CODE_COMMENT(this);
2701 vqsub(NeonS64, dst, src1, src2);
2702 vshr(NeonS64, dst, dst, 63);
2703 vmvn(dst, dst);
2704 }
2705
2706 void TurboAssembler::I64x2AllTrue(Register dst, QwNeonRegister src) {
2707 ASM_CODE_COMMENT(this);
2708 UseScratchRegisterScope temps(this);
2709 QwNeonRegister tmp = temps.AcquireQ();
2710 // src = | a | b | c | d |
2711 // tmp = | max(a,b) | max(c,d) | ...
2712 vpmax(NeonU32, tmp.low(), src.low(), src.high());
2713 // tmp = | max(a,b) == 0 | max(c,d) == 0 | ...
2714 vceq(Neon32, tmp, tmp, 0);
2715 // tmp = | max(a,b) == 0 or max(c,d) == 0 | ...
2716 vpmax(NeonU32, tmp.low(), tmp.low(), tmp.low());
2717 // dst = (max(a,b) == 0 || max(c,d) == 0)
2718 // dst will either be -1 or 0.
2719 vmov(NeonS32, dst, tmp.low(), 0);
2720 // dst = !dst (-1 -> 0, 0 -> 1)
2721 add(dst, dst, Operand(1));
2722 // This works because:
2723 // !dst
2724 // = !(max(a,b) == 0 || max(c,d) == 0)
2725 // = max(a,b) != 0 && max(c,d) != 0
2726 // = (a != 0 || b != 0) && (c != 0 || d != 0)
2727 // = defintion of i64x2.all_true.
2728 }
2729
2730 void TurboAssembler::I64x2Abs(QwNeonRegister dst, QwNeonRegister src) {
2731 ASM_CODE_COMMENT(this);
2732 UseScratchRegisterScope temps(this);
2733 Simd128Register tmp = temps.AcquireQ();
2734 vshr(NeonS64, tmp, src, 63);
2735 veor(dst, src, tmp);
2736 vsub(Neon64, dst, dst, tmp);
2737 }
2738
2739 namespace {
2740 using AssemblerFunc = void (Assembler::*)(DwVfpRegister, SwVfpRegister,
2741 VFPConversionMode, const Condition);
2742 // Helper function for f64x2 convert low instructions.
2743 // This ensures that we do not overwrite src, if dst == src.
2744 void F64x2ConvertLowHelper(Assembler* assm, QwNeonRegister dst,
2745 QwNeonRegister src, AssemblerFunc convert_fn) {
2746 LowDwVfpRegister src_d = LowDwVfpRegister::from_code(src.low().code());
2747 UseScratchRegisterScope temps(assm);
2748 if (dst == src) {
2749 LowDwVfpRegister tmp = temps.AcquireLowD();
2750 assm->vmov(tmp, src_d);
2751 src_d = tmp;
2752 }
2753 // Default arguments are not part of the function type
2754 (assm->*convert_fn)(dst.low(), src_d.low(), kDefaultRoundToZero, al);
2755 (assm->*convert_fn)(dst.high(), src_d.high(), kDefaultRoundToZero, al);
2756 }
2757 } // namespace
2758
2759 void TurboAssembler::F64x2ConvertLowI32x4S(QwNeonRegister dst,
2760 QwNeonRegister src) {
2761 F64x2ConvertLowHelper(this, dst, src, &Assembler::vcvt_f64_s32);
2762 }
2763
2764 void TurboAssembler::F64x2ConvertLowI32x4U(QwNeonRegister dst,
2765 QwNeonRegister src) {
2766 F64x2ConvertLowHelper(this, dst, src, &Assembler::vcvt_f64_u32);
2767 }
2768
2769 void TurboAssembler::F64x2PromoteLowF32x4(QwNeonRegister dst,
2770 QwNeonRegister src) {
2771 F64x2ConvertLowHelper(this, dst, src, &Assembler::vcvt_f64_f32);
2772 }
2773
2774 } // namespace internal
2775 } // namespace v8
2776
2777 #endif // V8_TARGET_ARCH_ARM
2778