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_CODEGEN_IA32_MACRO_ASSEMBLER_IA32_H_
10 #define V8_CODEGEN_IA32_MACRO_ASSEMBLER_IA32_H_
11
12 #include "src/codegen/assembler.h"
13 #include "src/codegen/bailout-reason.h"
14 #include "src/codegen/ia32/assembler-ia32.h"
15 #include "src/common/globals.h"
16
17 namespace v8 {
18 namespace internal {
19
20 // Convenience for platform-independent signatures. We do not normally
21 // distinguish memory operands from other operands on ia32.
22 using MemOperand = Operand;
23
24 enum RememberedSetAction { EMIT_REMEMBERED_SET, OMIT_REMEMBERED_SET };
25 enum SmiCheck { INLINE_SMI_CHECK, OMIT_SMI_CHECK };
26
27 // TODO(victorgomes): Move definition to macro-assembler.h, once all other
28 // platforms are updated.
29 enum class StackLimitKind { kInterruptStackLimit, kRealStackLimit };
30
31 // Convenient class to access arguments below the stack pointer.
32 class StackArgumentsAccessor {
33 public:
34 // argc = the number of arguments not including the receiver.
StackArgumentsAccessor(Register argc)35 explicit StackArgumentsAccessor(Register argc) : argc_(argc) {
36 DCHECK_NE(argc_, no_reg);
37 }
38
39 // Argument 0 is the receiver (despite argc not including the receiver).
40 Operand operator[](int index) const { return GetArgumentOperand(index); }
41
42 Operand GetArgumentOperand(int index) const;
GetReceiverOperand()43 Operand GetReceiverOperand() const { return GetArgumentOperand(0); }
44
45 private:
46 const Register argc_;
47
48 DISALLOW_IMPLICIT_CONSTRUCTORS(StackArgumentsAccessor);
49 };
50
51 class V8_EXPORT_PRIVATE TurboAssembler : public TurboAssemblerBase {
52 public:
53 using TurboAssemblerBase::TurboAssemblerBase;
54
55 void CheckPageFlag(Register object, Register scratch, int mask, Condition cc,
56 Label* condition_met,
57 Label::Distance condition_met_distance = Label::kFar);
58
59 // Activation support.
60 void EnterFrame(StackFrame::Type type);
EnterFrame(StackFrame::Type type,bool load_constant_pool_pointer_reg)61 void EnterFrame(StackFrame::Type type, bool load_constant_pool_pointer_reg) {
62 // Out-of-line constant pool not implemented on ia32.
63 UNREACHABLE();
64 }
65 void LeaveFrame(StackFrame::Type type);
66
67 // Allocate stack space of given size (i.e. decrement {esp} by the value
68 // stored in the given register, or by a constant). If you need to perform a
69 // stack check, do it before calling this function because this function may
70 // write into the newly allocated space. It may also overwrite the given
71 // register's value, in the version that takes a register.
72 #ifdef V8_OS_WIN
73 void AllocateStackSpace(Register bytes_scratch);
74 void AllocateStackSpace(int bytes);
75 #else
AllocateStackSpace(Register bytes)76 void AllocateStackSpace(Register bytes) { sub(esp, bytes); }
AllocateStackSpace(int bytes)77 void AllocateStackSpace(int bytes) { sub(esp, Immediate(bytes)); }
78 #endif
79
80 // Print a message to stdout and abort execution.
81 void Abort(AbortReason reason);
82
83 // Calls Abort(msg) if the condition cc is not satisfied.
84 // Use --debug_code to enable.
85 void Assert(Condition cc, AbortReason reason);
86
87 // Like Assert(), but without condition.
88 // Use --debug_code to enable.
89 void AssertUnreachable(AbortReason reason);
90
91 // Like Assert(), but always enabled.
92 void Check(Condition cc, AbortReason reason);
93
94 // Check that the stack is aligned.
95 void CheckStackAlignment();
96
97 // Move a constant into a destination using the most efficient encoding.
98 void Move(Register dst, const Immediate& src);
Move(Register dst,Smi src)99 void Move(Register dst, Smi src) { Move(dst, Immediate(src)); }
100 void Move(Register dst, Handle<HeapObject> src);
101 void Move(Register dst, Register src);
102 void Move(Operand dst, const Immediate& src);
103
104 // Move an immediate into an XMM register.
105 void Move(XMMRegister dst, uint32_t src);
106 void Move(XMMRegister dst, uint64_t src);
Move(XMMRegister dst,float src)107 void Move(XMMRegister dst, float src) { Move(dst, bit_cast<uint32_t>(src)); }
Move(XMMRegister dst,double src)108 void Move(XMMRegister dst, double src) { Move(dst, bit_cast<uint64_t>(src)); }
109
Call(Register reg)110 void Call(Register reg) { call(reg); }
Call(Label * target)111 void Call(Label* target) { call(target); }
112 void Call(Handle<Code> code_object, RelocInfo::Mode rmode);
113
114 // Load the builtin given by the Smi in |builtin_index| into the same
115 // register.
116 void LoadEntryFromBuiltinIndex(Register builtin_index);
117 void CallBuiltinByIndex(Register builtin_index) override;
118 void CallBuiltin(int builtin_index);
119
120 void LoadCodeObjectEntry(Register destination, Register code_object) override;
121 void CallCodeObject(Register code_object) override;
122 void JumpCodeObject(Register code_object) override;
123 void Jump(const ExternalReference& reference) override;
124
125 void RetpolineCall(Register reg);
126 void RetpolineCall(Address destination, RelocInfo::Mode rmode);
127
128 void Jump(Handle<Code> code_object, RelocInfo::Mode rmode);
129
130 void LoadMap(Register destination, Register object);
131
132 void RetpolineJump(Register reg);
133
134 void Trap() override;
135 void DebugBreak() override;
136
137 void CallForDeoptimization(Builtins::Name target, int deopt_id, Label* exit,
138 DeoptimizeKind kind,
139 Label* jump_deoptimization_entry_label);
140
141 // Jump the register contains a smi.
142 inline void JumpIfSmi(Register value, Label* smi_label,
143 Label::Distance distance = Label::kFar) {
144 test(value, Immediate(kSmiTagMask));
145 j(zero, smi_label, distance);
146 }
147 // Jump if the operand is a smi.
148 inline void JumpIfSmi(Operand value, Label* smi_label,
149 Label::Distance distance = Label::kFar) {
150 test(value, Immediate(kSmiTagMask));
151 j(zero, smi_label, distance);
152 }
153
JumpIfEqual(Register a,int32_t b,Label * dest)154 void JumpIfEqual(Register a, int32_t b, Label* dest) {
155 cmp(a, Immediate(b));
156 j(equal, dest);
157 }
158
JumpIfLessThan(Register a,int32_t b,Label * dest)159 void JumpIfLessThan(Register a, int32_t b, Label* dest) {
160 cmp(a, Immediate(b));
161 j(less, dest);
162 }
163
SmiUntag(Register reg)164 void SmiUntag(Register reg) { sar(reg, kSmiTagSize); }
165
166 // Removes current frame and its arguments from the stack preserving the
167 // arguments and a return address pushed to the stack for the next call. Both
168 // |callee_args_count| and |caller_args_count| do not include receiver.
169 // |callee_args_count| is not modified. |caller_args_count| is trashed.
170 // |number_of_temp_values_after_return_address| specifies the number of words
171 // pushed to the stack after the return address. This is to allow "allocation"
172 // of scratch registers that this function requires by saving their values on
173 // the stack.
174 void PrepareForTailCall(Register callee_args_count,
175 Register caller_args_count, Register scratch0,
176 Register scratch1,
177 int number_of_temp_values_after_return_address);
178
179 // Before calling a C-function from generated code, align arguments on stack.
180 // After aligning the frame, arguments must be stored in esp[0], esp[4],
181 // etc., not pushed. The argument count assumes all arguments are word sized.
182 // Some compilers/platforms require the stack to be aligned when calling
183 // C++ code.
184 // Needs a scratch register to do some arithmetic. This register will be
185 // trashed.
186 void PrepareCallCFunction(int num_arguments, Register scratch);
187
188 // Calls a C function and cleans up the space for arguments allocated
189 // by PrepareCallCFunction. The called function is not allowed to trigger a
190 // garbage collection, since that might move the code and invalidate the
191 // return address (unless this is somehow accounted for by the called
192 // function).
193 void CallCFunction(ExternalReference function, int num_arguments);
194 void CallCFunction(Register function, int num_arguments);
195
196 void ShlPair(Register high, Register low, uint8_t imm8);
197 void ShlPair_cl(Register high, Register low);
198 void ShrPair(Register high, Register low, uint8_t imm8);
199 void ShrPair_cl(Register high, Register low);
200 void SarPair(Register high, Register low, uint8_t imm8);
201 void SarPair_cl(Register high, Register low);
202
203 // Generates function and stub prologue code.
204 void StubPrologue(StackFrame::Type type);
205 void Prologue();
206
Lzcnt(Register dst,Register src)207 void Lzcnt(Register dst, Register src) { Lzcnt(dst, Operand(src)); }
208 void Lzcnt(Register dst, Operand src);
209
Tzcnt(Register dst,Register src)210 void Tzcnt(Register dst, Register src) { Tzcnt(dst, Operand(src)); }
211 void Tzcnt(Register dst, Operand src);
212
Popcnt(Register dst,Register src)213 void Popcnt(Register dst, Register src) { Popcnt(dst, Operand(src)); }
214 void Popcnt(Register dst, Operand src);
215
PushReturnAddressFrom(Register src)216 void PushReturnAddressFrom(Register src) { push(src); }
PopReturnAddressTo(Register dst)217 void PopReturnAddressTo(Register dst) { pop(dst); }
218
219 void Ret();
220
221 // Root register utility functions.
222
223 void InitializeRootRegister();
224
225 void LoadRoot(Register destination, RootIndex index) override;
226
227 // Indirect root-relative loads.
228 void LoadFromConstantsTable(Register destination,
229 int constant_index) override;
230 void LoadRootRegisterOffset(Register destination, intptr_t offset) override;
231 void LoadRootRelative(Register destination, int32_t offset) override;
232
233 void PushPC();
234
235 enum class PushArrayOrder { kNormal, kReverse };
236 // `array` points to the first element (the lowest address).
237 // `array` and `size` are not modified.
238 void PushArray(Register array, Register size, Register scratch,
239 PushArrayOrder order = PushArrayOrder::kNormal);
240
241 // Operand pointing to an external reference.
242 // May emit code to set up the scratch register. The operand is
243 // only guaranteed to be correct as long as the scratch register
244 // isn't changed.
245 // If the operand is used more than once, use a scratch register
246 // that is guaranteed not to be clobbered.
247 Operand ExternalReferenceAsOperand(ExternalReference reference,
248 Register scratch);
249 Operand ExternalReferenceAddressAsOperand(ExternalReference reference);
250 Operand HeapObjectAsOperand(Handle<HeapObject> object);
251
252 void LoadAddress(Register destination, ExternalReference source);
253
254 void CompareRoot(Register with, RootIndex index);
255 void CompareRoot(Register with, Register scratch, RootIndex index);
256
257 // Return and drop arguments from stack, where the number of arguments
258 // may be bigger than 2^16 - 1. Requires a scratch register.
259 void Ret(int bytes_dropped, Register scratch);
260
Pshufhw(XMMRegister dst,XMMRegister src,uint8_t shuffle)261 void Pshufhw(XMMRegister dst, XMMRegister src, uint8_t shuffle) {
262 Pshufhw(dst, Operand(src), shuffle);
263 }
264 void Pshufhw(XMMRegister dst, Operand src, uint8_t shuffle);
Pshuflw(XMMRegister dst,XMMRegister src,uint8_t shuffle)265 void Pshuflw(XMMRegister dst, XMMRegister src, uint8_t shuffle) {
266 Pshuflw(dst, Operand(src), shuffle);
267 }
268 void Pshuflw(XMMRegister dst, Operand src, uint8_t shuffle);
Pshufd(XMMRegister dst,XMMRegister src,uint8_t shuffle)269 void Pshufd(XMMRegister dst, XMMRegister src, uint8_t shuffle) {
270 Pshufd(dst, Operand(src), shuffle);
271 }
272 void Pshufd(XMMRegister dst, Operand src, uint8_t shuffle);
273 void Psraw(XMMRegister dst, uint8_t shift);
274 void Psrlw(XMMRegister dst, uint8_t shift);
275 void Psrlq(XMMRegister dst, uint8_t shift);
276
277 // SSE/SSE2 instructions with AVX version.
278 #define AVX_OP2_WITH_TYPE(macro_name, name, dst_type, src_type) \
279 void macro_name(dst_type dst, src_type src) { \
280 if (CpuFeatures::IsSupported(AVX)) { \
281 CpuFeatureScope scope(this, AVX); \
282 v##name(dst, src); \
283 } else { \
284 name(dst, src); \
285 } \
286 }
287
AVX_OP2_WITH_TYPE(Rcpps,rcpps,XMMRegister,const Operand &)288 AVX_OP2_WITH_TYPE(Rcpps, rcpps, XMMRegister, const Operand&)
289 AVX_OP2_WITH_TYPE(Rsqrtps, rsqrtps, XMMRegister, const Operand&)
290 AVX_OP2_WITH_TYPE(Movdqu, movdqu, XMMRegister, Operand)
291 AVX_OP2_WITH_TYPE(Movdqu, movdqu, Operand, XMMRegister)
292 AVX_OP2_WITH_TYPE(Movd, movd, XMMRegister, Register)
293 AVX_OP2_WITH_TYPE(Movd, movd, XMMRegister, Operand)
294 AVX_OP2_WITH_TYPE(Movd, movd, Register, XMMRegister)
295 AVX_OP2_WITH_TYPE(Movd, movd, Operand, XMMRegister)
296 AVX_OP2_WITH_TYPE(Cvtdq2ps, cvtdq2ps, XMMRegister, Operand)
297 AVX_OP2_WITH_TYPE(Cvtdq2ps, cvtdq2ps, XMMRegister, XMMRegister)
298 AVX_OP2_WITH_TYPE(Cvttps2dq, cvttps2dq, XMMRegister, XMMRegister)
299 AVX_OP2_WITH_TYPE(Sqrtps, sqrtps, XMMRegister, XMMRegister)
300 AVX_OP2_WITH_TYPE(Sqrtpd, sqrtpd, XMMRegister, XMMRegister)
301 AVX_OP2_WITH_TYPE(Sqrtpd, sqrtpd, XMMRegister, const Operand&)
302 AVX_OP2_WITH_TYPE(Movaps, movaps, XMMRegister, XMMRegister)
303 AVX_OP2_WITH_TYPE(Movapd, movapd, XMMRegister, XMMRegister)
304 AVX_OP2_WITH_TYPE(Movapd, movapd, XMMRegister, const Operand&)
305 AVX_OP2_WITH_TYPE(Movupd, movupd, XMMRegister, const Operand&)
306 AVX_OP2_WITH_TYPE(Pmovmskb, pmovmskb, Register, XMMRegister)
307 AVX_OP2_WITH_TYPE(Movmskps, movmskps, Register, XMMRegister)
308
309 #undef AVX_OP2_WITH_TYPE
310
311 // Only use these macros when non-destructive source of AVX version is not
312 // needed.
313 #define AVX_OP3_WITH_TYPE(macro_name, name, dst_type, src_type) \
314 void macro_name(dst_type dst, src_type src) { \
315 if (CpuFeatures::IsSupported(AVX)) { \
316 CpuFeatureScope scope(this, AVX); \
317 v##name(dst, dst, src); \
318 } else { \
319 name(dst, src); \
320 } \
321 }
322 #define AVX_OP3_XO(macro_name, name) \
323 AVX_OP3_WITH_TYPE(macro_name, name, XMMRegister, XMMRegister) \
324 AVX_OP3_WITH_TYPE(macro_name, name, XMMRegister, Operand)
325
326 AVX_OP3_XO(Packsswb, packsswb)
327 AVX_OP3_XO(Packuswb, packuswb)
328 AVX_OP3_XO(Paddusb, paddusb)
329 AVX_OP3_XO(Pand, pand)
330 AVX_OP3_XO(Pcmpeqb, pcmpeqb)
331 AVX_OP3_XO(Pcmpeqw, pcmpeqw)
332 AVX_OP3_XO(Pcmpeqd, pcmpeqd)
333 AVX_OP3_XO(Por, por)
334 AVX_OP3_XO(Psubb, psubb)
335 AVX_OP3_XO(Psubw, psubw)
336 AVX_OP3_XO(Psubd, psubd)
337 AVX_OP3_XO(Psubq, psubq)
338 AVX_OP3_XO(Punpcklbw, punpcklbw)
339 AVX_OP3_XO(Punpckhbw, punpckhbw)
340 AVX_OP3_XO(Punpckldq, punpckldq)
341 AVX_OP3_XO(Punpcklqdq, punpcklqdq)
342 AVX_OP3_XO(Pxor, pxor)
343 AVX_OP3_XO(Andps, andps)
344 AVX_OP3_XO(Andnps, andnps)
345 AVX_OP3_XO(Andpd, andpd)
346 AVX_OP3_XO(Xorps, xorps)
347 AVX_OP3_XO(Xorpd, xorpd)
348 AVX_OP3_XO(Sqrtss, sqrtss)
349 AVX_OP3_XO(Sqrtsd, sqrtsd)
350 AVX_OP3_XO(Orps, orps)
351 AVX_OP3_XO(Orpd, orpd)
352 AVX_OP3_XO(Andnpd, andnpd)
353
354 #undef AVX_OP3_XO
355 #undef AVX_OP3_WITH_TYPE
356
357 // Only use this macro when dst and src1 is the same in SSE case.
358 #define AVX_PACKED_OP3_WITH_TYPE(macro_name, name, dst_type, src_type) \
359 void macro_name(dst_type dst, dst_type src1, src_type src2) { \
360 if (CpuFeatures::IsSupported(AVX)) { \
361 CpuFeatureScope scope(this, AVX); \
362 v##name(dst, src1, src2); \
363 } else { \
364 DCHECK_EQ(dst, src1); \
365 name(dst, src2); \
366 } \
367 }
368 #define AVX_PACKED_OP3(macro_name, name) \
369 AVX_PACKED_OP3_WITH_TYPE(macro_name, name, XMMRegister, XMMRegister) \
370 AVX_PACKED_OP3_WITH_TYPE(macro_name, name, XMMRegister, Operand)
371
372 AVX_PACKED_OP3(Addps, addps)
373 AVX_PACKED_OP3(Addpd, addpd)
374 AVX_PACKED_OP3(Subps, subps)
375 AVX_PACKED_OP3(Subpd, subpd)
376 AVX_PACKED_OP3(Mulpd, mulpd)
377 AVX_PACKED_OP3(Divpd, divpd)
378 AVX_PACKED_OP3(Cmpeqpd, cmpeqpd)
379 AVX_PACKED_OP3(Cmpneqpd, cmpneqpd)
380 AVX_PACKED_OP3(Cmpltpd, cmpltpd)
381 AVX_PACKED_OP3(Cmpleps, cmpleps)
382 AVX_PACKED_OP3(Cmplepd, cmplepd)
383 AVX_PACKED_OP3(Minps, minps)
384 AVX_PACKED_OP3(Minpd, minpd)
385 AVX_PACKED_OP3(Maxps, maxps)
386 AVX_PACKED_OP3(Maxpd, maxpd)
387 AVX_PACKED_OP3(Cmpunordps, cmpunordps)
388 AVX_PACKED_OP3(Cmpunordpd, cmpunordpd)
389 AVX_PACKED_OP3(Psllw, psllw)
390 AVX_PACKED_OP3(Pslld, pslld)
391 AVX_PACKED_OP3(Psllq, psllq)
392 AVX_PACKED_OP3(Psrlw, psrlw)
393 AVX_PACKED_OP3(Psrld, psrld)
394 AVX_PACKED_OP3(Psrlq, psrlq)
395 AVX_PACKED_OP3(Psraw, psraw)
396 AVX_PACKED_OP3(Psrad, psrad)
397 AVX_PACKED_OP3(Pmaddwd, pmaddwd)
398 AVX_PACKED_OP3(Paddd, paddd)
399 AVX_PACKED_OP3(Paddq, paddq)
400 AVX_PACKED_OP3(Psubq, psubq)
401 AVX_PACKED_OP3(Pmuludq, pmuludq)
402 AVX_PACKED_OP3(Pavgb, pavgb)
403 AVX_PACKED_OP3(Pavgw, pavgw)
404 #undef AVX_PACKED_OP3
405
406 AVX_PACKED_OP3_WITH_TYPE(Psllw, psllw, XMMRegister, uint8_t)
407 AVX_PACKED_OP3_WITH_TYPE(Pslld, pslld, XMMRegister, uint8_t)
408 AVX_PACKED_OP3_WITH_TYPE(Psllq, psllq, XMMRegister, uint8_t)
409 AVX_PACKED_OP3_WITH_TYPE(Psrlw, psrlw, XMMRegister, uint8_t)
410 AVX_PACKED_OP3_WITH_TYPE(Psrld, psrld, XMMRegister, uint8_t)
411 AVX_PACKED_OP3_WITH_TYPE(Psrlq, psrlq, XMMRegister, uint8_t)
412 AVX_PACKED_OP3_WITH_TYPE(Psraw, psraw, XMMRegister, uint8_t)
413 AVX_PACKED_OP3_WITH_TYPE(Psrad, psrad, XMMRegister, uint8_t)
414 #undef AVX_PACKED_OP3_WITH_TYPE
415
416 // Non-SSE2 instructions.
417 #define AVX_OP2_WITH_TYPE_SCOPE(macro_name, name, dst_type, src_type, \
418 sse_scope) \
419 void macro_name(dst_type dst, src_type src) { \
420 if (CpuFeatures::IsSupported(AVX)) { \
421 CpuFeatureScope scope(this, AVX); \
422 v##name(dst, src); \
423 return; \
424 } \
425 if (CpuFeatures::IsSupported(sse_scope)) { \
426 CpuFeatureScope scope(this, sse_scope); \
427 name(dst, src); \
428 return; \
429 } \
430 UNREACHABLE(); \
431 }
432 #define AVX_OP2_XO_SSE3(macro_name, name) \
433 AVX_OP2_WITH_TYPE_SCOPE(macro_name, name, XMMRegister, XMMRegister, SSE3) \
434 AVX_OP2_WITH_TYPE_SCOPE(macro_name, name, XMMRegister, Operand, SSE3)
435 AVX_OP2_XO_SSE3(Movddup, movddup)
436
437 #undef AVX_OP2_XO_SSE3
438
439 #define AVX_OP2_XO_SSSE3(macro_name, name) \
440 AVX_OP2_WITH_TYPE_SCOPE(macro_name, name, XMMRegister, XMMRegister, SSSE3) \
441 AVX_OP2_WITH_TYPE_SCOPE(macro_name, name, XMMRegister, Operand, SSSE3)
442 AVX_OP2_XO_SSSE3(Pabsb, pabsb)
443 AVX_OP2_XO_SSSE3(Pabsw, pabsw)
444 AVX_OP2_XO_SSSE3(Pabsd, pabsd)
445
446 #undef AVX_OP2_XO_SSE3
447
448 #define AVX_OP2_XO_SSE4(macro_name, name) \
449 AVX_OP2_WITH_TYPE_SCOPE(macro_name, name, XMMRegister, XMMRegister, SSE4_1) \
450 AVX_OP2_WITH_TYPE_SCOPE(macro_name, name, XMMRegister, Operand, SSE4_1)
451
452 AVX_OP2_XO_SSE4(Ptest, ptest)
453 AVX_OP2_XO_SSE4(Pmovsxbw, pmovsxbw)
454 AVX_OP2_XO_SSE4(Pmovsxwd, pmovsxwd)
455 AVX_OP2_XO_SSE4(Pmovsxdq, pmovsxdq)
456 AVX_OP2_XO_SSE4(Pmovzxbw, pmovzxbw)
457 AVX_OP2_XO_SSE4(Pmovzxwd, pmovzxwd)
458 AVX_OP2_XO_SSE4(Pmovzxdq, pmovzxdq)
459
460 #undef AVX_OP2_WITH_TYPE_SCOPE
461 #undef AVX_OP2_XO_SSE4
462
463 #define AVX_OP3_WITH_TYPE_SCOPE(macro_name, name, dst_type, src_type, \
464 sse_scope) \
465 void macro_name(dst_type dst, src_type src) { \
466 if (CpuFeatures::IsSupported(AVX)) { \
467 CpuFeatureScope scope(this, AVX); \
468 v##name(dst, dst, src); \
469 return; \
470 } \
471 if (CpuFeatures::IsSupported(sse_scope)) { \
472 CpuFeatureScope scope(this, sse_scope); \
473 name(dst, src); \
474 return; \
475 } \
476 UNREACHABLE(); \
477 }
478 #define AVX_OP3_XO_SSE4(macro_name, name) \
479 AVX_OP3_WITH_TYPE_SCOPE(macro_name, name, XMMRegister, XMMRegister, SSE4_1) \
480 AVX_OP3_WITH_TYPE_SCOPE(macro_name, name, XMMRegister, Operand, SSE4_1)
481
482 AVX_OP3_XO_SSE4(Pmaxsd, pmaxsd)
483
484 #undef AVX_OP3_XO_SSE4
485 #undef AVX_OP3_WITH_TYPE_SCOPE
486
487 void Pshufb(XMMRegister dst, XMMRegister src) { Pshufb(dst, dst, src); }
Pshufb(XMMRegister dst,Operand src)488 void Pshufb(XMMRegister dst, Operand src) { Pshufb(dst, dst, src); }
489 // Handles SSE and AVX. On SSE, moves src to dst if they are not equal.
Pshufb(XMMRegister dst,XMMRegister src,XMMRegister mask)490 void Pshufb(XMMRegister dst, XMMRegister src, XMMRegister mask) {
491 Pshufb(dst, src, Operand(mask));
492 }
493 void Pshufb(XMMRegister dst, XMMRegister src, Operand mask);
Pblendw(XMMRegister dst,XMMRegister src,uint8_t imm8)494 void Pblendw(XMMRegister dst, XMMRegister src, uint8_t imm8) {
495 Pblendw(dst, Operand(src), imm8);
496 }
497 void Pblendw(XMMRegister dst, Operand src, uint8_t imm8);
498
Psignb(XMMRegister dst,XMMRegister src)499 void Psignb(XMMRegister dst, XMMRegister src) { Psignb(dst, Operand(src)); }
500 void Psignb(XMMRegister dst, Operand src);
Psignw(XMMRegister dst,XMMRegister src)501 void Psignw(XMMRegister dst, XMMRegister src) { Psignw(dst, Operand(src)); }
502 void Psignw(XMMRegister dst, Operand src);
Psignd(XMMRegister dst,XMMRegister src)503 void Psignd(XMMRegister dst, XMMRegister src) { Psignd(dst, Operand(src)); }
504 void Psignd(XMMRegister dst, Operand src);
505
Palignr(XMMRegister dst,XMMRegister src,uint8_t imm8)506 void Palignr(XMMRegister dst, XMMRegister src, uint8_t imm8) {
507 Palignr(dst, Operand(src), imm8);
508 }
509 void Palignr(XMMRegister dst, Operand src, uint8_t imm8);
510
511 void Pextrb(Register dst, XMMRegister src, uint8_t imm8);
512 void Pextrw(Register dst, XMMRegister src, uint8_t imm8);
513 void Pextrd(Register dst, XMMRegister src, uint8_t imm8);
Pinsrb(XMMRegister dst,Register src,int8_t imm8)514 void Pinsrb(XMMRegister dst, Register src, int8_t imm8) {
515 Pinsrb(dst, Operand(src), imm8);
516 }
517 void Pinsrb(XMMRegister dst, Operand src, int8_t imm8);
Pinsrd(XMMRegister dst,Register src,uint8_t imm8)518 void Pinsrd(XMMRegister dst, Register src, uint8_t imm8) {
519 Pinsrd(dst, Operand(src), imm8);
520 }
521 void Pinsrd(XMMRegister dst, Operand src, uint8_t imm8);
Pinsrw(XMMRegister dst,Register src,int8_t imm8)522 void Pinsrw(XMMRegister dst, Register src, int8_t imm8) {
523 Pinsrw(dst, Operand(src), imm8);
524 }
525 void Pinsrw(XMMRegister dst, Operand src, int8_t imm8);
526 void Vbroadcastss(XMMRegister dst, Operand src);
527
528 // Expression support
529 // cvtsi2sd instruction only writes to the low 64-bit of dst register, which
530 // hinders register renaming and makes dependence chains longer. So we use
531 // xorps to clear the dst register before cvtsi2sd to solve this issue.
Cvtsi2ss(XMMRegister dst,Register src)532 void Cvtsi2ss(XMMRegister dst, Register src) { Cvtsi2ss(dst, Operand(src)); }
533 void Cvtsi2ss(XMMRegister dst, Operand src);
Cvtsi2sd(XMMRegister dst,Register src)534 void Cvtsi2sd(XMMRegister dst, Register src) { Cvtsi2sd(dst, Operand(src)); }
535 void Cvtsi2sd(XMMRegister dst, Operand src);
536
Cvtui2ss(XMMRegister dst,Register src,Register tmp)537 void Cvtui2ss(XMMRegister dst, Register src, Register tmp) {
538 Cvtui2ss(dst, Operand(src), tmp);
539 }
540 void Cvtui2ss(XMMRegister dst, Operand src, Register tmp);
Cvttss2ui(Register dst,XMMRegister src,XMMRegister tmp)541 void Cvttss2ui(Register dst, XMMRegister src, XMMRegister tmp) {
542 Cvttss2ui(dst, Operand(src), tmp);
543 }
544 void Cvttss2ui(Register dst, Operand src, XMMRegister tmp);
Cvtui2sd(XMMRegister dst,Register src,Register scratch)545 void Cvtui2sd(XMMRegister dst, Register src, Register scratch) {
546 Cvtui2sd(dst, Operand(src), scratch);
547 }
548 void Cvtui2sd(XMMRegister dst, Operand src, Register scratch);
Cvttsd2ui(Register dst,XMMRegister src,XMMRegister tmp)549 void Cvttsd2ui(Register dst, XMMRegister src, XMMRegister tmp) {
550 Cvttsd2ui(dst, Operand(src), tmp);
551 }
552 void Cvttsd2ui(Register dst, Operand src, XMMRegister tmp);
553
554 void Roundps(XMMRegister dst, XMMRegister src, RoundingMode mode);
555 void Roundpd(XMMRegister dst, XMMRegister src, RoundingMode mode);
556
Push(Register src)557 void Push(Register src) { push(src); }
Push(Operand src)558 void Push(Operand src) { push(src); }
559 void Push(Immediate value);
Push(Handle<HeapObject> handle)560 void Push(Handle<HeapObject> handle) { push(Immediate(handle)); }
Push(Smi smi)561 void Push(Smi smi) { Push(Immediate(smi)); }
562
563 void SaveRegisters(RegList registers);
564 void RestoreRegisters(RegList registers);
565
566 void CallRecordWriteStub(Register object, Register address,
567 RememberedSetAction remembered_set_action,
568 SaveFPRegsMode fp_mode);
569 void CallRecordWriteStub(Register object, Register address,
570 RememberedSetAction remembered_set_action,
571 SaveFPRegsMode fp_mode, Address wasm_target);
572 void CallEphemeronKeyBarrier(Register object, Register address,
573 SaveFPRegsMode fp_mode);
574
575 // Calculate how much stack space (in bytes) are required to store caller
576 // registers excluding those specified in the arguments.
577 int RequiredStackSizeForCallerSaved(SaveFPRegsMode fp_mode,
578 Register exclusion1 = no_reg,
579 Register exclusion2 = no_reg,
580 Register exclusion3 = no_reg) const;
581
582 // PushCallerSaved and PopCallerSaved do not arrange the registers in any
583 // particular order so they are not useful for calls that can cause a GC.
584 // The caller can exclude up to 3 registers that do not need to be saved and
585 // restored.
586
587 // Push caller saved registers on the stack, and return the number of bytes
588 // stack pointer is adjusted.
589 int PushCallerSaved(SaveFPRegsMode fp_mode, Register exclusion1 = no_reg,
590 Register exclusion2 = no_reg,
591 Register exclusion3 = no_reg);
592 // Restore caller saved registers from the stack, and return the number of
593 // bytes stack pointer is adjusted.
594 int PopCallerSaved(SaveFPRegsMode fp_mode, Register exclusion1 = no_reg,
595 Register exclusion2 = no_reg,
596 Register exclusion3 = no_reg);
597
598 // Compute the start of the generated instruction stream from the current PC.
599 // This is an alternative to embedding the {CodeObject} handle as a reference.
600 void ComputeCodeStartAddress(Register dst);
601
602 // TODO(860429): Remove remaining poisoning infrastructure on ia32.
ResetSpeculationPoisonRegister()603 void ResetSpeculationPoisonRegister() { UNREACHABLE(); }
604
605 // Control-flow integrity:
606
607 // Define a function entrypoint. This doesn't emit any code for this
608 // architecture, as control-flow integrity is not supported for it.
CodeEntry()609 void CodeEntry() {}
610 // Define an exception handler.
ExceptionHandler()611 void ExceptionHandler() {}
612 // Define an exception handler and bind a label.
BindExceptionHandler(Label * label)613 void BindExceptionHandler(Label* label) { bind(label); }
614
615 void CallRecordWriteStub(Register object, Register address,
616 RememberedSetAction remembered_set_action,
617 SaveFPRegsMode fp_mode, Handle<Code> code_target,
618 Address wasm_target);
619 };
620
621 // MacroAssembler implements a collection of frequently used macros.
622 class V8_EXPORT_PRIVATE MacroAssembler : public TurboAssembler {
623 public:
624 using TurboAssembler::TurboAssembler;
625
626 // Load a register with a long value as efficiently as possible.
Set(Register dst,int32_t x)627 void Set(Register dst, int32_t x) {
628 if (x == 0) {
629 xor_(dst, dst);
630 } else {
631 mov(dst, Immediate(x));
632 }
633 }
634
635 void PushRoot(RootIndex index);
636
637 // Compare the object in a register to a value and jump if they are equal.
638 void JumpIfRoot(Register with, RootIndex index, Label* if_equal,
639 Label::Distance if_equal_distance = Label::kFar) {
640 CompareRoot(with, index);
641 j(equal, if_equal, if_equal_distance);
642 }
643
644 // Compare the object in a register to a value and jump if they are not equal.
645 void JumpIfNotRoot(Register with, RootIndex index, Label* if_not_equal,
646 Label::Distance if_not_equal_distance = Label::kFar) {
647 CompareRoot(with, index);
648 j(not_equal, if_not_equal, if_not_equal_distance);
649 }
650
651 // Checks if value is in range [lower_limit, higher_limit] using a single
652 // comparison.
653 void JumpIfIsInRange(Register value, unsigned lower_limit,
654 unsigned higher_limit, Register scratch,
655 Label* on_in_range,
656 Label::Distance near_jump = Label::kFar);
657
658 // ---------------------------------------------------------------------------
659 // GC Support
660 // Notify the garbage collector that we wrote a pointer into an object.
661 // |object| is the object being stored into, |value| is the object being
662 // stored. value and scratch registers are clobbered by the operation.
663 // The offset is the offset from the start of the object, not the offset from
664 // the tagged HeapObject pointer. For use with FieldOperand(reg, off).
665 void RecordWriteField(
666 Register object, int offset, Register value, Register scratch,
667 SaveFPRegsMode save_fp,
668 RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET,
669 SmiCheck smi_check = INLINE_SMI_CHECK);
670
671 // For page containing |object| mark region covering |address|
672 // dirty. |object| is the object being stored into, |value| is the
673 // object being stored. The address and value registers are clobbered by the
674 // operation. RecordWrite filters out smis so it does not update the
675 // write barrier if the value is a smi.
676 void RecordWrite(
677 Register object, Register address, Register value, SaveFPRegsMode save_fp,
678 RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET,
679 SmiCheck smi_check = INLINE_SMI_CHECK);
680
681 // Frame restart support
682 void MaybeDropFrames();
683
684 // Enter specific kind of exit frame. Expects the number of
685 // arguments in register eax and sets up the number of arguments in
686 // register edi and the pointer to the first argument in register
687 // esi.
688 void EnterExitFrame(int argc, bool save_doubles, StackFrame::Type frame_type);
689
690 void EnterApiExitFrame(int argc, Register scratch);
691
692 // Leave the current exit frame. Expects the return value in
693 // register eax:edx (untouched) and the pointer to the first
694 // argument in register esi (if pop_arguments == true).
695 void LeaveExitFrame(bool save_doubles, bool pop_arguments = true);
696
697 // Leave the current exit frame. Expects the return value in
698 // register eax (untouched).
699 void LeaveApiExitFrame();
700
701 // Load the global proxy from the current context.
702 void LoadGlobalProxy(Register dst);
703
704 // Load a value from the native context with a given index.
705 void LoadNativeContextSlot(Register dst, int index);
706
707 // ---------------------------------------------------------------------------
708 // JavaScript invokes
709
710 // Invoke the JavaScript function code by either calling or jumping.
711
712 void InvokeFunctionCode(Register function, Register new_target,
713 Register expected_parameter_count,
714 Register actual_parameter_count, InvokeFlag flag);
715
716 // On function call, call into the debugger.
717 // This may clobber ecx.
718 void CallDebugOnFunctionCall(Register fun, Register new_target,
719 Register expected_parameter_count,
720 Register actual_parameter_count);
721
722 // Invoke the JavaScript function in the given register. Changes the
723 // current context to the context in the function before invoking.
724 void InvokeFunction(Register function, Register new_target,
725 Register actual_parameter_count, InvokeFlag flag);
726
727 // Compare object type for heap object.
728 // Incoming register is heap_object and outgoing register is map.
729 void CmpObjectType(Register heap_object, InstanceType type, Register map);
730
731 // Compare instance type for map.
732 void CmpInstanceType(Register map, InstanceType type);
733
734 // Smi tagging support.
SmiTag(Register reg)735 void SmiTag(Register reg) {
736 STATIC_ASSERT(kSmiTag == 0);
737 STATIC_ASSERT(kSmiTagSize == 1);
738 add(reg, reg);
739 }
740
741 // Jump if register contain a non-smi.
742 inline void JumpIfNotSmi(Register value, Label* not_smi_label,
743 Label::Distance distance = Label::kFar) {
744 test(value, Immediate(kSmiTagMask));
745 j(not_zero, not_smi_label, distance);
746 }
747 // Jump if the operand is not a smi.
748 inline void JumpIfNotSmi(Operand value, Label* smi_label,
749 Label::Distance distance = Label::kFar) {
750 test(value, Immediate(kSmiTagMask));
751 j(not_zero, smi_label, distance);
752 }
753
754 template <typename Field>
DecodeField(Register reg)755 void DecodeField(Register reg) {
756 static const int shift = Field::kShift;
757 static const int mask = Field::kMask >> Field::kShift;
758 if (shift != 0) {
759 sar(reg, shift);
760 }
761 and_(reg, Immediate(mask));
762 }
763
764 // Abort execution if argument is not a smi, enabled via --debug-code.
765 void AssertSmi(Register object);
766
767 // Abort execution if argument is a smi, enabled via --debug-code.
768 void AssertNotSmi(Register object);
769
770 // Abort execution if argument is not a JSFunction, enabled via --debug-code.
771 void AssertFunction(Register object);
772
773 // Abort execution if argument is not a Constructor, enabled via --debug-code.
774 void AssertConstructor(Register object);
775
776 // Abort execution if argument is not a JSBoundFunction,
777 // enabled via --debug-code.
778 void AssertBoundFunction(Register object);
779
780 // Abort execution if argument is not a JSGeneratorObject (or subclass),
781 // enabled via --debug-code.
782 void AssertGeneratorObject(Register object);
783
784 // Abort execution if argument is not undefined or an AllocationSite, enabled
785 // via --debug-code.
786 void AssertUndefinedOrAllocationSite(Register object, Register scratch);
787
788 // ---------------------------------------------------------------------------
789 // Exception handling
790
791 // Push a new stack handler and link it into stack handler chain.
792 void PushStackHandler(Register scratch);
793
794 // Unlink the stack handler on top of the stack from the stack handler chain.
795 void PopStackHandler(Register scratch);
796
797 // ---------------------------------------------------------------------------
798 // Runtime calls
799
800 // Call a runtime routine.
801 void CallRuntime(const Runtime::Function* f, int num_arguments,
802 SaveFPRegsMode save_doubles = kDontSaveFPRegs);
803
804 // Convenience function: Same as above, but takes the fid instead.
805 void CallRuntime(Runtime::FunctionId fid,
806 SaveFPRegsMode save_doubles = kDontSaveFPRegs) {
807 const Runtime::Function* function = Runtime::FunctionForId(fid);
808 CallRuntime(function, function->nargs, save_doubles);
809 }
810
811 // Convenience function: Same as above, but takes the fid instead.
812 void CallRuntime(Runtime::FunctionId fid, int num_arguments,
813 SaveFPRegsMode save_doubles = kDontSaveFPRegs) {
814 CallRuntime(Runtime::FunctionForId(fid), num_arguments, save_doubles);
815 }
816
817 // Convenience function: tail call a runtime routine (jump).
818 void TailCallRuntime(Runtime::FunctionId fid);
819
820 // Jump to a runtime routine.
821 void JumpToExternalReference(const ExternalReference& ext,
822 bool builtin_exit_frame = false);
823
824 // Generates a trampoline to jump to the off-heap instruction stream.
825 void JumpToInstructionStream(Address entry);
826
827 // ---------------------------------------------------------------------------
828 // Utilities
829
830 // Emit code to discard a non-negative number of pointer-sized elements
831 // from the stack, clobbering only the esp register.
832 void Drop(int element_count);
833
Pop(Register dst)834 void Pop(Register dst) { pop(dst); }
Pop(Operand dst)835 void Pop(Operand dst) { pop(dst); }
836
837 // ---------------------------------------------------------------------------
838 // In-place weak references.
839 void LoadWeakValue(Register in_out, Label* target_if_cleared);
840
841 // ---------------------------------------------------------------------------
842 // StatsCounter support
843
844 void IncrementCounter(StatsCounter* counter, int value, Register scratch);
845 void DecrementCounter(StatsCounter* counter, int value, Register scratch);
846
847 // ---------------------------------------------------------------------------
848 // Stack limit utilities
849 void CompareStackLimit(Register with, StackLimitKind kind);
850 void StackOverflowCheck(Register num_args, Register scratch,
851 Label* stack_overflow, bool include_receiver = false);
852
SafepointRegisterStackIndex(Register reg)853 static int SafepointRegisterStackIndex(Register reg) {
854 return SafepointRegisterStackIndex(reg.code());
855 }
856
857 private:
858 // Helper functions for generating invokes.
859 void InvokePrologue(Register expected_parameter_count,
860 Register actual_parameter_count, Label* done,
861 InvokeFlag flag);
862
863 void EnterExitFramePrologue(StackFrame::Type frame_type, Register scratch);
864 void EnterExitFrameEpilogue(int argc, bool save_doubles);
865
866 void LeaveExitFrameEpilogue();
867
868 // Compute memory operands for safepoint stack slots.
869 static int SafepointRegisterStackIndex(int reg_code);
870
871 // Needs access to SafepointRegisterStackIndex for compiled frame
872 // traversal.
873 friend class CommonFrame;
874
875 DISALLOW_IMPLICIT_CONSTRUCTORS(MacroAssembler);
876 };
877
878 // -----------------------------------------------------------------------------
879 // Static helper functions.
880
881 // Generate an Operand for loading a field from an object.
FieldOperand(Register object,int offset)882 inline Operand FieldOperand(Register object, int offset) {
883 return Operand(object, offset - kHeapObjectTag);
884 }
885
886 // Generate an Operand for loading an indexed field from an object.
FieldOperand(Register object,Register index,ScaleFactor scale,int offset)887 inline Operand FieldOperand(Register object, Register index, ScaleFactor scale,
888 int offset) {
889 return Operand(object, index, scale, offset - kHeapObjectTag);
890 }
891
892 #define ACCESS_MASM(masm) masm->
893
894 } // namespace internal
895 } // namespace v8
896
897 #endif // V8_CODEGEN_IA32_MACRO_ASSEMBLER_IA32_H_
898