1 // Copyright 2014 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_PPC_MACRO_ASSEMBLER_PPC_H_
10 #define V8_CODEGEN_PPC_MACRO_ASSEMBLER_PPC_H_
11
12 #include "src/codegen/bailout-reason.h"
13 #include "src/codegen/ppc/assembler-ppc.h"
14 #include "src/common/globals.h"
15 #include "src/numbers/double.h"
16 #include "src/objects/contexts.h"
17
18 namespace v8 {
19 namespace internal {
20
21 // ----------------------------------------------------------------------------
22 // Static helper functions
23
24 // Generate a MemOperand for loading a field from an object.
FieldMemOperand(Register object,int offset)25 inline MemOperand FieldMemOperand(Register object, int offset) {
26 return MemOperand(object, offset - kHeapObjectTag);
27 }
28
29 enum RememberedSetAction { EMIT_REMEMBERED_SET, OMIT_REMEMBERED_SET };
30 enum SmiCheck { INLINE_SMI_CHECK, OMIT_SMI_CHECK };
31 enum LinkRegisterStatus { kLRHasNotBeenSaved, kLRHasBeenSaved };
32
33 Register GetRegisterThatIsNotOneOf(Register reg1, Register reg2 = no_reg,
34 Register reg3 = no_reg,
35 Register reg4 = no_reg,
36 Register reg5 = no_reg,
37 Register reg6 = no_reg);
38
39 // These exist to provide portability between 32 and 64bit
40 #if V8_TARGET_ARCH_PPC64
41 #define LoadPX ldx
42 #define LoadPUX ldux
43 #define StorePX stdx
44 #define StorePUX stdux
45 #define ShiftLeftImm sldi
46 #define ShiftRightImm srdi
47 #define ClearLeftImm clrldi
48 #define ClearRightImm clrrdi
49 #define ShiftRightArithImm sradi
50 #define ShiftLeft_ sld
51 #define ShiftRight_ srd
52 #define ShiftRightArith srad
53 #else
54 #define LoadPX lwzx
55 #define LoadPUX lwzux
56 #define StorePX stwx
57 #define StorePUX stwux
58 #define ShiftLeftImm slwi
59 #define ShiftRightImm srwi
60 #define ClearLeftImm clrlwi
61 #define ClearRightImm clrrwi
62 #define ShiftRightArithImm srawi
63 #define ShiftLeft_ slw
64 #define ShiftRight_ srw
65 #define ShiftRightArith sraw
66 #endif
67
68 class V8_EXPORT_PRIVATE TurboAssembler : public TurboAssemblerBase {
69 public:
70 using TurboAssemblerBase::TurboAssemblerBase;
71
72 // Converts the integer (untagged smi) in |src| to a double, storing
73 // the result to |dst|
74 void ConvertIntToDouble(Register src, DoubleRegister dst);
75
76 // Converts the unsigned integer (untagged smi) in |src| to
77 // a double, storing the result to |dst|
78 void ConvertUnsignedIntToDouble(Register src, DoubleRegister dst);
79
80 // Converts the integer (untagged smi) in |src| to
81 // a float, storing the result in |dst|
82 void ConvertIntToFloat(Register src, DoubleRegister dst);
83
84 // Converts the unsigned integer (untagged smi) in |src| to
85 // a float, storing the result in |dst|
86 void ConvertUnsignedIntToFloat(Register src, DoubleRegister dst);
87
88 #if V8_TARGET_ARCH_PPC64
89 void ConvertInt64ToFloat(Register src, DoubleRegister double_dst);
90 void ConvertInt64ToDouble(Register src, DoubleRegister double_dst);
91 void ConvertUnsignedInt64ToFloat(Register src, DoubleRegister double_dst);
92 void ConvertUnsignedInt64ToDouble(Register src, DoubleRegister double_dst);
93 #endif
94
95 // Converts the double_input to an integer. Note that, upon return,
96 // the contents of double_dst will also hold the fixed point representation.
97 void ConvertDoubleToInt64(const DoubleRegister double_input,
98 #if !V8_TARGET_ARCH_PPC64
99 const Register dst_hi,
100 #endif
101 const Register dst, const DoubleRegister double_dst,
102 FPRoundingMode rounding_mode = kRoundToZero);
103
104 #if V8_TARGET_ARCH_PPC64
105 // Converts the double_input to an unsigned integer. Note that, upon return,
106 // the contents of double_dst will also hold the fixed point representation.
107 void ConvertDoubleToUnsignedInt64(
108 const DoubleRegister double_input, const Register dst,
109 const DoubleRegister double_dst,
110 FPRoundingMode rounding_mode = kRoundToZero);
111 #endif
112
113 // Activation support.
114 void EnterFrame(StackFrame::Type type,
115 bool load_constant_pool_pointer_reg = false);
116
117 // Returns the pc offset at which the frame ends.
118 int LeaveFrame(StackFrame::Type type, int stack_adjustment = 0);
119
120 // Push a fixed frame, consisting of lr, fp, constant pool.
121 void PushCommonFrame(Register marker_reg = no_reg);
122
123 // Generates function and stub prologue code.
124 void StubPrologue(StackFrame::Type type);
125 void Prologue();
126
127 // Push a standard frame, consisting of lr, fp, constant pool,
128 // context and JS function
129 void PushStandardFrame(Register function_reg);
130
131 // Restore caller's frame pointer and return address prior to being
132 // overwritten by tail call stack preparation.
133 void RestoreFrameStateForTailCall();
134
135 // Get the actual activation frame alignment for target environment.
136 static int ActivationFrameAlignment();
137
InitializeRootRegister()138 void InitializeRootRegister() {
139 ExternalReference isolate_root = ExternalReference::isolate_root(isolate());
140 mov(kRootRegister, Operand(isolate_root));
141 }
142
143 // These exist to provide portability between 32 and 64bit
144 void LoadP(Register dst, const MemOperand& mem, Register scratch = no_reg);
145 void LoadPU(Register dst, const MemOperand& mem, Register scratch = no_reg);
146 void LoadWordArith(Register dst, const MemOperand& mem,
147 Register scratch = no_reg);
148 void StoreP(Register src, const MemOperand& mem, Register scratch = no_reg);
149 void StorePU(Register src, const MemOperand& mem, Register scratch = no_reg);
150
151 void LoadDouble(DoubleRegister dst, const MemOperand& mem,
152 Register scratch = no_reg);
153 void LoadFloat32(DoubleRegister dst, const MemOperand& mem,
154 Register scratch = no_reg);
155 void LoadDoubleLiteral(DoubleRegister result, Double value, Register scratch);
156 void LoadSimd128(Simd128Register dst, const MemOperand& mem,
157 Register ScratchReg, Simd128Register ScratchDoubleReg);
158
159 // load a literal signed int value <value> to GPR <dst>
160 void LoadIntLiteral(Register dst, int value);
161 // load an SMI value <value> to GPR <dst>
162 void LoadSmiLiteral(Register dst, Smi smi);
163
164 void LoadSingle(DoubleRegister dst, const MemOperand& mem,
165 Register scratch = no_reg);
166 void LoadSingleU(DoubleRegister dst, const MemOperand& mem,
167 Register scratch = no_reg);
168 void LoadPC(Register dst);
169 void ComputeCodeStartAddress(Register dst);
170
171 void StoreDouble(DoubleRegister src, const MemOperand& mem,
172 Register scratch = no_reg);
173 void StoreDoubleU(DoubleRegister src, const MemOperand& mem,
174 Register scratch = no_reg);
175
176 void StoreSingle(DoubleRegister src, const MemOperand& mem,
177 Register scratch = no_reg);
178 void StoreSingleU(DoubleRegister src, const MemOperand& mem,
179 Register scratch = no_reg);
180 void StoreSimd128(Simd128Register src, const MemOperand& mem,
181 Register ScratchReg, Simd128Register ScratchDoubleReg);
182
183 void Cmpi(Register src1, const Operand& src2, Register scratch,
184 CRegister cr = cr7);
185 void Cmpli(Register src1, const Operand& src2, Register scratch,
186 CRegister cr = cr7);
187 void Cmpwi(Register src1, const Operand& src2, Register scratch,
188 CRegister cr = cr7);
189 void CompareTagged(Register src1, Register src2, CRegister cr = cr7) {
190 if (COMPRESS_POINTERS_BOOL) {
191 cmpw(src1, src2, cr);
192 } else {
193 cmp(src1, src2, cr);
194 }
195 }
196
197 // Set new rounding mode RN to FPSCR
198 void SetRoundingMode(FPRoundingMode RN);
199
200 // reset rounding mode to default (kRoundToNearest)
201 void ResetRoundingMode();
202 void Add(Register dst, Register src, intptr_t value, Register scratch);
203
Push(Register src)204 void Push(Register src) { push(src); }
205 // Push a handle.
206 void Push(Handle<HeapObject> handle);
207 void Push(Smi smi);
208
209 // Push two registers. Pushes leftmost register first (to highest address).
Push(Register src1,Register src2)210 void Push(Register src1, Register src2) {
211 StorePU(src2, MemOperand(sp, -2 * kSystemPointerSize));
212 StoreP(src1, MemOperand(sp, kSystemPointerSize));
213 }
214
215 // Push three registers. Pushes leftmost register first (to highest address).
Push(Register src1,Register src2,Register src3)216 void Push(Register src1, Register src2, Register src3) {
217 StorePU(src3, MemOperand(sp, -3 * kSystemPointerSize));
218 StoreP(src2, MemOperand(sp, kSystemPointerSize));
219 StoreP(src1, MemOperand(sp, 2 * kSystemPointerSize));
220 }
221
222 // Push four registers. Pushes leftmost register first (to highest address).
Push(Register src1,Register src2,Register src3,Register src4)223 void Push(Register src1, Register src2, Register src3, Register src4) {
224 StorePU(src4, MemOperand(sp, -4 * kSystemPointerSize));
225 StoreP(src3, MemOperand(sp, kSystemPointerSize));
226 StoreP(src2, MemOperand(sp, 2 * kSystemPointerSize));
227 StoreP(src1, MemOperand(sp, 3 * kSystemPointerSize));
228 }
229
230 // Push five registers. Pushes leftmost register first (to highest address).
Push(Register src1,Register src2,Register src3,Register src4,Register src5)231 void Push(Register src1, Register src2, Register src3, Register src4,
232 Register src5) {
233 StorePU(src5, MemOperand(sp, -5 * kSystemPointerSize));
234 StoreP(src4, MemOperand(sp, kSystemPointerSize));
235 StoreP(src3, MemOperand(sp, 2 * kSystemPointerSize));
236 StoreP(src2, MemOperand(sp, 3 * kSystemPointerSize));
237 StoreP(src1, MemOperand(sp, 4 * kSystemPointerSize));
238 }
239
240 enum PushArrayOrder { kNormal, kReverse };
241 void PushArray(Register array, Register size, Register scratch,
242 Register scratch2, PushArrayOrder order = kNormal);
243
Pop(Register dst)244 void Pop(Register dst) { pop(dst); }
245
246 // Pop two registers. Pops rightmost register first (from lower address).
Pop(Register src1,Register src2)247 void Pop(Register src1, Register src2) {
248 LoadP(src2, MemOperand(sp, 0));
249 LoadP(src1, MemOperand(sp, kSystemPointerSize));
250 addi(sp, sp, Operand(2 * kSystemPointerSize));
251 }
252
253 // Pop three registers. Pops rightmost register first (from lower address).
Pop(Register src1,Register src2,Register src3)254 void Pop(Register src1, Register src2, Register src3) {
255 LoadP(src3, MemOperand(sp, 0));
256 LoadP(src2, MemOperand(sp, kSystemPointerSize));
257 LoadP(src1, MemOperand(sp, 2 * kSystemPointerSize));
258 addi(sp, sp, Operand(3 * kSystemPointerSize));
259 }
260
261 // Pop four registers. Pops rightmost register first (from lower address).
Pop(Register src1,Register src2,Register src3,Register src4)262 void Pop(Register src1, Register src2, Register src3, Register src4) {
263 LoadP(src4, MemOperand(sp, 0));
264 LoadP(src3, MemOperand(sp, kSystemPointerSize));
265 LoadP(src2, MemOperand(sp, 2 * kSystemPointerSize));
266 LoadP(src1, MemOperand(sp, 3 * kSystemPointerSize));
267 addi(sp, sp, Operand(4 * kSystemPointerSize));
268 }
269
270 // Pop five registers. Pops rightmost register first (from lower address).
Pop(Register src1,Register src2,Register src3,Register src4,Register src5)271 void Pop(Register src1, Register src2, Register src3, Register src4,
272 Register src5) {
273 LoadP(src5, MemOperand(sp, 0));
274 LoadP(src4, MemOperand(sp, kSystemPointerSize));
275 LoadP(src3, MemOperand(sp, 2 * kSystemPointerSize));
276 LoadP(src2, MemOperand(sp, 3 * kSystemPointerSize));
277 LoadP(src1, MemOperand(sp, 4 * kSystemPointerSize));
278 addi(sp, sp, Operand(5 * kSystemPointerSize));
279 }
280
281 void SaveRegisters(RegList registers);
282 void RestoreRegisters(RegList registers);
283
284 void CallRecordWriteStub(Register object, Register address,
285 RememberedSetAction remembered_set_action,
286 SaveFPRegsMode fp_mode);
287 void CallRecordWriteStub(Register object, Register address,
288 RememberedSetAction remembered_set_action,
289 SaveFPRegsMode fp_mode, Address wasm_target);
290 void CallEphemeronKeyBarrier(Register object, Register address,
291 SaveFPRegsMode fp_mode);
292
293 void MultiPush(RegList regs, Register location = sp);
294 void MultiPop(RegList regs, Register location = sp);
295
296 void MultiPushDoubles(RegList dregs, Register location = sp);
297 void MultiPopDoubles(RegList dregs, Register location = sp);
298
299 // Calculate how much stack space (in bytes) are required to store caller
300 // registers excluding those specified in the arguments.
301 int RequiredStackSizeForCallerSaved(SaveFPRegsMode fp_mode,
302 Register exclusion1 = no_reg,
303 Register exclusion2 = no_reg,
304 Register exclusion3 = no_reg) const;
305
306 // Push caller saved registers on the stack, and return the number of bytes
307 // stack pointer is adjusted.
308 int PushCallerSaved(SaveFPRegsMode fp_mode, Register exclusion1 = no_reg,
309 Register exclusion2 = no_reg,
310 Register exclusion3 = no_reg);
311 // Restore caller saved registers from the stack, and return the number of
312 // bytes stack pointer is adjusted.
313 int PopCallerSaved(SaveFPRegsMode fp_mode, Register exclusion1 = no_reg,
314 Register exclusion2 = no_reg,
315 Register exclusion3 = no_reg);
316
317 // Load an object from the root table.
LoadRoot(Register destination,RootIndex index)318 void LoadRoot(Register destination, RootIndex index) override {
319 LoadRoot(destination, index, al);
320 }
321 void LoadRoot(Register destination, RootIndex index, Condition cond);
322
323 void SwapP(Register src, Register dst, Register scratch);
324 void SwapP(Register src, MemOperand dst, Register scratch);
325 void SwapP(MemOperand src, MemOperand dst, Register scratch_0,
326 Register scratch_1);
327 void SwapFloat32(DoubleRegister src, DoubleRegister dst,
328 DoubleRegister scratch);
329 void SwapFloat32(DoubleRegister src, MemOperand dst, DoubleRegister scratch);
330 void SwapFloat32(MemOperand src, MemOperand dst, DoubleRegister scratch_0,
331 DoubleRegister scratch_1);
332 void SwapDouble(DoubleRegister src, DoubleRegister dst,
333 DoubleRegister scratch);
334 void SwapDouble(DoubleRegister src, MemOperand dst, DoubleRegister scratch);
335 void SwapDouble(MemOperand src, MemOperand dst, DoubleRegister scratch_0,
336 DoubleRegister scratch_1);
337 void SwapSimd128(Simd128Register src, Simd128Register dst,
338 Simd128Register scratch);
339 void SwapSimd128(Simd128Register src, MemOperand dst,
340 Simd128Register scratch);
341 void SwapSimd128(MemOperand src, MemOperand dst, Simd128Register scratch);
342
343 // Before calling a C-function from generated code, align arguments on stack.
344 // After aligning the frame, non-register arguments must be stored in
345 // sp[0], sp[4], etc., not pushed. The argument count assumes all arguments
346 // are word sized. If double arguments are used, this function assumes that
347 // all double arguments are stored before core registers; otherwise the
348 // correct alignment of the double values is not guaranteed.
349 // Some compilers/platforms require the stack to be aligned when calling
350 // C++ code.
351 // Needs a scratch register to do some arithmetic. This register will be
352 // trashed.
353 void PrepareCallCFunction(int num_reg_arguments, int num_double_registers,
354 Register scratch);
355 void PrepareCallCFunction(int num_reg_arguments, Register scratch);
356
357 void PrepareForTailCall(Register callee_args_count,
358 Register caller_args_count, Register scratch0,
359 Register scratch1);
360
361 // There are two ways of passing double arguments on ARM, depending on
362 // whether soft or hard floating point ABI is used. These functions
363 // abstract parameter passing for the three different ways we call
364 // C functions from generated code.
365 void MovToFloatParameter(DoubleRegister src);
366 void MovToFloatParameters(DoubleRegister src1, DoubleRegister src2);
367 void MovToFloatResult(DoubleRegister src);
368
369 // Calls a C function and cleans up the space for arguments allocated
370 // by PrepareCallCFunction. The called function is not allowed to trigger a
371 // garbage collection, since that might move the code and invalidate the
372 // return address (unless this is somehow accounted for by the called
373 // function).
374 void CallCFunction(ExternalReference function, int num_arguments,
375 bool has_function_descriptor = true);
376 void CallCFunction(Register function, int num_arguments,
377 bool has_function_descriptor = true);
378 void CallCFunction(ExternalReference function, int num_reg_arguments,
379 int num_double_arguments,
380 bool has_function_descriptor = true);
381 void CallCFunction(Register function, int num_reg_arguments,
382 int num_double_arguments,
383 bool has_function_descriptor = true);
384
385 void MovFromFloatParameter(DoubleRegister dst);
386 void MovFromFloatResult(DoubleRegister dst);
387
388 void Trap() override;
389 void DebugBreak() override;
390
391 // Calls Abort(msg) if the condition cond is not satisfied.
392 // Use --debug_code to enable.
393 void Assert(Condition cond, AbortReason reason, CRegister cr = cr7);
394
395 // Like Assert(), but always enabled.
396 void Check(Condition cond, AbortReason reason, CRegister cr = cr7);
397
398 // Print a message to stdout and abort execution.
399 void Abort(AbortReason reason);
400
401 #if !V8_TARGET_ARCH_PPC64
402 void ShiftLeftPair(Register dst_low, Register dst_high, Register src_low,
403 Register src_high, Register scratch, Register shift);
404 void ShiftLeftPair(Register dst_low, Register dst_high, Register src_low,
405 Register src_high, uint32_t shift);
406 void ShiftRightPair(Register dst_low, Register dst_high, Register src_low,
407 Register src_high, Register scratch, Register shift);
408 void ShiftRightPair(Register dst_low, Register dst_high, Register src_low,
409 Register src_high, uint32_t shift);
410 void ShiftRightAlgPair(Register dst_low, Register dst_high, Register src_low,
411 Register src_high, Register scratch, Register shift);
412 void ShiftRightAlgPair(Register dst_low, Register dst_high, Register src_low,
413 Register src_high, uint32_t shift);
414 #endif
415
416 void LoadFromConstantsTable(Register destination,
417 int constant_index) override;
418 void LoadRootRegisterOffset(Register destination, intptr_t offset) override;
419 void LoadRootRelative(Register destination, int32_t offset) override;
420
421 // Jump, Call, and Ret pseudo instructions implementing inter-working.
422 void Jump(Register target);
423 void Jump(Address target, RelocInfo::Mode rmode, Condition cond = al,
424 CRegister cr = cr7);
425 void Jump(Handle<Code> code, RelocInfo::Mode rmode, Condition cond = al,
426 CRegister cr = cr7);
427 void Jump(const ExternalReference& reference) override;
428 void Jump(intptr_t target, RelocInfo::Mode rmode, Condition cond = al,
429 CRegister cr = cr7);
430 void Call(Register target);
431 void Call(Address target, RelocInfo::Mode rmode, Condition cond = al);
432 void Call(Handle<Code> code, RelocInfo::Mode rmode = RelocInfo::CODE_TARGET,
433 Condition cond = al);
434 void Call(Label* target);
435
436 // Load the builtin given by the Smi in |builtin_index| into the same
437 // register.
438 void LoadEntryFromBuiltinIndex(Register builtin_index);
439 void LoadCodeObjectEntry(Register destination, Register code_object) override;
440 void CallCodeObject(Register code_object) override;
441 void JumpCodeObject(Register code_object) override;
442
443 void CallBuiltinByIndex(Register builtin_index) override;
444 void CallForDeoptimization(Builtins::Name target, int deopt_id, Label* exit,
445 DeoptimizeKind kind,
446 Label* jump_deoptimization_entry_label);
447
448 // Emit code to discard a non-negative number of pointer-sized elements
449 // from the stack, clobbering only the sp register.
450 void Drop(int count);
451 void Drop(Register count, Register scratch = r0);
452
Ret()453 void Ret() { blr(); }
454 void Ret(Condition cond, CRegister cr = cr7) { bclr(cond, cr); }
Ret(int drop)455 void Ret(int drop) {
456 Drop(drop);
457 blr();
458 }
459
460 // If the value is a NaN, canonicalize the value else, do nothing.
461 void CanonicalizeNaN(const DoubleRegister dst, const DoubleRegister src);
CanonicalizeNaN(const DoubleRegister value)462 void CanonicalizeNaN(const DoubleRegister value) {
463 CanonicalizeNaN(value, value);
464 }
465 void CheckPageFlag(Register object, Register scratch, int mask, Condition cc,
466 Label* condition_met);
467
468 // Move values between integer and floating point registers.
469 void MovIntToDouble(DoubleRegister dst, Register src, Register scratch);
470 void MovUnsignedIntToDouble(DoubleRegister dst, Register src,
471 Register scratch);
472 void MovInt64ToDouble(DoubleRegister dst,
473 #if !V8_TARGET_ARCH_PPC64
474 Register src_hi,
475 #endif
476 Register src);
477 #if V8_TARGET_ARCH_PPC64
478 void MovInt64ComponentsToDouble(DoubleRegister dst, Register src_hi,
479 Register src_lo, Register scratch);
480 #endif
481 void InsertDoubleLow(DoubleRegister dst, Register src, Register scratch);
482 void InsertDoubleHigh(DoubleRegister dst, Register src, Register scratch);
483 void MovDoubleLowToInt(Register dst, DoubleRegister src);
484 void MovDoubleHighToInt(Register dst, DoubleRegister src);
485 void MovDoubleToInt64(
486 #if !V8_TARGET_ARCH_PPC64
487 Register dst_hi,
488 #endif
489 Register dst, DoubleRegister src);
490 void MovIntToFloat(DoubleRegister dst, Register src);
491 void MovFloatToInt(Register dst, DoubleRegister src);
492 // Register move. May do nothing if the registers are identical.
Move(Register dst,Smi smi)493 void Move(Register dst, Smi smi) { LoadSmiLiteral(dst, smi); }
494 void Move(Register dst, Handle<HeapObject> value,
495 RelocInfo::Mode rmode = RelocInfo::FULL_EMBEDDED_OBJECT);
496 void Move(Register dst, ExternalReference reference);
497 void Move(Register dst, Register src, Condition cond = al);
498 void Move(DoubleRegister dst, DoubleRegister src);
499
500 void SmiUntag(Register dst, const MemOperand& src, RCBit rc);
501 void SmiUntag(Register reg, RCBit rc = LeaveRC) { SmiUntag(reg, reg, rc); }
502
503 void SmiUntag(Register dst, Register src, RCBit rc = LeaveRC) {
504 if (COMPRESS_POINTERS_BOOL) {
505 srawi(dst, src, kSmiShift, rc);
506 } else {
507 ShiftRightArithImm(dst, src, kSmiShift, rc);
508 }
509 }
510
511 void ZeroExtByte(Register dst, Register src);
512 void ZeroExtHalfWord(Register dst, Register src);
513 void ZeroExtWord32(Register dst, Register src);
514
515 // ---------------------------------------------------------------------------
516 // Bit testing/extraction
517 //
518 // Bit numbering is such that the least significant bit is bit 0
519 // (for consistency between 32/64-bit).
520
521 // Extract consecutive bits (defined by rangeStart - rangeEnd) from src
522 // and, if !test, shift them into the least significant bits of dst.
523 inline void ExtractBitRange(Register dst, Register src, int rangeStart,
524 int rangeEnd, RCBit rc = LeaveRC,
525 bool test = false) {
526 DCHECK(rangeStart >= rangeEnd && rangeStart < kBitsPerSystemPointer);
527 int rotate = (rangeEnd == 0) ? 0 : kBitsPerSystemPointer - rangeEnd;
528 int width = rangeStart - rangeEnd + 1;
529 if (rc == SetRC && rangeStart < 16 && (rangeEnd == 0 || test)) {
530 // Prefer faster andi when applicable.
531 andi(dst, src, Operand(((1 << width) - 1) << rangeEnd));
532 } else {
533 #if V8_TARGET_ARCH_PPC64
534 rldicl(dst, src, rotate, kBitsPerSystemPointer - width, rc);
535 #else
536 rlwinm(dst, src, rotate, kBitsPerSystemPointer - width,
537 kBitsPerSystemPointer - 1, rc);
538 #endif
539 }
540 }
541
542 inline void ExtractBit(Register dst, Register src, uint32_t bitNumber,
543 RCBit rc = LeaveRC, bool test = false) {
544 ExtractBitRange(dst, src, bitNumber, bitNumber, rc, test);
545 }
546
547 // Extract consecutive bits (defined by mask) from src and place them
548 // into the least significant bits of dst.
549 inline void ExtractBitMask(Register dst, Register src, uintptr_t mask,
550 RCBit rc = LeaveRC, bool test = false) {
551 int start = kBitsPerSystemPointer - 1;
552 int end;
553 uintptr_t bit = (1L << start);
554
555 while (bit && (mask & bit) == 0) {
556 start--;
557 bit >>= 1;
558 }
559 end = start;
560 bit >>= 1;
561
562 while (bit && (mask & bit)) {
563 end--;
564 bit >>= 1;
565 }
566
567 // 1-bits in mask must be contiguous
568 DCHECK(bit == 0 || (mask & ((bit << 1) - 1)) == 0);
569
570 ExtractBitRange(dst, src, start, end, rc, test);
571 }
572
573 // Test single bit in value.
574 inline void TestBit(Register value, int bitNumber, Register scratch = r0) {
575 ExtractBitRange(scratch, value, bitNumber, bitNumber, SetRC, true);
576 }
577
578 // Test consecutive bit range in value. Range is defined by mask.
579 inline void TestBitMask(Register value, uintptr_t mask,
580 Register scratch = r0) {
581 ExtractBitMask(scratch, value, mask, SetRC, true);
582 }
583 // Test consecutive bit range in value. Range is defined by
584 // rangeStart - rangeEnd.
585 inline void TestBitRange(Register value, int rangeStart, int rangeEnd,
586 Register scratch = r0) {
587 ExtractBitRange(scratch, value, rangeStart, rangeEnd, SetRC, true);
588 }
589
TestIfSmi(Register value,Register scratch)590 inline void TestIfSmi(Register value, Register scratch) {
591 TestBitRange(value, kSmiTagSize - 1, 0, scratch);
592 }
593 // Jump the register contains a smi.
JumpIfSmi(Register value,Label * smi_label)594 inline void JumpIfSmi(Register value, Label* smi_label) {
595 TestIfSmi(value, r0);
596 beq(smi_label, cr0); // branch if SMI
597 }
598 void JumpIfEqual(Register x, int32_t y, Label* dest);
599 void JumpIfLessThan(Register x, int32_t y, Label* dest);
600
601 #if V8_TARGET_ARCH_PPC64
602 inline void TestIfInt32(Register value, Register scratch,
603 CRegister cr = cr7) {
604 // High bits must be identical to fit into an 32-bit integer
605 extsw(scratch, value);
606 cmp(scratch, value, cr);
607 }
608 #else
609 inline void TestIfInt32(Register hi_word, Register lo_word, Register scratch,
610 CRegister cr = cr7) {
611 // High bits must be identical to fit into an 32-bit integer
612 srawi(scratch, lo_word, 31);
613 cmp(scratch, hi_word, cr);
614 }
615 #endif
616
617 // Overflow handling functions.
618 // Usage: call the appropriate arithmetic function and then call one of the
619 // flow control functions with the corresponding label.
620
621 // Compute dst = left + right, setting condition codes. dst may be same as
622 // either left or right (or a unique register). left and right must not be
623 // the same register.
624 void AddAndCheckForOverflow(Register dst, Register left, Register right,
625 Register overflow_dst, Register scratch = r0);
626 void AddAndCheckForOverflow(Register dst, Register left, intptr_t right,
627 Register overflow_dst, Register scratch = r0);
628
629 // Compute dst = left - right, setting condition codes. dst may be same as
630 // either left or right (or a unique register). left and right must not be
631 // the same register.
632 void SubAndCheckForOverflow(Register dst, Register left, Register right,
633 Register overflow_dst, Register scratch = r0);
634
635 // Performs a truncating conversion of a floating point number as used by
636 // the JS bitwise operations. See ECMA-262 9.5: ToInt32. Goes to 'done' if it
637 // succeeds, otherwise falls through if result is saturated. On return
638 // 'result' either holds answer, or is clobbered on fall through.
639 void TryInlineTruncateDoubleToI(Register result, DoubleRegister input,
640 Label* done);
641 void TruncateDoubleToI(Isolate* isolate, Zone* zone, Register result,
642 DoubleRegister double_input, StubCallMode stub_mode);
643
644 void LoadConstantPoolPointerRegister();
645
646 // Loads the constant pool pointer (kConstantPoolRegister).
647 void LoadConstantPoolPointerRegisterFromCodeTargetAddress(
648 Register code_target_address);
AbortConstantPoolBuilding()649 void AbortConstantPoolBuilding() {
650 #ifdef DEBUG
651 // Avoid DCHECK(!is_linked()) failure in ~Label()
652 bind(ConstantPoolPosition());
653 #endif
654 }
655
656 // Generates an instruction sequence s.t. the return address points to the
657 // instruction following the call.
658 // The return address on the stack is used by frame iteration.
659 void StoreReturnAddressAndCall(Register target);
660
661 void ResetSpeculationPoisonRegister();
662
663 // Control-flow integrity:
664
665 // Define a function entrypoint. This doesn't emit any code for this
666 // architecture, as control-flow integrity is not supported for it.
CodeEntry()667 void CodeEntry() {}
668 // Define an exception handler.
ExceptionHandler()669 void ExceptionHandler() {}
670 // Define an exception handler and bind a label.
BindExceptionHandler(Label * label)671 void BindExceptionHandler(Label* label) { bind(label); }
672
673 // ---------------------------------------------------------------------------
674 // Pointer compression Support
675
676 // Loads a field containing a HeapObject and decompresses it if pointer
677 // compression is enabled.
678 void LoadTaggedPointerField(const Register& destination,
679 const MemOperand& field_operand,
680 const Register& scratch = no_reg);
681
682 // Loads a field containing any tagged value and decompresses it if necessary.
683 void LoadAnyTaggedField(const Register& destination,
684 const MemOperand& field_operand,
685 const Register& scratch = no_reg);
686
687 // Loads a field containing smi value and untags it.
688 void SmiUntagField(Register dst, const MemOperand& src, RCBit rc = LeaveRC);
689
690 // Compresses and stores tagged value to given on-heap location.
691 void StoreTaggedField(const Register& value,
692 const MemOperand& dst_field_operand,
693 const Register& scratch = no_reg);
694 void StoreTaggedFieldX(const Register& value,
695 const MemOperand& dst_field_operand,
696 const Register& scratch = no_reg);
697
698 void DecompressTaggedSigned(Register destination, MemOperand field_operand);
699 void DecompressTaggedSigned(Register destination, Register src);
700 void DecompressTaggedPointer(Register destination, MemOperand field_operand);
701 void DecompressTaggedPointer(Register destination, Register source);
702 void DecompressAnyTagged(Register destination, MemOperand field_operand);
703 void DecompressAnyTagged(Register destination, Register source);
704
705 void LoadWord(Register dst, const MemOperand& mem, Register scratch);
706 void StoreWord(Register src, const MemOperand& mem, Register scratch);
707
708 private:
709 static const int kSmiShift = kSmiTagSize + kSmiShiftSize;
710
711 int CalculateStackPassedWords(int num_reg_arguments,
712 int num_double_arguments);
713 void CallCFunctionHelper(Register function, int num_reg_arguments,
714 int num_double_arguments,
715 bool has_function_descriptor);
716 void CallRecordWriteStub(Register object, Register address,
717 RememberedSetAction remembered_set_action,
718 SaveFPRegsMode fp_mode, Handle<Code> code_target,
719 Address wasm_target);
720 };
721
722 // MacroAssembler implements a collection of frequently used acros.
723 class V8_EXPORT_PRIVATE MacroAssembler : public TurboAssembler {
724 public:
725 using TurboAssembler::TurboAssembler;
726
727 // It assumes that the arguments are located below the stack pointer.
728 // argc is the number of arguments not including the receiver.
729 // TODO(victorgomes): Remove this function once we stick with the reversed
730 // arguments order.
LoadReceiver(Register dest,Register argc)731 void LoadReceiver(Register dest, Register argc) {
732 LoadP(dest, MemOperand(sp, 0));
733 }
734
StoreReceiver(Register rec,Register argc,Register scratch)735 void StoreReceiver(Register rec, Register argc, Register scratch) {
736 StoreP(rec, MemOperand(sp, 0));
737 }
738
739 // ---------------------------------------------------------------------------
740 // GC Support
741
742 // Notify the garbage collector that we wrote a pointer into an object.
743 // |object| is the object being stored into, |value| is the object being
744 // stored. value and scratch registers are clobbered by the operation.
745 // The offset is the offset from the start of the object, not the offset from
746 // the tagged HeapObject pointer. For use with FieldMemOperand(reg, off).
747 void RecordWriteField(
748 Register object, int offset, Register value, Register scratch,
749 LinkRegisterStatus lr_status, SaveFPRegsMode save_fp,
750 RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET,
751 SmiCheck smi_check = INLINE_SMI_CHECK);
752
753 // For a given |object| notify the garbage collector that the slot |address|
754 // has been written. |value| is the object being stored. The value and
755 // address registers are clobbered by the operation.
756 void RecordWrite(
757 Register object, Register address, Register value,
758 LinkRegisterStatus lr_status, SaveFPRegsMode save_fp,
759 RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET,
760 SmiCheck smi_check = INLINE_SMI_CHECK);
761
762 // Enter exit frame.
763 // stack_space - extra stack space, used for parameters before call to C.
764 // At least one slot (for the return address) should be provided.
765 void EnterExitFrame(bool save_doubles, int stack_space = 1,
766 StackFrame::Type frame_type = StackFrame::EXIT);
767
768 // Leave the current exit frame. Expects the return value in r0.
769 // Expect the number of values, pushed prior to the exit frame, to
770 // remove in a register (or no_reg, if there is nothing to remove).
771 void LeaveExitFrame(bool save_doubles, Register argument_count,
772 bool argument_count_is_length = false);
773
774 void LoadMap(Register destination, Register object);
775
776 // Load the global proxy from the current context.
LoadGlobalProxy(Register dst)777 void LoadGlobalProxy(Register dst) {
778 LoadNativeContextSlot(Context::GLOBAL_PROXY_INDEX, dst);
779 }
780
781 void LoadNativeContextSlot(int index, Register dst);
782
783 // ----------------------------------------------------------------
784 // new PPC macro-assembler interfaces that are slightly higher level
785 // than assembler-ppc and may generate variable length sequences
786
787 // load a literal double value <value> to FPR <result>
788
789 void LoadHalfWord(Register dst, const MemOperand& mem,
790 Register scratch = no_reg);
791 void LoadHalfWordArith(Register dst, const MemOperand& mem,
792 Register scratch = no_reg);
793 void StoreHalfWord(Register src, const MemOperand& mem, Register scratch);
794
795 void LoadByte(Register dst, const MemOperand& mem, Register scratch);
796 void StoreByte(Register src, const MemOperand& mem, Register scratch);
797
798 void LoadDoubleU(DoubleRegister dst, const MemOperand& mem,
799 Register scratch = no_reg);
800
801 void Cmplwi(Register src1, const Operand& src2, Register scratch,
802 CRegister cr = cr7);
803 void And(Register ra, Register rs, const Operand& rb, RCBit rc = LeaveRC);
804 void Or(Register ra, Register rs, const Operand& rb, RCBit rc = LeaveRC);
805 void Xor(Register ra, Register rs, const Operand& rb, RCBit rc = LeaveRC);
806
807 void AddSmiLiteral(Register dst, Register src, Smi smi, Register scratch);
808 void SubSmiLiteral(Register dst, Register src, Smi smi, Register scratch);
809 void CmpSmiLiteral(Register src1, Smi smi, Register scratch,
810 CRegister cr = cr7);
811 void CmplSmiLiteral(Register src1, Smi smi, Register scratch,
812 CRegister cr = cr7);
813 void AndSmiLiteral(Register dst, Register src, Smi smi, Register scratch,
814 RCBit rc = LeaveRC);
815
816 // ---------------------------------------------------------------------------
817 // JavaScript invokes
818
819 // Removes current frame and its arguments from the stack preserving
820 // the arguments and a return address pushed to the stack for the next call.
821 // Both |callee_args_count| and |caller_args_countg| do not include
822 // receiver. |callee_args_count| is not modified. |caller_args_count|
823 // is trashed.
824
825 // Invoke the JavaScript function code by either calling or jumping.
826 void InvokeFunctionCode(Register function, Register new_target,
827 Register expected_parameter_count,
828 Register actual_parameter_count, InvokeFlag flag);
829
830 // On function call, call into the debugger if necessary.
831 void CheckDebugHook(Register fun, Register new_target,
832 Register expected_parameter_count,
833 Register actual_parameter_count);
834
835 // Invoke the JavaScript function in the given register. Changes the
836 // current context to the context in the function before invoking.
837 void InvokeFunctionWithNewTarget(Register function, Register new_target,
838 Register actual_parameter_count,
839 InvokeFlag flag);
840 void InvokeFunction(Register function, Register expected_parameter_count,
841 Register actual_parameter_count, InvokeFlag flag);
842
843 // Frame restart support
844 void MaybeDropFrames();
845
846 // Exception handling
847
848 // Push a new stack handler and link into stack handler chain.
849 void PushStackHandler();
850
851 // Unlink the stack handler on top of the stack from the stack handler chain.
852 // Must preserve the result register.
853 void PopStackHandler();
854
855 // ---------------------------------------------------------------------------
856 // Support functions.
857
858 // Compare object type for heap object. heap_object contains a non-Smi
859 // whose object type should be compared with the given type. This both
860 // sets the flags and leaves the object type in the type_reg register.
861 // It leaves the map in the map register (unless the type_reg and map register
862 // are the same register). It leaves the heap object in the heap_object
863 // register unless the heap_object register is the same register as one of the
864 // other registers.
865 // Type_reg can be no_reg. In that case ip is used.
866 void CompareObjectType(Register heap_object, Register map, Register type_reg,
867 InstanceType type);
868
869 // Compare instance type in a map. map contains a valid map object whose
870 // object type should be compared with the given type. This both
871 // sets the flags and leaves the object type in the type_reg register.
872 void CompareInstanceType(Register map, Register type_reg, InstanceType type);
873
874 // Compare the object in a register to a value from the root list.
875 // Uses the ip register as scratch.
876 void CompareRoot(Register obj, RootIndex index);
PushRoot(RootIndex index)877 void PushRoot(RootIndex index) {
878 LoadRoot(r0, index);
879 Push(r0);
880 }
881
882 // Compare the object in a register to a value and jump if they are equal.
JumpIfRoot(Register with,RootIndex index,Label * if_equal)883 void JumpIfRoot(Register with, RootIndex index, Label* if_equal) {
884 CompareRoot(with, index);
885 beq(if_equal);
886 }
887
888 // Compare the object in a register to a value and jump if they are not equal.
JumpIfNotRoot(Register with,RootIndex index,Label * if_not_equal)889 void JumpIfNotRoot(Register with, RootIndex index, Label* if_not_equal) {
890 CompareRoot(with, index);
891 bne(if_not_equal);
892 }
893
894 // Checks if value is in range [lower_limit, higher_limit] using a single
895 // comparison.
896 void JumpIfIsInRange(Register value, unsigned lower_limit,
897 unsigned higher_limit, Label* on_in_range);
898
899 // ---------------------------------------------------------------------------
900 // Runtime calls
901
902 static int CallSizeNotPredictableCodeSize(Address target,
903 RelocInfo::Mode rmode,
904 Condition cond = al);
905 void CallJSEntry(Register target);
906
907 // Call a runtime routine.
908 void CallRuntime(const Runtime::Function* f, int num_arguments,
909 SaveFPRegsMode save_doubles = kDontSaveFPRegs);
CallRuntimeSaveDoubles(Runtime::FunctionId fid)910 void CallRuntimeSaveDoubles(Runtime::FunctionId fid) {
911 const Runtime::Function* function = Runtime::FunctionForId(fid);
912 CallRuntime(function, function->nargs, kSaveFPRegs);
913 }
914
915 // Convenience function: Same as above, but takes the fid instead.
916 void CallRuntime(Runtime::FunctionId fid,
917 SaveFPRegsMode save_doubles = kDontSaveFPRegs) {
918 const Runtime::Function* function = Runtime::FunctionForId(fid);
919 CallRuntime(function, function->nargs, save_doubles);
920 }
921
922 // Convenience function: Same as above, but takes the fid instead.
923 void CallRuntime(Runtime::FunctionId fid, int num_arguments,
924 SaveFPRegsMode save_doubles = kDontSaveFPRegs) {
925 CallRuntime(Runtime::FunctionForId(fid), num_arguments, save_doubles);
926 }
927
928 // Convenience function: tail call a runtime routine (jump).
929 void TailCallRuntime(Runtime::FunctionId fid);
930
931 // Jump to a runtime routine.
932 void JumpToExternalReference(const ExternalReference& builtin,
933 bool builtin_exit_frame = false);
934
935 // Generates a trampoline to jump to the off-heap instruction stream.
936 void JumpToInstructionStream(Address entry);
937
938 // ---------------------------------------------------------------------------
939 // In-place weak references.
940 void LoadWeakValue(Register out, Register in, Label* target_if_cleared);
941
942 // ---------------------------------------------------------------------------
943 // StatsCounter support
944
945 void IncrementCounter(StatsCounter* counter, int value, Register scratch1,
946 Register scratch2);
947 void DecrementCounter(StatsCounter* counter, int value, Register scratch1,
948 Register scratch2);
949
950 // ---------------------------------------------------------------------------
951 // Smi utilities
952
953 // Shift left by kSmiShift
954 void SmiTag(Register reg, RCBit rc = LeaveRC) { SmiTag(reg, reg, rc); }
955 void SmiTag(Register dst, Register src, RCBit rc = LeaveRC) {
956 ShiftLeftImm(dst, src, Operand(kSmiShift), rc);
957 }
958
SmiToPtrArrayOffset(Register dst,Register src)959 void SmiToPtrArrayOffset(Register dst, Register src) {
960 #if defined(V8_COMPRESS_POINTERS) || defined(V8_31BIT_SMIS_ON_64BIT_ARCH)
961 STATIC_ASSERT(kSmiTag == 0 && kSmiShift < kSystemPointerSizeLog2);
962 ShiftLeftImm(dst, src, Operand(kSystemPointerSizeLog2 - kSmiShift));
963 #else
964 STATIC_ASSERT(kSmiTag == 0 && kSmiShift > kSystemPointerSizeLog2);
965 ShiftRightArithImm(dst, src, kSmiShift - kSystemPointerSizeLog2);
966 #endif
967 }
968
969 // Jump if either of the registers contain a non-smi.
JumpIfNotSmi(Register value,Label * not_smi_label)970 inline void JumpIfNotSmi(Register value, Label* not_smi_label) {
971 TestIfSmi(value, r0);
972 bne(not_smi_label, cr0);
973 }
974
975 // Abort execution if argument is a smi, enabled via --debug-code.
976 void AssertNotSmi(Register object);
977 void AssertSmi(Register object);
978
979 #if !defined(V8_COMPRESS_POINTERS) && !defined(V8_31BIT_SMIS_ON_64BIT_ARCH)
980 // Ensure it is permissible to read/write int value directly from
981 // upper half of the smi.
982 STATIC_ASSERT(kSmiTag == 0);
983 STATIC_ASSERT(kSmiTagSize + kSmiShiftSize == 32);
984 #endif
985 #if V8_TARGET_ARCH_PPC64 && V8_TARGET_LITTLE_ENDIAN
986 #define SmiWordOffset(offset) (offset + kSystemPointerSize / 2)
987 #else
988 #define SmiWordOffset(offset) offset
989 #endif
990
991 // Abort execution if argument is not a Constructor, enabled via --debug-code.
992 void AssertConstructor(Register object);
993
994 // Abort execution if argument is not a JSFunction, enabled via --debug-code.
995 void AssertFunction(Register object);
996
997 // Abort execution if argument is not a JSBoundFunction,
998 // enabled via --debug-code.
999 void AssertBoundFunction(Register object);
1000
1001 // Abort execution if argument is not a JSGeneratorObject (or subclass),
1002 // enabled via --debug-code.
1003 void AssertGeneratorObject(Register object);
1004
1005 // Abort execution if argument is not undefined or an AllocationSite, enabled
1006 // via --debug-code.
1007 void AssertUndefinedOrAllocationSite(Register object, Register scratch);
1008
1009 // ---------------------------------------------------------------------------
1010 // Patching helpers.
1011
1012 template <typename Field>
1013 void DecodeField(Register dst, Register src, RCBit rc = LeaveRC) {
1014 ExtractBitRange(dst, src, Field::kShift + Field::kSize - 1, Field::kShift,
1015 rc);
1016 }
1017
1018 template <typename Field>
1019 void DecodeField(Register reg, RCBit rc = LeaveRC) {
1020 DecodeField<Field>(reg, reg, rc);
1021 }
1022
1023 private:
1024 static const int kSmiShift = kSmiTagSize + kSmiShiftSize;
1025
1026 // Helper functions for generating invokes.
1027 void InvokePrologue(Register expected_parameter_count,
1028 Register actual_parameter_count, Label* done,
1029 InvokeFlag flag);
1030
1031 DISALLOW_IMPLICIT_CONSTRUCTORS(MacroAssembler);
1032 };
1033
1034 #define ACCESS_MASM(masm) masm->
1035
1036 } // namespace internal
1037 } // namespace v8
1038
1039 #endif // V8_CODEGEN_PPC_MACRO_ASSEMBLER_PPC_H_
1040