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