Line data Source code
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 : #ifndef INCLUDED_FROM_MACRO_ASSEMBLER_H
6 : #error This header must be included via macro-assembler.h
7 : #endif
8 :
9 : #ifndef V8_X64_MACRO_ASSEMBLER_X64_H_
10 : #define V8_X64_MACRO_ASSEMBLER_X64_H_
11 :
12 : #include "src/bailout-reason.h"
13 : #include "src/base/flags.h"
14 : #include "src/contexts.h"
15 : #include "src/globals.h"
16 : #include "src/x64/assembler-x64.h"
17 :
18 : namespace v8 {
19 : namespace internal {
20 :
21 : // Convenience for platform-independent signatures.
22 : typedef Operand MemOperand;
23 :
24 : class StringConstantBase;
25 :
26 : enum RememberedSetAction { EMIT_REMEMBERED_SET, OMIT_REMEMBERED_SET };
27 : enum SmiCheck { INLINE_SMI_CHECK, OMIT_SMI_CHECK };
28 :
29 : struct SmiIndex {
30 : SmiIndex(Register index_register, ScaleFactor scale)
31 : : reg(index_register),
32 : scale(scale) {}
33 : Register reg;
34 : ScaleFactor scale;
35 : };
36 :
37 : enum StackArgumentsAccessorReceiverMode {
38 : ARGUMENTS_CONTAIN_RECEIVER,
39 : ARGUMENTS_DONT_CONTAIN_RECEIVER
40 : };
41 :
42 : class StackArgumentsAccessor {
43 : public:
44 : StackArgumentsAccessor(Register base_reg, int argument_count_immediate,
45 : StackArgumentsAccessorReceiverMode receiver_mode =
46 : ARGUMENTS_CONTAIN_RECEIVER,
47 : int extra_displacement_to_last_argument = 0)
48 : : base_reg_(base_reg),
49 : argument_count_reg_(no_reg),
50 : argument_count_immediate_(argument_count_immediate),
51 : receiver_mode_(receiver_mode),
52 : extra_displacement_to_last_argument_(
53 : extra_displacement_to_last_argument) {}
54 :
55 952 : StackArgumentsAccessor(Register base_reg, Register argument_count_reg,
56 : StackArgumentsAccessorReceiverMode receiver_mode =
57 : ARGUMENTS_CONTAIN_RECEIVER,
58 : int extra_displacement_to_last_argument = 0)
59 : : base_reg_(base_reg),
60 : argument_count_reg_(argument_count_reg),
61 : argument_count_immediate_(0),
62 : receiver_mode_(receiver_mode),
63 : extra_displacement_to_last_argument_(
64 952 : extra_displacement_to_last_argument) {}
65 :
66 : StackArgumentsAccessor(Register base_reg,
67 : const ParameterCount& parameter_count,
68 : StackArgumentsAccessorReceiverMode receiver_mode =
69 : ARGUMENTS_CONTAIN_RECEIVER,
70 : int extra_displacement_to_last_argument = 0);
71 :
72 : Operand GetArgumentOperand(int index);
73 672 : Operand GetReceiverOperand() {
74 : DCHECK(receiver_mode_ == ARGUMENTS_CONTAIN_RECEIVER);
75 1008 : return GetArgumentOperand(0);
76 : }
77 :
78 : private:
79 : const Register base_reg_;
80 : const Register argument_count_reg_;
81 : const int argument_count_immediate_;
82 : const StackArgumentsAccessorReceiverMode receiver_mode_;
83 : const int extra_displacement_to_last_argument_;
84 :
85 : DISALLOW_IMPLICIT_CONSTRUCTORS(StackArgumentsAccessor);
86 : };
87 :
88 81810283 : class V8_EXPORT_PRIVATE TurboAssembler : public TurboAssemblerBase {
89 : public:
90 122668858 : using TurboAssemblerBase::TurboAssemblerBase;
91 :
92 : template <typename Dst, typename... Args>
93 : struct AvxHelper {
94 : Assembler* assm;
95 : // Call an method where the AVX version expects the dst argument to be
96 : // duplicated.
97 : template <void (Assembler::*avx)(Dst, Dst, Args...),
98 : void (Assembler::*no_avx)(Dst, Args...)>
99 1130282 : void emit(Dst dst, Args... args) {
100 1130282 : if (CpuFeatures::IsSupported(AVX)) {
101 : CpuFeatureScope scope(assm, AVX);
102 1128732 : (assm->*avx)(dst, dst, args...);
103 : } else {
104 1550 : (assm->*no_avx)(dst, args...);
105 : }
106 1130323 : }
107 :
108 : // Call an method where the AVX version expects no duplicated dst argument.
109 : template <void (Assembler::*avx)(Dst, Args...),
110 : void (Assembler::*no_avx)(Dst, Args...)>
111 4222813 : void emit(Dst dst, Args... args) {
112 4222813 : if (CpuFeatures::IsSupported(AVX)) {
113 486478 : CpuFeatureScope scope(assm, AVX);
114 4213179 : (assm->*avx)(dst, args...);
115 : } else {
116 9634 : (assm->*no_avx)(dst, args...);
117 : }
118 4222860 : }
119 : };
120 :
121 : #define AVX_OP(macro_name, name) \
122 : template <typename Dst, typename... Args> \
123 : void macro_name(Dst dst, Args... args) { \
124 : AvxHelper<Dst, Args...>{this} \
125 : .template emit<&Assembler::v##name, &Assembler::name>(dst, args...); \
126 : }
127 :
128 6049 : AVX_OP(Subsd, subsd)
129 193 : AVX_OP(Divss, divss)
130 660 : AVX_OP(Divsd, divsd)
131 10009 : AVX_OP(Xorps, xorps)
132 110921 : AVX_OP(Xorpd, xorpd)
133 120185 : AVX_OP(Movd, movd)
134 159872 : AVX_OP(Movq, movq)
135 353 : AVX_OP(Movaps, movaps)
136 126914 : AVX_OP(Movapd, movapd)
137 13324 : AVX_OP(Movups, movups)
138 195 : AVX_OP(Movmskps, movmskps)
139 661 : AVX_OP(Movmskpd, movmskpd)
140 807339 : AVX_OP(Movss, movss)
141 3152124 : AVX_OP(Movsd, movsd)
142 269431 : AVX_OP(Pcmpeqd, pcmpeqd)
143 49104 : AVX_OP(Pslld, pslld)
144 210171 : AVX_OP(Psllq, psllq)
145 37201 : AVX_OP(Psrld, psrld)
146 189561 : AVX_OP(Psrlq, psrlq)
147 : AVX_OP(Addsd, addsd)
148 : AVX_OP(Mulsd, mulsd)
149 42 : AVX_OP(Andps, andps)
150 32 : AVX_OP(Andpd, andpd)
151 : AVX_OP(Orpd, orpd)
152 : AVX_OP(Cmpeqps, cmpeqps)
153 : AVX_OP(Cmpltps, cmpltps)
154 : AVX_OP(Cmpleps, cmpleps)
155 : AVX_OP(Cmpneqps, cmpneqps)
156 : AVX_OP(Cmpnltps, cmpnltps)
157 : AVX_OP(Cmpnleps, cmpnleps)
158 : AVX_OP(Cmpeqpd, cmpeqpd)
159 : AVX_OP(Cmpltpd, cmpltpd)
160 : AVX_OP(Cmplepd, cmplepd)
161 : AVX_OP(Cmpneqpd, cmpneqpd)
162 : AVX_OP(Cmpnltpd, cmpnltpd)
163 : AVX_OP(Cmpnlepd, cmpnlepd)
164 586 : AVX_OP(Roundss, roundss)
165 46187 : AVX_OP(Roundsd, roundsd)
166 152 : AVX_OP(Sqrtss, sqrtss)
167 543 : AVX_OP(Sqrtsd, sqrtsd)
168 1090 : AVX_OP(Ucomiss, ucomiss)
169 4630 : AVX_OP(Ucomisd, ucomisd)
170 :
171 : #undef AVX_OP
172 :
173 1852 : void PushReturnAddressFrom(Register src) { pushq(src); }
174 1460 : void PopReturnAddressTo(Register dst) { popq(dst); }
175 :
176 : void Ret();
177 :
178 : // Return and drop arguments from stack, where the number of arguments
179 : // may be bigger than 2^16 - 1. Requires a scratch register.
180 : void Ret(int bytes_dropped, Register scratch);
181 :
182 : // Load a register with a long value as efficiently as possible.
183 : void Set(Register dst, int64_t x);
184 : void Set(Operand dst, intptr_t x);
185 :
186 : // Operations on roots in the root-array.
187 : void LoadRoot(Register destination, RootIndex index) override;
188 : void LoadRoot(Operand destination, RootIndex index) {
189 : LoadRoot(kScratchRegister, index);
190 : movq(destination, kScratchRegister);
191 : }
192 :
193 : void Push(Register src);
194 : void Push(Operand src);
195 : void Push(Immediate value);
196 : void Push(Smi smi);
197 : void Push(Handle<HeapObject> source);
198 :
199 : // Before calling a C-function from generated code, align arguments on stack.
200 : // After aligning the frame, arguments must be stored in rsp[0], rsp[8],
201 : // etc., not pushed. The argument count assumes all arguments are word sized.
202 : // The number of slots reserved for arguments depends on platform. On Windows
203 : // stack slots are reserved for the arguments passed in registers. On other
204 : // platforms stack slots are only reserved for the arguments actually passed
205 : // on the stack.
206 : void PrepareCallCFunction(int num_arguments);
207 :
208 : // Calls a C function and cleans up the space for arguments allocated
209 : // by PrepareCallCFunction. The called function is not allowed to trigger a
210 : // garbage collection, since that might move the code and invalidate the
211 : // return address (unless this is somehow accounted for by the called
212 : // function).
213 : void CallCFunction(ExternalReference function, int num_arguments);
214 : void CallCFunction(Register function, int num_arguments);
215 :
216 : // Calculate the number of stack slots to reserve for arguments when calling a
217 : // C function.
218 : int ArgumentStackSlotsForCFunctionCall(int num_arguments);
219 :
220 : void CheckPageFlag(Register object, Register scratch, int mask, Condition cc,
221 : Label* condition_met,
222 : Label::Distance condition_met_distance = Label::kFar);
223 :
224 : void Cvtss2sd(XMMRegister dst, XMMRegister src);
225 : void Cvtss2sd(XMMRegister dst, Operand src);
226 : void Cvtsd2ss(XMMRegister dst, XMMRegister src);
227 : void Cvtsd2ss(XMMRegister dst, Operand src);
228 : void Cvttsd2si(Register dst, XMMRegister src);
229 : void Cvttsd2si(Register dst, Operand src);
230 : void Cvttsd2siq(Register dst, XMMRegister src);
231 : void Cvttsd2siq(Register dst, Operand src);
232 : void Cvttss2si(Register dst, XMMRegister src);
233 : void Cvttss2si(Register dst, Operand src);
234 : void Cvttss2siq(Register dst, XMMRegister src);
235 : void Cvttss2siq(Register dst, Operand src);
236 : void Cvtqsi2ss(XMMRegister dst, Register src);
237 : void Cvtqsi2ss(XMMRegister dst, Operand src);
238 : void Cvtqsi2sd(XMMRegister dst, Register src);
239 : void Cvtqsi2sd(XMMRegister dst, Operand src);
240 : void Cvtlsi2ss(XMMRegister dst, Register src);
241 : void Cvtlsi2ss(XMMRegister dst, Operand src);
242 : void Cvtlui2ss(XMMRegister dst, Register src);
243 : void Cvtlui2ss(XMMRegister dst, Operand src);
244 : void Cvtlui2sd(XMMRegister dst, Register src);
245 : void Cvtlui2sd(XMMRegister dst, Operand src);
246 : void Cvtqui2ss(XMMRegister dst, Register src);
247 : void Cvtqui2ss(XMMRegister dst, Operand src);
248 : void Cvtqui2sd(XMMRegister dst, Register src);
249 : void Cvtqui2sd(XMMRegister dst, Operand src);
250 : void Cvttsd2uiq(Register dst, Operand src, Label* fail = nullptr);
251 : void Cvttsd2uiq(Register dst, XMMRegister src, Label* fail = nullptr);
252 : void Cvttss2uiq(Register dst, Operand src, Label* fail = nullptr);
253 : void Cvttss2uiq(Register dst, XMMRegister src, Label* fail = nullptr);
254 :
255 : // cvtsi2sd instruction only writes to the low 64-bit of dst register, which
256 : // hinders register renaming and makes dependence chains longer. So we use
257 : // xorpd to clear the dst register before cvtsi2sd to solve this issue.
258 : void Cvtlsi2sd(XMMRegister dst, Register src);
259 : void Cvtlsi2sd(XMMRegister dst, Operand src);
260 :
261 : void Lzcntq(Register dst, Register src);
262 : void Lzcntq(Register dst, Operand src);
263 : void Lzcntl(Register dst, Register src);
264 : void Lzcntl(Register dst, Operand src);
265 : void Tzcntq(Register dst, Register src);
266 : void Tzcntq(Register dst, Operand src);
267 : void Tzcntl(Register dst, Register src);
268 : void Tzcntl(Register dst, Operand src);
269 : void Popcntl(Register dst, Register src);
270 : void Popcntl(Register dst, Operand src);
271 : void Popcntq(Register dst, Register src);
272 : void Popcntq(Register dst, Operand src);
273 :
274 : // Is the value a tagged smi.
275 : Condition CheckSmi(Register src);
276 : Condition CheckSmi(Operand src);
277 :
278 : // Jump to label if the value is a tagged smi.
279 : void JumpIfSmi(Register src, Label* on_smi,
280 : Label::Distance near_jump = Label::kFar);
281 :
282 188322 : void JumpIfEqual(Register a, int32_t b, Label* dest) {
283 188322 : cmpl(a, Immediate(b));
284 188322 : j(equal, dest);
285 188323 : }
286 :
287 40664 : void JumpIfLessThan(Register a, int32_t b, Label* dest) {
288 40664 : cmpl(a, Immediate(b));
289 40664 : j(less, dest);
290 40664 : }
291 :
292 : void Move(Register dst, Smi source);
293 :
294 : void Move(Operand dst, Smi source) {
295 : Register constant = GetSmiConstant(source);
296 : movq(dst, constant);
297 : }
298 :
299 : void Move(Register dst, ExternalReference ext);
300 :
301 : void Move(XMMRegister dst, uint32_t src);
302 : void Move(XMMRegister dst, uint64_t src);
303 216 : void Move(XMMRegister dst, float src) { Move(dst, bit_cast<uint32_t>(src)); }
304 3824 : void Move(XMMRegister dst, double src) { Move(dst, bit_cast<uint64_t>(src)); }
305 :
306 : // Move if the registers are not identical.
307 : void Move(Register target, Register source);
308 :
309 : void Move(Register dst, Handle<HeapObject> source,
310 : RelocInfo::Mode rmode = RelocInfo::EMBEDDED_OBJECT);
311 : void Move(Operand dst, Handle<HeapObject> source,
312 : RelocInfo::Mode rmode = RelocInfo::EMBEDDED_OBJECT);
313 :
314 : // Loads a pointer into a register with a relocation mode.
315 : void Move(Register dst, Address ptr, RelocInfo::Mode rmode) {
316 : // This method must not be used with heap object references. The stored
317 : // address is not GC safe. Use the handle version instead.
318 : DCHECK(rmode > RelocInfo::LAST_GCED_ENUM);
319 42486592 : movq(dst, Immediate64(ptr, rmode));
320 : }
321 :
322 : // Move src0 to dst0 and src1 to dst1, handling possible overlaps.
323 : void MovePair(Register dst0, Register src0, Register dst1, Register src1);
324 :
325 : void MoveStringConstant(Register result, const StringConstantBase* string,
326 : RelocInfo::Mode rmode = RelocInfo::EMBEDDED_OBJECT);
327 :
328 : // Convert smi to word-size sign-extended value.
329 : void SmiUntag(Register dst, Register src);
330 : void SmiUntag(Register dst, Operand src);
331 :
332 : // Loads the address of the external reference into the destination
333 : // register.
334 : void LoadAddress(Register destination, ExternalReference source);
335 :
336 : void LoadFromConstantsTable(Register destination,
337 : int constant_index) override;
338 : void LoadRootRegisterOffset(Register destination, intptr_t offset) override;
339 : void LoadRootRelative(Register destination, int32_t offset) override;
340 :
341 : // Operand pointing to an external reference.
342 : // May emit code to set up the scratch register. The operand is
343 : // only guaranteed to be correct as long as the scratch register
344 : // isn't changed.
345 : // If the operand is used more than once, use a scratch register
346 : // that is guaranteed not to be clobbered.
347 : Operand ExternalReferenceAsOperand(ExternalReference reference,
348 : Register scratch = kScratchRegister);
349 :
350 84840 : void Call(Register reg) { call(reg); }
351 : void Call(Operand op);
352 : void Call(Handle<Code> code_object, RelocInfo::Mode rmode);
353 : void Call(Address destination, RelocInfo::Mode rmode);
354 : void Call(ExternalReference ext);
355 : void Call(Label* target) { call(target); }
356 :
357 : void CallBuiltinPointer(Register builtin_pointer) override;
358 :
359 : void LoadCodeObjectEntry(Register destination, Register code_object) override;
360 : void CallCodeObject(Register code_object) override;
361 : void JumpCodeObject(Register code_object) override;
362 :
363 : void RetpolineCall(Register reg);
364 : void RetpolineCall(Address destination, RelocInfo::Mode rmode);
365 :
366 : void Jump(Address destination, RelocInfo::Mode rmode);
367 : void Jump(ExternalReference ext);
368 : void Jump(Operand op);
369 : void Jump(Handle<Code> code_object, RelocInfo::Mode rmode,
370 : Condition cc = always);
371 :
372 : void RetpolineJump(Register reg);
373 :
374 : void CallForDeoptimization(Address target, int deopt_id);
375 :
376 : // Non-SSE2 instructions.
377 : void Pextrd(Register dst, XMMRegister src, int8_t imm8);
378 : void Pinsrd(XMMRegister dst, Register src, int8_t imm8);
379 : void Pinsrd(XMMRegister dst, Operand src, int8_t imm8);
380 :
381 : void CompareRoot(Register with, RootIndex index);
382 : void CompareRoot(Operand with, RootIndex index);
383 :
384 : // Generates function and stub prologue code.
385 : void StubPrologue(StackFrame::Type type);
386 : void Prologue();
387 :
388 : // Calls Abort(msg) if the condition cc is not satisfied.
389 : // Use --debug_code to enable.
390 : void Assert(Condition cc, AbortReason reason);
391 :
392 : // Like Assert(), but without condition.
393 : // Use --debug_code to enable.
394 : void AssertUnreachable(AbortReason reason);
395 :
396 : // Abort execution if a 64 bit register containing a 32 bit payload does not
397 : // have zeros in the top 32 bits, enabled via --debug-code.
398 : void AssertZeroExtended(Register reg);
399 :
400 : // Like Assert(), but always enabled.
401 : void Check(Condition cc, AbortReason reason);
402 :
403 : // Print a message to stdout and abort execution.
404 : void Abort(AbortReason msg);
405 :
406 : // Check that the stack is aligned.
407 : void CheckStackAlignment();
408 :
409 : // Activation support.
410 : void EnterFrame(StackFrame::Type type);
411 : void EnterFrame(StackFrame::Type type, bool load_constant_pool_pointer_reg) {
412 : // Out-of-line constant pool not implemented on x64.
413 : UNREACHABLE();
414 : }
415 : void LeaveFrame(StackFrame::Type type);
416 :
417 : // Removes current frame and its arguments from the stack preserving the
418 : // arguments and a return address pushed to the stack for the next call. Both
419 : // |callee_args_count| and |caller_args_count_reg| do not include receiver.
420 : // |callee_args_count| is not modified, |caller_args_count_reg| is trashed.
421 : void PrepareForTailCall(const ParameterCount& callee_args_count,
422 : Register caller_args_count_reg, Register scratch0,
423 : Register scratch1);
424 :
425 : // Call a runtime routine. This expects {centry} to contain a fitting CEntry
426 : // builtin for the target runtime function and uses an indirect call.
427 : void CallRuntimeWithCEntry(Runtime::FunctionId fid, Register centry);
428 :
429 888607 : void InitializeRootRegister() {
430 888607 : ExternalReference isolate_root = ExternalReference::isolate_root(isolate());
431 888607 : Move(kRootRegister, isolate_root);
432 888607 : }
433 :
434 : void SaveRegisters(RegList registers);
435 : void RestoreRegisters(RegList registers);
436 :
437 : void CallRecordWriteStub(Register object, Register address,
438 : RememberedSetAction remembered_set_action,
439 : SaveFPRegsMode fp_mode);
440 : void CallRecordWriteStub(Register object, Register address,
441 : RememberedSetAction remembered_set_action,
442 : SaveFPRegsMode fp_mode, Address wasm_target);
443 :
444 : void MoveNumber(Register dst, double value);
445 : void MoveNonSmi(Register dst, double value);
446 :
447 : // Calculate how much stack space (in bytes) are required to store caller
448 : // registers excluding those specified in the arguments.
449 : int RequiredStackSizeForCallerSaved(SaveFPRegsMode fp_mode,
450 : Register exclusion1 = no_reg,
451 : Register exclusion2 = no_reg,
452 : Register exclusion3 = no_reg) const;
453 :
454 : // PushCallerSaved and PopCallerSaved do not arrange the registers in any
455 : // particular order so they are not useful for calls that can cause a GC.
456 : // The caller can exclude up to 3 registers that do not need to be saved and
457 : // restored.
458 :
459 : // Push caller saved registers on the stack, and return the number of bytes
460 : // stack pointer is adjusted.
461 : int PushCallerSaved(SaveFPRegsMode fp_mode, Register exclusion1 = no_reg,
462 : Register exclusion2 = no_reg,
463 : Register exclusion3 = no_reg);
464 : // Restore caller saved registers from the stack, and return the number of
465 : // bytes stack pointer is adjusted.
466 : int PopCallerSaved(SaveFPRegsMode fp_mode, Register exclusion1 = no_reg,
467 : Register exclusion2 = no_reg,
468 : Register exclusion3 = no_reg);
469 :
470 : // Compute the start of the generated instruction stream from the current PC.
471 : // This is an alternative to embedding the {CodeObject} handle as a reference.
472 : void ComputeCodeStartAddress(Register dst);
473 :
474 : void ResetSpeculationPoisonRegister();
475 :
476 : // ---------------------------------------------------------------------------
477 : // Pointer compression support
478 :
479 : // Loads a field containing a HeapObject and decompresses it if pointer
480 : // compression is enabled.
481 : void LoadTaggedPointerField(Register destination, Operand field_operand);
482 :
483 : // Loads a field containing any tagged value and decompresses it if necessary.
484 : // When pointer compression is enabled, uses |scratch| to decompress the
485 : // value.
486 : void LoadAnyTaggedField(Register destination, Operand field_operand,
487 : Register scratch = kScratchRegister);
488 :
489 : // Loads a field containing a HeapObject, decompresses it if necessary and
490 : // pushes full pointer to the stack. When pointer compression is enabled,
491 : // uses |scratch| to decompress the value.
492 : void PushTaggedPointerField(Operand field_operand, Register scratch);
493 :
494 : // Loads a field containing any tagged value, decompresses it if necessary and
495 : // pushes the full pointer to the stack. When pointer compression is enabled,
496 : // uses |scratch1| and |scratch2| to decompress the value.
497 : void PushTaggedAnyField(Operand field_operand, Register scratch1,
498 : Register scratch2);
499 :
500 : // Loads a field containing smi value and untags it.
501 : void SmiUntagField(Register dst, Operand src);
502 :
503 : // Compresses tagged value if necessary and stores it to given on-heap
504 : // location.
505 : void StoreTaggedField(Operand dst_field_operand, Immediate immediate);
506 : void StoreTaggedField(Operand dst_field_operand, Register value);
507 :
508 : // The following macros work even when pointer compression is not enabled.
509 : void DecompressTaggedSigned(Register destination, Operand field_operand);
510 : void DecompressTaggedPointer(Register destination, Operand field_operand);
511 : void DecompressAnyTagged(Register destination, Operand field_operand,
512 : Register scratch = kScratchRegister);
513 :
514 : protected:
515 : static const int kSmiShift = kSmiTagSize + kSmiShiftSize;
516 : int smi_count = 0;
517 : int heap_object_count = 0;
518 :
519 : // Returns a register holding the smi value. The register MUST NOT be
520 : // modified. It may be the "smi 1 constant" register.
521 : Register GetSmiConstant(Smi value);
522 :
523 : void CallRecordWriteStub(Register object, Register address,
524 : RememberedSetAction remembered_set_action,
525 : SaveFPRegsMode fp_mode, Handle<Code> code_target,
526 : Address wasm_target);
527 : };
528 :
529 : // MacroAssembler implements a collection of frequently used macros.
530 75586289 : class V8_EXPORT_PRIVATE MacroAssembler : public TurboAssembler {
531 : public:
532 113337637 : using TurboAssembler::TurboAssembler;
533 :
534 : // Loads and stores the value of an external reference.
535 : // Special case code for load and store to take advantage of
536 : // load_rax/store_rax if possible/necessary.
537 : // For other operations, just use:
538 : // Operand operand = ExternalReferenceAsOperand(extref);
539 : // operation(operand, ..);
540 : void Load(Register destination, ExternalReference source);
541 : void Store(ExternalReference destination, Register source);
542 :
543 : // Pushes the address of the external reference onto the stack.
544 : void PushAddress(ExternalReference source);
545 :
546 : // Operations on roots in the root-array.
547 : // Load a root value where the index (or part of it) is variable.
548 : // The variable_offset register is added to the fixed_offset value
549 : // to get the index into the root-array.
550 : void PushRoot(RootIndex index);
551 :
552 : // Compare the object in a register to a value and jump if they are equal.
553 280 : void JumpIfRoot(Register with, RootIndex index, Label* if_equal,
554 : Label::Distance if_equal_distance = Label::kFar) {
555 280 : CompareRoot(with, index);
556 280 : j(equal, if_equal, if_equal_distance);
557 280 : }
558 : void JumpIfRoot(Operand with, RootIndex index, Label* if_equal,
559 : Label::Distance if_equal_distance = Label::kFar) {
560 : CompareRoot(with, index);
561 : j(equal, if_equal, if_equal_distance);
562 : }
563 :
564 : // Compare the object in a register to a value and jump if they are not equal.
565 56 : void JumpIfNotRoot(Register with, RootIndex index, Label* if_not_equal,
566 : Label::Distance if_not_equal_distance = Label::kFar) {
567 56 : CompareRoot(with, index);
568 56 : j(not_equal, if_not_equal, if_not_equal_distance);
569 56 : }
570 : void JumpIfNotRoot(Operand with, RootIndex index, Label* if_not_equal,
571 : Label::Distance if_not_equal_distance = Label::kFar) {
572 : CompareRoot(with, index);
573 : j(not_equal, if_not_equal, if_not_equal_distance);
574 : }
575 :
576 : // ---------------------------------------------------------------------------
577 : // GC Support
578 :
579 : // Notify the garbage collector that we wrote a pointer into an object.
580 : // |object| is the object being stored into, |value| is the object being
581 : // stored. value and scratch registers are clobbered by the operation.
582 : // The offset is the offset from the start of the object, not the offset from
583 : // the tagged HeapObject pointer. For use with FieldOperand(reg, off).
584 : void RecordWriteField(
585 : Register object, int offset, Register value, Register scratch,
586 : SaveFPRegsMode save_fp,
587 : RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET,
588 : SmiCheck smi_check = INLINE_SMI_CHECK);
589 :
590 : // For page containing |object| mark region covering |address|
591 : // dirty. |object| is the object being stored into, |value| is the
592 : // object being stored. The address and value registers are clobbered by the
593 : // operation. RecordWrite filters out smis so it does not update
594 : // the write barrier if the value is a smi.
595 : void RecordWrite(
596 : Register object, Register address, Register value, SaveFPRegsMode save_fp,
597 : RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET,
598 : SmiCheck smi_check = INLINE_SMI_CHECK);
599 :
600 : // Frame restart support.
601 : void MaybeDropFrames();
602 :
603 : // Enter specific kind of exit frame; either in normal or
604 : // debug mode. Expects the number of arguments in register rax and
605 : // sets up the number of arguments in register rdi and the pointer
606 : // to the first argument in register rsi.
607 : //
608 : // Allocates arg_stack_space * kSystemPointerSize memory (not GCed) on the
609 : // stack accessible via StackSpaceOperand.
610 : void EnterExitFrame(int arg_stack_space = 0, bool save_doubles = false,
611 : StackFrame::Type frame_type = StackFrame::EXIT);
612 :
613 : // Enter specific kind of exit frame. Allocates
614 : // (arg_stack_space * kSystemPointerSize) memory (not GCed) on the stack
615 : // accessible via StackSpaceOperand.
616 : void EnterApiExitFrame(int arg_stack_space);
617 :
618 : // Leave the current exit frame. Expects/provides the return value in
619 : // register rax:rdx (untouched) and the pointer to the first
620 : // argument in register rsi (if pop_arguments == true).
621 : void LeaveExitFrame(bool save_doubles = false, bool pop_arguments = true);
622 :
623 : // Leave the current exit frame. Expects/provides the return value in
624 : // register rax (untouched).
625 : void LeaveApiExitFrame();
626 :
627 : // Push and pop the registers that can hold pointers.
628 0 : void PushSafepointRegisters() { Pushad(); }
629 0 : void PopSafepointRegisters() { Popad(); }
630 :
631 : // ---------------------------------------------------------------------------
632 : // JavaScript invokes
633 :
634 : // Invoke the JavaScript function code by either calling or jumping.
635 : void InvokeFunctionCode(Register function, Register new_target,
636 : const ParameterCount& expected,
637 : const ParameterCount& actual, InvokeFlag flag);
638 :
639 : // On function call, call into the debugger if necessary.
640 : void CheckDebugHook(Register fun, Register new_target,
641 : const ParameterCount& expected,
642 : const ParameterCount& actual);
643 :
644 : // Invoke the JavaScript function in the given register. Changes the
645 : // current context to the context in the function before invoking.
646 : void InvokeFunction(Register function, Register new_target,
647 : const ParameterCount& actual, InvokeFlag flag);
648 :
649 : void InvokeFunction(Register function, Register new_target,
650 : const ParameterCount& expected,
651 : const ParameterCount& actual, InvokeFlag flag);
652 :
653 : // ---------------------------------------------------------------------------
654 : // Conversions between tagged smi values and non-tagged integer values.
655 :
656 : // Tag an word-size value. The result must be known to be a valid smi value.
657 : void SmiTag(Register dst, Register src);
658 :
659 : // Simple comparison of smis. Both sides must be known smis to use these,
660 : // otherwise use Cmp.
661 : void SmiCompare(Register smi1, Register smi2);
662 : void SmiCompare(Register dst, Smi src);
663 : void SmiCompare(Register dst, Operand src);
664 : void SmiCompare(Operand dst, Register src);
665 : void SmiCompare(Operand dst, Smi src);
666 :
667 : // Functions performing a check on a known or potential smi. Returns
668 : // a condition that is satisfied if the check is successful.
669 :
670 : // Test-and-jump functions. Typically combines a check function
671 : // above with a conditional jump.
672 :
673 : // Jump to label if the value is not a tagged smi.
674 : void JumpIfNotSmi(Register src,
675 : Label* on_not_smi,
676 : Label::Distance near_jump = Label::kFar);
677 :
678 : // Jump to label if the value is not a tagged smi.
679 : void JumpIfNotSmi(Operand src, Label* on_not_smi,
680 : Label::Distance near_jump = Label::kFar);
681 :
682 : // Operations on tagged smi values.
683 :
684 : // Smis represent a subset of integers. The subset is always equivalent to
685 : // a two's complement interpretation of a fixed number of bits.
686 :
687 : // Add an integer constant to a tagged smi, giving a tagged smi as result.
688 : // No overflow testing on the result is done.
689 : void SmiAddConstant(Operand dst, Smi constant);
690 :
691 : // Specialized operations
692 :
693 : // Converts, if necessary, a smi to a combination of number and
694 : // multiplier to be used as a scaled index.
695 : // The src register contains a *positive* smi value. The shift is the
696 : // power of two to multiply the index value by (e.g. to index by
697 : // smi-value * kSystemPointerSize, pass the smi and kSystemPointerSizeLog2).
698 : // The returned index register may be either src or dst, depending
699 : // on what is most efficient. If src and dst are different registers,
700 : // src is always unchanged.
701 : SmiIndex SmiToIndex(Register dst, Register src, int shift);
702 :
703 : // ---------------------------------------------------------------------------
704 : // Macro instructions.
705 :
706 : void Cmp(Register dst, Handle<Object> source);
707 : void Cmp(Operand dst, Handle<Object> source);
708 : void Cmp(Register dst, Smi src);
709 : void Cmp(Operand dst, Smi src);
710 :
711 : // Checks if value is in range [lower_limit, higher_limit] using a single
712 : // comparison.
713 : void JumpIfIsInRange(Register value, unsigned lower_limit,
714 : unsigned higher_limit, Label* on_in_range,
715 : Label::Distance near_jump = Label::kFar);
716 :
717 : // Emit code to discard a non-negative number of pointer-sized elements
718 : // from the stack, clobbering only the rsp register.
719 : void Drop(int stack_elements);
720 : // Emit code to discard a positive number of pointer-sized elements
721 : // from the stack under the return address which remains on the top,
722 : // clobbering the rsp register.
723 : void DropUnderReturnAddress(int stack_elements,
724 : Register scratch = kScratchRegister);
725 :
726 : void PushQuad(Operand src);
727 : void PushImm32(int32_t imm32);
728 : void Pop(Register dst);
729 : void Pop(Operand dst);
730 : void PopQuad(Operand dst);
731 :
732 : // ---------------------------------------------------------------------------
733 : // SIMD macros.
734 : void Absps(XMMRegister dst);
735 : void Negps(XMMRegister dst);
736 : void Abspd(XMMRegister dst);
737 : void Negpd(XMMRegister dst);
738 : // Generates a trampoline to jump to the off-heap instruction stream.
739 : void JumpToInstructionStream(Address entry);
740 :
741 : // Non-x64 instructions.
742 : // Push/pop all general purpose registers.
743 : // Does not push rsp/rbp nor any of the assembler's special purpose registers
744 : // (kScratchRegister, kRootRegister).
745 : void Pushad();
746 : void Popad();
747 :
748 : // Compare object type for heap object.
749 : // Always use unsigned comparisons: above and below, not less and greater.
750 : // Incoming register is heap_object and outgoing register is map.
751 : // They may be the same register, and may be kScratchRegister.
752 : void CmpObjectType(Register heap_object, InstanceType type, Register map);
753 :
754 : // Compare instance type for map.
755 : // Always use unsigned comparisons: above and below, not less and greater.
756 : void CmpInstanceType(Register map, InstanceType type);
757 :
758 : void DoubleToI(Register result_reg, XMMRegister input_reg,
759 : XMMRegister scratch, Label* lost_precision, Label* is_nan,
760 : Label::Distance dst = Label::kFar);
761 :
762 : template<typename Field>
763 56 : void DecodeField(Register reg) {
764 : static const int shift = Field::kShift;
765 : static const int mask = Field::kMask >> Field::kShift;
766 : if (shift != 0) {
767 0 : shrq(reg, Immediate(shift));
768 : }
769 56 : andq(reg, Immediate(mask));
770 56 : }
771 :
772 : // Abort execution if argument is a smi, enabled via --debug-code.
773 : void AssertNotSmi(Register object);
774 :
775 : // Abort execution if argument is not a smi, enabled via --debug-code.
776 : void AssertSmi(Register object);
777 : void AssertSmi(Operand object);
778 :
779 : // Abort execution if argument is not a Constructor, enabled via --debug-code.
780 : void AssertConstructor(Register object);
781 :
782 : // Abort execution if argument is not a JSFunction, enabled via --debug-code.
783 : void AssertFunction(Register object);
784 :
785 : // Abort execution if argument is not a JSBoundFunction,
786 : // enabled via --debug-code.
787 : void AssertBoundFunction(Register object);
788 :
789 : // Abort execution if argument is not a JSGeneratorObject (or subclass),
790 : // enabled via --debug-code.
791 : void AssertGeneratorObject(Register object);
792 :
793 : // Abort execution if argument is not undefined or an AllocationSite, enabled
794 : // via --debug-code.
795 : void AssertUndefinedOrAllocationSite(Register object);
796 :
797 : // ---------------------------------------------------------------------------
798 : // Exception handling
799 :
800 : // Push a new stack handler and link it into stack handler chain.
801 : void PushStackHandler();
802 :
803 : // Unlink the stack handler on top of the stack from the stack handler chain.
804 : void PopStackHandler();
805 :
806 : // ---------------------------------------------------------------------------
807 : // Support functions.
808 :
809 : // Load the global proxy from the current context.
810 112 : void LoadGlobalProxy(Register dst) {
811 112 : LoadNativeContextSlot(Context::GLOBAL_PROXY_INDEX, dst);
812 112 : }
813 :
814 : // Load the native context slot with the current index.
815 : void LoadNativeContextSlot(int index, Register dst);
816 :
817 : // ---------------------------------------------------------------------------
818 : // Runtime calls
819 :
820 : // Call a runtime routine.
821 : void CallRuntime(const Runtime::Function* f,
822 : int num_arguments,
823 : SaveFPRegsMode save_doubles = kDontSaveFPRegs);
824 :
825 : // Convenience function: Same as above, but takes the fid instead.
826 1568 : void CallRuntime(Runtime::FunctionId fid,
827 : SaveFPRegsMode save_doubles = kDontSaveFPRegs) {
828 1568 : const Runtime::Function* function = Runtime::FunctionForId(fid);
829 1568 : CallRuntime(function, function->nargs, save_doubles);
830 1568 : }
831 :
832 : // Convenience function: Same as above, but takes the fid instead.
833 336 : void CallRuntime(Runtime::FunctionId fid, int num_arguments,
834 : SaveFPRegsMode save_doubles = kDontSaveFPRegs) {
835 392 : CallRuntime(Runtime::FunctionForId(fid), num_arguments, save_doubles);
836 336 : }
837 :
838 : // Convenience function: tail call a runtime routine (jump)
839 : void TailCallRuntime(Runtime::FunctionId fid);
840 :
841 : // Jump to a runtime routines
842 : void JumpToExternalReference(const ExternalReference& ext,
843 : bool builtin_exit_frame = false);
844 :
845 : // ---------------------------------------------------------------------------
846 : // StatsCounter support
847 : void IncrementCounter(StatsCounter* counter, int value);
848 : void DecrementCounter(StatsCounter* counter, int value);
849 :
850 : // ---------------------------------------------------------------------------
851 : // In-place weak references.
852 : void LoadWeakValue(Register in_out, Label* target_if_cleared);
853 :
854 : // ---------------------------------------------------------------------------
855 : // Debugging
856 :
857 : static int SafepointRegisterStackIndex(Register reg) {
858 : return SafepointRegisterStackIndex(reg.code());
859 : }
860 :
861 : private:
862 : // Order general registers are pushed by Pushad.
863 : // rax, rcx, rdx, rbx, rsi, rdi, r8, r9, r11, r12, r14, r15.
864 : static const int kSafepointPushRegisterIndices[Register::kNumRegisters];
865 : static const int kNumSafepointSavedRegisters = 12;
866 :
867 : // Helper functions for generating invokes.
868 : void InvokePrologue(const ParameterCount& expected,
869 : const ParameterCount& actual, Label* done,
870 : bool* definitely_mismatches, InvokeFlag flag,
871 : Label::Distance near_jump);
872 :
873 : void EnterExitFramePrologue(bool save_rax, StackFrame::Type frame_type);
874 :
875 : // Allocates arg_stack_space * kSystemPointerSize memory (not GCed) on the
876 : // stack accessible via StackSpaceOperand.
877 : void EnterExitFrameEpilogue(int arg_stack_space, bool save_doubles);
878 :
879 : void LeaveExitFrameEpilogue();
880 :
881 : // Compute memory operands for safepoint stack slots.
882 : static int SafepointRegisterStackIndex(int reg_code) {
883 0 : return kNumSafepointRegisters - kSafepointPushRegisterIndices[reg_code] - 1;
884 : }
885 :
886 : // Needs access to SafepointRegisterStackIndex for compiled frame
887 : // traversal.
888 : friend class StandardFrame;
889 :
890 : DISALLOW_IMPLICIT_CONSTRUCTORS(MacroAssembler);
891 : };
892 :
893 : // -----------------------------------------------------------------------------
894 : // Static helper functions.
895 :
896 : // Generate an Operand for loading a field from an object.
897 5320 : inline Operand FieldOperand(Register object, int offset) {
898 567620 : return Operand(object, offset - kHeapObjectTag);
899 : }
900 :
901 :
902 : // Generate an Operand for loading an indexed field from an object.
903 336 : inline Operand FieldOperand(Register object,
904 : Register index,
905 : ScaleFactor scale,
906 : int offset) {
907 9095 : return Operand(object, index, scale, offset - kHeapObjectTag);
908 : }
909 :
910 :
911 : inline Operand ContextOperand(Register context, int index) {
912 672 : return Operand(context, Context::SlotOffset(index));
913 : }
914 :
915 :
916 : inline Operand ContextOperand(Register context, Register index) {
917 : return Operand(context, index, times_system_pointer_size,
918 : Context::SlotOffset(0));
919 : }
920 :
921 :
922 336 : inline Operand NativeContextOperand() {
923 336 : return ContextOperand(rsi, Context::NATIVE_CONTEXT_INDEX);
924 : }
925 :
926 :
927 : // Provides access to exit frame stack space (not GCed).
928 392 : inline Operand StackSpaceOperand(int index) {
929 : #ifdef _WIN64
930 : const int kShaddowSpace = 4;
931 : return Operand(rsp, (index + kShaddowSpace) * kSystemPointerSize);
932 : #else
933 392 : return Operand(rsp, index * kSystemPointerSize);
934 : #endif
935 : }
936 :
937 :
938 56 : inline Operand StackOperandForReturnAddress(int32_t disp) {
939 56 : return Operand(rsp, disp);
940 : }
941 :
942 : #define ACCESS_MASM(masm) masm->
943 :
944 : } // namespace internal
945 : } // namespace v8
946 :
947 : #endif // V8_X64_MACRO_ASSEMBLER_X64_H_
|