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_MIPS64_MACRO_ASSEMBLER_MIPS64_H_
10 #define V8_CODEGEN_MIPS64_MACRO_ASSEMBLER_MIPS64_H_
11
12 #include "src/codegen/assembler.h"
13 #include "src/codegen/mips64/assembler-mips64.h"
14 #include "src/common/globals.h"
15
16 namespace v8 {
17 namespace internal {
18
19 // Forward declarations.
20 enum class AbortReason : uint8_t;
21
22 // Reserved Register Usage Summary.
23 //
24 // Registers t8, t9, and at are reserved for use by the MacroAssembler.
25 //
26 // The programmer should know that the MacroAssembler may clobber these three,
27 // but won't touch other registers except in special cases.
28 //
29 // Per the MIPS ABI, register t9 must be used for indirect function call
30 // via 'jalr t9' or 'jr t9' instructions. This is relied upon by gcc when
31 // trying to update gp register for position-independent-code. Whenever
32 // MIPS generated code calls C code, it must be via t9 register.
33
34 // Flags used for LeaveExitFrame function.
35 enum LeaveExitFrameMode { EMIT_RETURN = true, NO_EMIT_RETURN = false };
36
37 // Allow programmer to use Branch Delay Slot of Branches, Jumps, Calls.
38 enum BranchDelaySlot { USE_DELAY_SLOT, PROTECT };
39
40 // Flags used for the li macro-assembler function.
41 enum LiFlags {
42 // If the constant value can be represented in just 16 bits, then
43 // optimize the li to use a single instruction, rather than lui/ori/dsll
44 // sequence. A number of other optimizations that emits less than
45 // maximum number of instructions exists.
46 OPTIMIZE_SIZE = 0,
47 // Always use 6 instructions (lui/ori/dsll sequence) for release 2 or 4
48 // instructions for release 6 (lui/ori/dahi/dati), even if the constant
49 // could be loaded with just one, so that this value is patchable later.
50 CONSTANT_SIZE = 1,
51 // For address loads only 4 instruction are required. Used to mark
52 // constant load that will be used as address without relocation
53 // information. It ensures predictable code size, so specific sites
54 // in code are patchable.
55 ADDRESS_LOAD = 2
56 };
57
58 enum RememberedSetAction { EMIT_REMEMBERED_SET, OMIT_REMEMBERED_SET };
59 enum SmiCheck { INLINE_SMI_CHECK, OMIT_SMI_CHECK };
60 enum RAStatus { kRAHasNotBeenSaved, kRAHasBeenSaved };
61
62 Register GetRegisterThatIsNotOneOf(Register reg1, Register reg2 = no_reg,
63 Register reg3 = no_reg,
64 Register reg4 = no_reg,
65 Register reg5 = no_reg,
66 Register reg6 = no_reg);
67
68 // -----------------------------------------------------------------------------
69 // Static helper functions.
70
71 #if defined(V8_TARGET_LITTLE_ENDIAN)
72 #define SmiWordOffset(offset) (offset + kPointerSize / 2)
73 #else
74 #define SmiWordOffset(offset) offset
75 #endif
76
77 // Generate a MemOperand for loading a field from an object.
FieldMemOperand(Register object,int offset)78 inline MemOperand FieldMemOperand(Register object, int offset) {
79 return MemOperand(object, offset - kHeapObjectTag);
80 }
81
82 // Generate a MemOperand for storing arguments 5..N on the stack
83 // when calling CallCFunction().
84 // TODO(plind): Currently ONLY used for O32. Should be fixed for
85 // n64, and used in RegExp code, and other places
86 // with more than 8 arguments.
CFunctionArgumentOperand(int index)87 inline MemOperand CFunctionArgumentOperand(int index) {
88 DCHECK_GT(index, kCArgSlotCount);
89 // Argument 5 takes the slot just past the four Arg-slots.
90 int offset = (index - 5) * kPointerSize + kCArgsSlotsSize;
91 return MemOperand(sp, offset);
92 }
93
94 class V8_EXPORT_PRIVATE TurboAssembler : public TurboAssemblerBase {
95 public:
96 using TurboAssemblerBase::TurboAssemblerBase;
97
98 // Activation support.
99 void EnterFrame(StackFrame::Type type);
EnterFrame(StackFrame::Type type,bool load_constant_pool_pointer_reg)100 void EnterFrame(StackFrame::Type type, bool load_constant_pool_pointer_reg) {
101 // Out-of-line constant pool not implemented on mips.
102 UNREACHABLE();
103 }
104 void LeaveFrame(StackFrame::Type type);
105
106 // Generates function and stub prologue code.
107 void StubPrologue(StackFrame::Type type);
108 void Prologue();
109
InitializeRootRegister()110 void InitializeRootRegister() {
111 ExternalReference isolate_root = ExternalReference::isolate_root(isolate());
112 li(kRootRegister, Operand(isolate_root));
113 }
114
115 // Jump unconditionally to given label.
116 // We NEED a nop in the branch delay slot, as it used by v8, for example in
117 // CodeGenerator::ProcessDeferred().
118 // Currently the branch delay slot is filled by the MacroAssembler.
119 // Use rather b(Label) for code generation.
jmp(Label * L)120 void jmp(Label* L) { Branch(L); }
121
122 // -------------------------------------------------------------------------
123 // Debugging.
124
125 void Trap() override;
126 void DebugBreak() override;
127
128 // Calls Abort(msg) if the condition cc is not satisfied.
129 // Use --debug_code to enable.
130 void Assert(Condition cc, AbortReason reason, Register rs, Operand rt);
131
132 // Like Assert(), but always enabled.
133 void Check(Condition cc, AbortReason reason, Register rs, Operand rt);
134
135 // Print a message to stdout and abort execution.
136 void Abort(AbortReason msg);
137
138 // Arguments macros.
139 #define COND_TYPED_ARGS Condition cond, Register r1, const Operand &r2
140 #define COND_ARGS cond, r1, r2
141
142 // Cases when relocation is not needed.
143 #define DECLARE_NORELOC_PROTOTYPE(Name, target_type) \
144 void Name(target_type target, BranchDelaySlot bd = PROTECT); \
145 inline void Name(BranchDelaySlot bd, target_type target) { \
146 Name(target, bd); \
147 } \
148 void Name(target_type target, COND_TYPED_ARGS, \
149 BranchDelaySlot bd = PROTECT); \
150 inline void Name(BranchDelaySlot bd, target_type target, COND_TYPED_ARGS) { \
151 Name(target, COND_ARGS, bd); \
152 }
153
154 #define DECLARE_BRANCH_PROTOTYPES(Name) \
155 DECLARE_NORELOC_PROTOTYPE(Name, Label*) \
156 DECLARE_NORELOC_PROTOTYPE(Name, int32_t)
157
158 DECLARE_BRANCH_PROTOTYPES(Branch)
DECLARE_BRANCH_PROTOTYPES(BranchAndLink)159 DECLARE_BRANCH_PROTOTYPES(BranchAndLink)
160 DECLARE_BRANCH_PROTOTYPES(BranchShort)
161
162 #undef DECLARE_BRANCH_PROTOTYPES
163 #undef COND_TYPED_ARGS
164 #undef COND_ARGS
165
166 // Floating point branches
167 void CompareF32(FPUCondition cc, FPURegister cmp1, FPURegister cmp2) {
168 CompareF(S, cc, cmp1, cmp2);
169 }
170
CompareIsNanF32(FPURegister cmp1,FPURegister cmp2)171 void CompareIsNanF32(FPURegister cmp1, FPURegister cmp2) {
172 CompareIsNanF(S, cmp1, cmp2);
173 }
174
CompareF64(FPUCondition cc,FPURegister cmp1,FPURegister cmp2)175 void CompareF64(FPUCondition cc, FPURegister cmp1, FPURegister cmp2) {
176 CompareF(D, cc, cmp1, cmp2);
177 }
178
CompareIsNanF64(FPURegister cmp1,FPURegister cmp2)179 void CompareIsNanF64(FPURegister cmp1, FPURegister cmp2) {
180 CompareIsNanF(D, cmp1, cmp2);
181 }
182
183 void BranchTrueShortF(Label* target, BranchDelaySlot bd = PROTECT);
184 void BranchFalseShortF(Label* target, BranchDelaySlot bd = PROTECT);
185
186 void BranchTrueF(Label* target, BranchDelaySlot bd = PROTECT);
187 void BranchFalseF(Label* target, BranchDelaySlot bd = PROTECT);
188
189 // MSA branches
190 void BranchMSA(Label* target, MSABranchDF df, MSABranchCondition cond,
191 MSARegister wt, BranchDelaySlot bd = PROTECT);
192
193 void Branch(Label* L, Condition cond, Register rs, RootIndex index,
194 BranchDelaySlot bdslot = PROTECT);
195
196 static int InstrCountForLi64Bit(int64_t value);
197 inline void LiLower32BitHelper(Register rd, Operand j);
198 void li_optimized(Register rd, Operand j, LiFlags mode = OPTIMIZE_SIZE);
199 // Load int32 in the rd register.
200 void li(Register rd, Operand j, LiFlags mode = OPTIMIZE_SIZE);
201 inline void li(Register rd, int64_t j, LiFlags mode = OPTIMIZE_SIZE) {
202 li(rd, Operand(j), mode);
203 }
204 // inline void li(Register rd, int32_t j, LiFlags mode = OPTIMIZE_SIZE) {
205 // li(rd, Operand(static_cast<int64_t>(j)), mode);
206 // }
207 void li(Register dst, Handle<HeapObject> value, LiFlags mode = OPTIMIZE_SIZE);
208 void li(Register dst, ExternalReference value, LiFlags mode = OPTIMIZE_SIZE);
209 void li(Register dst, const StringConstantBase* string,
210 LiFlags mode = OPTIMIZE_SIZE);
211
212 void LoadFromConstantsTable(Register destination,
213 int constant_index) override;
214 void LoadRootRegisterOffset(Register destination, intptr_t offset) override;
215 void LoadRootRelative(Register destination, int32_t offset) override;
216
217 // Jump, Call, and Ret pseudo instructions implementing inter-working.
218 #define COND_ARGS \
219 Condition cond = al, Register rs = zero_reg, \
220 const Operand &rt = Operand(zero_reg), \
221 BranchDelaySlot bd = PROTECT
222
223 void Jump(Register target, COND_ARGS);
224 void Jump(intptr_t target, RelocInfo::Mode rmode, COND_ARGS);
225 void Jump(Address target, RelocInfo::Mode rmode, COND_ARGS);
226 // Deffer from li, this method save target to the memory, and then load
227 // it to register use ld, it can be used in wasm jump table for concurrent
228 // patching.
229 void PatchAndJump(Address target);
230 void Jump(Handle<Code> code, RelocInfo::Mode rmode, COND_ARGS);
231 void Jump(const ExternalReference& reference) override;
232 void Call(Register target, COND_ARGS);
233 void Call(Address target, RelocInfo::Mode rmode, COND_ARGS);
234 void Call(Handle<Code> code, RelocInfo::Mode rmode = RelocInfo::CODE_TARGET,
235 COND_ARGS);
236 void Call(Label* target);
237 void LoadAddress(Register dst, Label* target);
238
239 // Load the builtin given by the Smi in |builtin_index| into the same
240 // register.
241 void LoadEntryFromBuiltinIndex(Register builtin_index);
242 void CallBuiltinByIndex(Register builtin_index) override;
243
LoadCodeObjectEntry(Register destination,Register code_object)244 void LoadCodeObjectEntry(Register destination,
245 Register code_object) override {
246 // TODO(mips): Implement.
247 UNIMPLEMENTED();
248 }
CallCodeObject(Register code_object)249 void CallCodeObject(Register code_object) override {
250 // TODO(mips): Implement.
251 UNIMPLEMENTED();
252 }
JumpCodeObject(Register code_object)253 void JumpCodeObject(Register code_object) override {
254 // TODO(mips): Implement.
255 UNIMPLEMENTED();
256 }
257
258 // Generates an instruction sequence s.t. the return address points to the
259 // instruction following the call.
260 // The return address on the stack is used by frame iteration.
261 void StoreReturnAddressAndCall(Register target);
262
263 void CallForDeoptimization(Builtins::Name target, int deopt_id, Label* exit,
264 DeoptimizeKind kind,
265 Label* jump_deoptimization_entry_label);
266
267 void Ret(COND_ARGS);
268 inline void Ret(BranchDelaySlot bd, Condition cond = al,
269 Register rs = zero_reg,
270 const Operand& rt = Operand(zero_reg)) {
271 Ret(cond, rs, rt, bd);
272 }
273
274 // Emit code to discard a non-negative number of pointer-sized elements
275 // from the stack, clobbering only the sp register.
276 void Drop(int count, Condition cond = cc_always, Register reg = no_reg,
277 const Operand& op = Operand(no_reg));
278
279 // Trivial case of DropAndRet that utilizes the delay slot.
280 void DropAndRet(int drop);
281
282 void DropAndRet(int drop, Condition cond, Register reg, const Operand& op);
283
284 void Ld(Register rd, const MemOperand& rs);
285 void Sd(Register rd, const MemOperand& rs);
286
push(Register src)287 void push(Register src) {
288 Daddu(sp, sp, Operand(-kPointerSize));
289 Sd(src, MemOperand(sp, 0));
290 }
Push(Register src)291 void Push(Register src) { push(src); }
292 void Push(Handle<HeapObject> handle);
293 void Push(Smi smi);
294
295 // Push two registers. Pushes leftmost register first (to highest address).
Push(Register src1,Register src2)296 void Push(Register src1, Register src2) {
297 Dsubu(sp, sp, Operand(2 * kPointerSize));
298 Sd(src1, MemOperand(sp, 1 * kPointerSize));
299 Sd(src2, MemOperand(sp, 0 * kPointerSize));
300 }
301
302 // Push three registers. Pushes leftmost register first (to highest address).
Push(Register src1,Register src2,Register src3)303 void Push(Register src1, Register src2, Register src3) {
304 Dsubu(sp, sp, Operand(3 * kPointerSize));
305 Sd(src1, MemOperand(sp, 2 * kPointerSize));
306 Sd(src2, MemOperand(sp, 1 * kPointerSize));
307 Sd(src3, MemOperand(sp, 0 * kPointerSize));
308 }
309
310 // Push four registers. Pushes leftmost register first (to highest address).
Push(Register src1,Register src2,Register src3,Register src4)311 void Push(Register src1, Register src2, Register src3, Register src4) {
312 Dsubu(sp, sp, Operand(4 * kPointerSize));
313 Sd(src1, MemOperand(sp, 3 * kPointerSize));
314 Sd(src2, MemOperand(sp, 2 * kPointerSize));
315 Sd(src3, MemOperand(sp, 1 * kPointerSize));
316 Sd(src4, MemOperand(sp, 0 * kPointerSize));
317 }
318
319 // Push five registers. Pushes leftmost register first (to highest address).
Push(Register src1,Register src2,Register src3,Register src4,Register src5)320 void Push(Register src1, Register src2, Register src3, Register src4,
321 Register src5) {
322 Dsubu(sp, sp, Operand(5 * kPointerSize));
323 Sd(src1, MemOperand(sp, 4 * kPointerSize));
324 Sd(src2, MemOperand(sp, 3 * kPointerSize));
325 Sd(src3, MemOperand(sp, 2 * kPointerSize));
326 Sd(src4, MemOperand(sp, 1 * kPointerSize));
327 Sd(src5, MemOperand(sp, 0 * kPointerSize));
328 }
329
Push(Register src,Condition cond,Register tst1,Register tst2)330 void Push(Register src, Condition cond, Register tst1, Register tst2) {
331 // Since we don't have conditional execution we use a Branch.
332 Branch(3, cond, tst1, Operand(tst2));
333 Dsubu(sp, sp, Operand(kPointerSize));
334 Sd(src, MemOperand(sp, 0));
335 }
336
337 enum PushArrayOrder { kNormal, kReverse };
338 void PushArray(Register array, Register size, Register scratch,
339 Register scratch2, PushArrayOrder order = kNormal);
340
341 void SaveRegisters(RegList registers);
342 void RestoreRegisters(RegList registers);
343
344 void CallRecordWriteStub(Register object, Register address,
345 RememberedSetAction remembered_set_action,
346 SaveFPRegsMode fp_mode);
347 void CallRecordWriteStub(Register object, Register address,
348 RememberedSetAction remembered_set_action,
349 SaveFPRegsMode fp_mode, Address wasm_target);
350 void CallEphemeronKeyBarrier(Register object, Register address,
351 SaveFPRegsMode fp_mode);
352
353 // Push multiple registers on the stack.
354 // Registers are saved in numerical order, with higher numbered registers
355 // saved in higher memory addresses.
356 void MultiPush(RegList regs);
357 void MultiPushFPU(RegList regs);
358
359 // Calculate how much stack space (in bytes) are required to store caller
360 // registers excluding those specified in the arguments.
361 int RequiredStackSizeForCallerSaved(SaveFPRegsMode fp_mode,
362 Register exclusion1 = no_reg,
363 Register exclusion2 = no_reg,
364 Register exclusion3 = no_reg) const;
365
366 // Push caller saved registers on the stack, and return the number of bytes
367 // stack pointer is adjusted.
368 int PushCallerSaved(SaveFPRegsMode fp_mode, Register exclusion1 = no_reg,
369 Register exclusion2 = no_reg,
370 Register exclusion3 = no_reg);
371 // Restore caller saved registers from the stack, and return the number of
372 // bytes stack pointer is adjusted.
373 int PopCallerSaved(SaveFPRegsMode fp_mode, Register exclusion1 = no_reg,
374 Register exclusion2 = no_reg,
375 Register exclusion3 = no_reg);
376
pop(Register dst)377 void pop(Register dst) {
378 Ld(dst, MemOperand(sp, 0));
379 Daddu(sp, sp, Operand(kPointerSize));
380 }
Pop(Register dst)381 void Pop(Register dst) { pop(dst); }
382
383 // Pop two registers. Pops rightmost register first (from lower address).
Pop(Register src1,Register src2)384 void Pop(Register src1, Register src2) {
385 DCHECK(src1 != src2);
386 Ld(src2, MemOperand(sp, 0 * kPointerSize));
387 Ld(src1, MemOperand(sp, 1 * kPointerSize));
388 Daddu(sp, sp, 2 * kPointerSize);
389 }
390
391 // Pop three registers. Pops rightmost register first (from lower address).
Pop(Register src1,Register src2,Register src3)392 void Pop(Register src1, Register src2, Register src3) {
393 Ld(src3, MemOperand(sp, 0 * kPointerSize));
394 Ld(src2, MemOperand(sp, 1 * kPointerSize));
395 Ld(src1, MemOperand(sp, 2 * kPointerSize));
396 Daddu(sp, sp, 3 * kPointerSize);
397 }
398
399 void Pop(uint32_t count = 1) { Daddu(sp, sp, Operand(count * kPointerSize)); }
400
401 // Pops multiple values from the stack and load them in the
402 // registers specified in regs. Pop order is the opposite as in MultiPush.
403 void MultiPop(RegList regs);
404 void MultiPopFPU(RegList regs);
405
406 #define DEFINE_INSTRUCTION(instr) \
407 void instr(Register rd, Register rs, const Operand& rt); \
408 void instr(Register rd, Register rs, Register rt) { \
409 instr(rd, rs, Operand(rt)); \
410 } \
411 void instr(Register rs, Register rt, int32_t j) { instr(rs, rt, Operand(j)); }
412
413 #define DEFINE_INSTRUCTION2(instr) \
414 void instr(Register rs, const Operand& rt); \
415 void instr(Register rs, Register rt) { instr(rs, Operand(rt)); } \
416 void instr(Register rs, int32_t j) { instr(rs, Operand(j)); }
417
418 DEFINE_INSTRUCTION(Addu)
419 DEFINE_INSTRUCTION(Daddu)
420 DEFINE_INSTRUCTION(Div)
421 DEFINE_INSTRUCTION(Divu)
422 DEFINE_INSTRUCTION(Ddivu)
423 DEFINE_INSTRUCTION(Mod)
424 DEFINE_INSTRUCTION(Modu)
425 DEFINE_INSTRUCTION(Ddiv)
426 DEFINE_INSTRUCTION(Subu)
427 DEFINE_INSTRUCTION(Dsubu)
428 DEFINE_INSTRUCTION(Dmod)
429 DEFINE_INSTRUCTION(Dmodu)
430 DEFINE_INSTRUCTION(Mul)
431 DEFINE_INSTRUCTION(Mulh)
432 DEFINE_INSTRUCTION(Mulhu)
433 DEFINE_INSTRUCTION(Dmul)
434 DEFINE_INSTRUCTION(Dmulh)
435 DEFINE_INSTRUCTION2(Mult)
436 DEFINE_INSTRUCTION2(Dmult)
437 DEFINE_INSTRUCTION2(Multu)
438 DEFINE_INSTRUCTION2(Dmultu)
439 DEFINE_INSTRUCTION2(Div)
440 DEFINE_INSTRUCTION2(Ddiv)
441 DEFINE_INSTRUCTION2(Divu)
442 DEFINE_INSTRUCTION2(Ddivu)
443
444 DEFINE_INSTRUCTION(And)
445 DEFINE_INSTRUCTION(Or)
446 DEFINE_INSTRUCTION(Xor)
447 DEFINE_INSTRUCTION(Nor)
448 DEFINE_INSTRUCTION2(Neg)
449
450 DEFINE_INSTRUCTION(Slt)
451 DEFINE_INSTRUCTION(Sltu)
452 DEFINE_INSTRUCTION(Sle)
453 DEFINE_INSTRUCTION(Sleu)
454 DEFINE_INSTRUCTION(Sgt)
455 DEFINE_INSTRUCTION(Sgtu)
456 DEFINE_INSTRUCTION(Sge)
457 DEFINE_INSTRUCTION(Sgeu)
458
459 // MIPS32 R2 instruction macro.
460 DEFINE_INSTRUCTION(Ror)
461 DEFINE_INSTRUCTION(Dror)
462
463 #undef DEFINE_INSTRUCTION
464 #undef DEFINE_INSTRUCTION2
465 #undef DEFINE_INSTRUCTION3
466
467 void SmiUntag(Register dst, const MemOperand& src);
SmiUntag(Register dst,Register src)468 void SmiUntag(Register dst, Register src) {
469 if (SmiValuesAre32Bits()) {
470 dsra32(dst, src, kSmiShift - 32);
471 } else {
472 DCHECK(SmiValuesAre31Bits());
473 sra(dst, src, kSmiShift);
474 }
475 }
476
SmiUntag(Register reg)477 void SmiUntag(Register reg) { SmiUntag(reg, reg); }
478
479 // Removes current frame and its arguments from the stack preserving
480 // the arguments and a return address pushed to the stack for the next call.
481 // Both |callee_args_count| and |caller_args_count| do not include
482 // receiver. |callee_args_count| is not modified. |caller_args_count|
483 // is trashed.
484 void PrepareForTailCall(Register callee_args_count,
485 Register caller_args_count, Register scratch0,
486 Register scratch1);
487
488 int CalculateStackPassedWords(int num_reg_arguments,
489 int num_double_arguments);
490
491 // Before calling a C-function from generated code, align arguments on stack
492 // and add space for the four mips argument slots.
493 // After aligning the frame, non-register arguments must be stored on the
494 // stack, after the argument-slots using helper: CFunctionArgumentOperand().
495 // The argument count assumes all arguments are word sized.
496 // Some compilers/platforms require the stack to be aligned when calling
497 // C++ code.
498 // Needs a scratch register to do some arithmetic. This register will be
499 // trashed.
500 void PrepareCallCFunction(int num_reg_arguments, int num_double_registers,
501 Register scratch);
502 void PrepareCallCFunction(int num_reg_arguments, Register scratch);
503
504 // Arguments 1-4 are placed in registers a0 through a3 respectively.
505 // Arguments 5..n are stored to stack using following:
506 // Sw(a4, CFunctionArgumentOperand(5));
507
508 // Calls a C function and cleans up the space for arguments allocated
509 // by PrepareCallCFunction. The called function is not allowed to trigger a
510 // garbage collection, since that might move the code and invalidate the
511 // return address (unless this is somehow accounted for by the called
512 // function).
513 void CallCFunction(ExternalReference function, int num_arguments);
514 void CallCFunction(Register function, int num_arguments);
515 void CallCFunction(ExternalReference function, int num_reg_arguments,
516 int num_double_arguments);
517 void CallCFunction(Register function, int num_reg_arguments,
518 int num_double_arguments);
519 void MovFromFloatResult(DoubleRegister dst);
520 void MovFromFloatParameter(DoubleRegister dst);
521
522 // There are two ways of passing double arguments on MIPS, depending on
523 // whether soft or hard floating point ABI is used. These functions
524 // abstract parameter passing for the three different ways we call
525 // C functions from generated code.
526 void MovToFloatParameter(DoubleRegister src);
527 void MovToFloatParameters(DoubleRegister src1, DoubleRegister src2);
528 void MovToFloatResult(DoubleRegister src);
529
530 // See comments at the beginning of Builtins::Generate_CEntry.
PrepareCEntryArgs(int num_args)531 inline void PrepareCEntryArgs(int num_args) { li(a0, num_args); }
PrepareCEntryFunction(const ExternalReference & ref)532 inline void PrepareCEntryFunction(const ExternalReference& ref) {
533 li(a1, ref);
534 }
535
536 void CheckPageFlag(Register object, Register scratch, int mask, Condition cc,
537 Label* condition_met);
538 #undef COND_ARGS
539
540 // Performs a truncating conversion of a floating point number as used by
541 // the JS bitwise operations. See ECMA-262 9.5: ToInt32.
542 // Exits with 'result' holding the answer.
543 void TruncateDoubleToI(Isolate* isolate, Zone* zone, Register result,
544 DoubleRegister double_input, StubCallMode stub_mode);
545
546 // Conditional move.
547 void Movz(Register rd, Register rs, Register rt);
548 void Movn(Register rd, Register rs, Register rt);
549 void Movt(Register rd, Register rs, uint16_t cc = 0);
550 void Movf(Register rd, Register rs, uint16_t cc = 0);
551
552 void LoadZeroIfFPUCondition(Register dest);
553 void LoadZeroIfNotFPUCondition(Register dest);
554
555 void LoadZeroIfConditionNotZero(Register dest, Register condition);
556 void LoadZeroIfConditionZero(Register dest, Register condition);
557 void LoadZeroOnCondition(Register rd, Register rs, const Operand& rt,
558 Condition cond);
559
560 void Clz(Register rd, Register rs);
561 void Dclz(Register rd, Register rs);
562 void Ctz(Register rd, Register rs);
563 void Dctz(Register rd, Register rs);
564 void Popcnt(Register rd, Register rs);
565 void Dpopcnt(Register rd, Register rs);
566
567 // MIPS64 R2 instruction macro.
568 void Ext(Register rt, Register rs, uint16_t pos, uint16_t size);
569 void Dext(Register rt, Register rs, uint16_t pos, uint16_t size);
570 void Ins(Register rt, Register rs, uint16_t pos, uint16_t size);
571 void Dins(Register rt, Register rs, uint16_t pos, uint16_t size);
572 void ExtractBits(Register dest, Register source, Register pos, int size,
573 bool sign_extend = false);
574 void InsertBits(Register dest, Register source, Register pos, int size);
575 void Neg_s(FPURegister fd, FPURegister fs);
576 void Neg_d(FPURegister fd, FPURegister fs);
577
578 // MIPS64 R6 instruction macros.
579 void Bovc(Register rt, Register rs, Label* L);
580 void Bnvc(Register rt, Register rs, Label* L);
581
582 // Convert single to unsigned word.
583 void Trunc_uw_s(FPURegister fd, FPURegister fs, FPURegister scratch);
584 void Trunc_uw_s(Register rd, FPURegister fs, FPURegister scratch);
585
586 // Change endianness
587 void ByteSwapSigned(Register dest, Register src, int operand_size);
588 void ByteSwapUnsigned(Register dest, Register src, int operand_size);
589
590 void Ulh(Register rd, const MemOperand& rs);
591 void Ulhu(Register rd, const MemOperand& rs);
592 void Ush(Register rd, const MemOperand& rs, Register scratch);
593
594 void Ulw(Register rd, const MemOperand& rs);
595 void Ulwu(Register rd, const MemOperand& rs);
596 void Usw(Register rd, const MemOperand& rs);
597
598 void Uld(Register rd, const MemOperand& rs);
599 void Usd(Register rd, const MemOperand& rs);
600
601 void Ulwc1(FPURegister fd, const MemOperand& rs, Register scratch);
602 void Uswc1(FPURegister fd, const MemOperand& rs, Register scratch);
603
604 void Uldc1(FPURegister fd, const MemOperand& rs, Register scratch);
605 void Usdc1(FPURegister fd, const MemOperand& rs, Register scratch);
606
607 void Lb(Register rd, const MemOperand& rs);
608 void Lbu(Register rd, const MemOperand& rs);
609 void Sb(Register rd, const MemOperand& rs);
610
611 void Lh(Register rd, const MemOperand& rs);
612 void Lhu(Register rd, const MemOperand& rs);
613 void Sh(Register rd, const MemOperand& rs);
614
615 void Lw(Register rd, const MemOperand& rs);
616 void Lwu(Register rd, const MemOperand& rs);
617 void Sw(Register rd, const MemOperand& rs);
618
619 void Lwc1(FPURegister fd, const MemOperand& src);
620 void Swc1(FPURegister fs, const MemOperand& dst);
621
622 void Ldc1(FPURegister fd, const MemOperand& src);
623 void Sdc1(FPURegister fs, const MemOperand& dst);
624
625 void Ll(Register rd, const MemOperand& rs);
626 void Sc(Register rd, const MemOperand& rs);
627
628 void Lld(Register rd, const MemOperand& rs);
629 void Scd(Register rd, const MemOperand& rs);
630
631 // Perform a floating-point min or max operation with the
632 // (IEEE-754-compatible) semantics of MIPS32's Release 6 MIN.fmt/MAX.fmt.
633 // Some cases, typically NaNs or +/-0.0, are expected to be rare and are
634 // handled in out-of-line code. The specific behaviour depends on supported
635 // instructions.
636 //
637 // These functions assume (and assert) that src1!=src2. It is permitted
638 // for the result to alias either input register.
639 void Float32Max(FPURegister dst, FPURegister src1, FPURegister src2,
640 Label* out_of_line);
641 void Float32Min(FPURegister dst, FPURegister src1, FPURegister src2,
642 Label* out_of_line);
643 void Float64Max(FPURegister dst, FPURegister src1, FPURegister src2,
644 Label* out_of_line);
645 void Float64Min(FPURegister dst, FPURegister src1, FPURegister src2,
646 Label* out_of_line);
647
648 // Generate out-of-line cases for the macros above.
649 void Float32MaxOutOfLine(FPURegister dst, FPURegister src1, FPURegister src2);
650 void Float32MinOutOfLine(FPURegister dst, FPURegister src1, FPURegister src2);
651 void Float64MaxOutOfLine(FPURegister dst, FPURegister src1, FPURegister src2);
652 void Float64MinOutOfLine(FPURegister dst, FPURegister src1, FPURegister src2);
653
IsDoubleZeroRegSet()654 bool IsDoubleZeroRegSet() { return has_double_zero_reg_set_; }
655
mov(Register rd,Register rt)656 void mov(Register rd, Register rt) { or_(rd, rt, zero_reg); }
657
Move(Register dst,Handle<HeapObject> handle)658 inline void Move(Register dst, Handle<HeapObject> handle) { li(dst, handle); }
Move(Register dst,Smi smi)659 inline void Move(Register dst, Smi smi) { li(dst, Operand(smi)); }
660
Move(Register dst,Register src)661 inline void Move(Register dst, Register src) {
662 if (dst != src) {
663 mov(dst, src);
664 }
665 }
666
Move(FPURegister dst,FPURegister src)667 inline void Move(FPURegister dst, FPURegister src) { Move_d(dst, src); }
668
Move(Register dst_low,Register dst_high,FPURegister src)669 inline void Move(Register dst_low, Register dst_high, FPURegister src) {
670 mfc1(dst_low, src);
671 mfhc1(dst_high, src);
672 }
673
Move(Register dst,FPURegister src)674 inline void Move(Register dst, FPURegister src) { dmfc1(dst, src); }
675
Move(FPURegister dst,Register src)676 inline void Move(FPURegister dst, Register src) { dmtc1(src, dst); }
677
FmoveHigh(Register dst_high,FPURegister src)678 inline void FmoveHigh(Register dst_high, FPURegister src) {
679 mfhc1(dst_high, src);
680 }
681
FmoveHigh(FPURegister dst,Register src_high)682 inline void FmoveHigh(FPURegister dst, Register src_high) {
683 mthc1(src_high, dst);
684 }
685
FmoveLow(Register dst_low,FPURegister src)686 inline void FmoveLow(Register dst_low, FPURegister src) {
687 mfc1(dst_low, src);
688 }
689
690 void FmoveLow(FPURegister dst, Register src_low);
691
Move(FPURegister dst,Register src_low,Register src_high)692 inline void Move(FPURegister dst, Register src_low, Register src_high) {
693 mtc1(src_low, dst);
694 mthc1(src_high, dst);
695 }
696
Move_d(FPURegister dst,FPURegister src)697 inline void Move_d(FPURegister dst, FPURegister src) {
698 if (dst != src) {
699 mov_d(dst, src);
700 }
701 }
702
Move_s(FPURegister dst,FPURegister src)703 inline void Move_s(FPURegister dst, FPURegister src) {
704 if (dst != src) {
705 mov_s(dst, src);
706 }
707 }
708
Move(FPURegister dst,float imm)709 void Move(FPURegister dst, float imm) { Move(dst, bit_cast<uint32_t>(imm)); }
Move(FPURegister dst,double imm)710 void Move(FPURegister dst, double imm) { Move(dst, bit_cast<uint64_t>(imm)); }
711 void Move(FPURegister dst, uint32_t src);
712 void Move(FPURegister dst, uint64_t src);
713
714 // DaddOverflow sets overflow register to a negative value if
715 // overflow occured, otherwise it is zero or positive
716 void DaddOverflow(Register dst, Register left, const Operand& right,
717 Register overflow);
718 // DsubOverflow sets overflow register to a negative value if
719 // overflow occured, otherwise it is zero or positive
720 void DsubOverflow(Register dst, Register left, const Operand& right,
721 Register overflow);
722 // MulOverflow sets overflow register to zero if no overflow occured
723 void MulOverflow(Register dst, Register left, const Operand& right,
724 Register overflow);
725
726 // Number of instructions needed for calculation of switch table entry address
727 #ifdef _MIPS_ARCH_MIPS64R6
728 static const int kSwitchTablePrologueSize = 6;
729 #else
730 static const int kSwitchTablePrologueSize = 11;
731 #endif
732
733 // GetLabelFunction must be lambda '[](size_t index) -> Label*' or a
734 // functor/function with 'Label *func(size_t index)' declaration.
735 template <typename Func>
736 void GenerateSwitchTable(Register index, size_t case_count,
737 Func GetLabelFunction);
738
739 // Load an object from the root table.
740 void LoadRoot(Register destination, RootIndex index) override;
741 void LoadRoot(Register destination, RootIndex index, Condition cond,
742 Register src1, const Operand& src2);
743
744 // If the value is a NaN, canonicalize the value else, do nothing.
745 void FPUCanonicalizeNaN(const DoubleRegister dst, const DoubleRegister src);
746
747 // ---------------------------------------------------------------------------
748 // FPU macros. These do not handle special cases like NaN or +- inf.
749
750 // Convert unsigned word to double.
751 void Cvt_d_uw(FPURegister fd, FPURegister fs);
752 void Cvt_d_uw(FPURegister fd, Register rs);
753
754 // Convert unsigned long to double.
755 void Cvt_d_ul(FPURegister fd, FPURegister fs);
756 void Cvt_d_ul(FPURegister fd, Register rs);
757
758 // Convert unsigned word to float.
759 void Cvt_s_uw(FPURegister fd, FPURegister fs);
760 void Cvt_s_uw(FPURegister fd, Register rs);
761
762 // Convert unsigned long to float.
763 void Cvt_s_ul(FPURegister fd, FPURegister fs);
764 void Cvt_s_ul(FPURegister fd, Register rs);
765
766 // Convert double to unsigned word.
767 void Trunc_uw_d(FPURegister fd, FPURegister fs, FPURegister scratch);
768 void Trunc_uw_d(Register rd, FPURegister fs, FPURegister scratch);
769
770 // Convert double to unsigned long.
771 void Trunc_ul_d(FPURegister fd, FPURegister fs, FPURegister scratch,
772 Register result = no_reg);
773 void Trunc_ul_d(Register rd, FPURegister fs, FPURegister scratch,
774 Register result = no_reg);
775
776 // Convert single to unsigned long.
777 void Trunc_ul_s(FPURegister fd, FPURegister fs, FPURegister scratch,
778 Register result = no_reg);
779 void Trunc_ul_s(Register rd, FPURegister fs, FPURegister scratch,
780 Register result = no_reg);
781
782 // Round double functions
783 void Trunc_d_d(FPURegister fd, FPURegister fs);
784 void Round_d_d(FPURegister fd, FPURegister fs);
785 void Floor_d_d(FPURegister fd, FPURegister fs);
786 void Ceil_d_d(FPURegister fd, FPURegister fs);
787
788 // Round float functions
789 void Trunc_s_s(FPURegister fd, FPURegister fs);
790 void Round_s_s(FPURegister fd, FPURegister fs);
791 void Floor_s_s(FPURegister fd, FPURegister fs);
792 void Ceil_s_s(FPURegister fd, FPURegister fs);
793
794 void MSARoundW(MSARegister dst, MSARegister src, FPURoundingMode mode);
795 void MSARoundD(MSARegister dst, MSARegister src, FPURoundingMode mode);
796
797 // Jump the register contains a smi.
798 void JumpIfSmi(Register value, Label* smi_label, Register scratch = at,
799 BranchDelaySlot bd = PROTECT);
800
JumpIfEqual(Register a,int32_t b,Label * dest)801 void JumpIfEqual(Register a, int32_t b, Label* dest) {
802 li(kScratchReg, Operand(b));
803 Branch(dest, eq, a, Operand(kScratchReg));
804 }
805
JumpIfLessThan(Register a,int32_t b,Label * dest)806 void JumpIfLessThan(Register a, int32_t b, Label* dest) {
807 li(kScratchReg, Operand(b));
808 Branch(dest, lt, a, Operand(kScratchReg));
809 }
810
811 // Push a standard frame, consisting of ra, fp, context and JS function.
812 void PushStandardFrame(Register function_reg);
813
814 // Get the actual activation frame alignment for target environment.
815 static int ActivationFrameAlignment();
816
817 // Load Scaled Address instructions. Parameter sa (shift argument) must be
818 // between [1, 31] (inclusive). On pre-r6 architectures the scratch register
819 // may be clobbered.
820 void Lsa(Register rd, Register rs, Register rt, uint8_t sa,
821 Register scratch = at);
822 void Dlsa(Register rd, Register rs, Register rt, uint8_t sa,
823 Register scratch = at);
824
825 // Compute the start of the generated instruction stream from the current PC.
826 // This is an alternative to embedding the {CodeObject} handle as a reference.
827 void ComputeCodeStartAddress(Register dst);
828
829 void ResetSpeculationPoisonRegister();
830
831 // Control-flow integrity:
832
833 // Define a function entrypoint. This doesn't emit any code for this
834 // architecture, as control-flow integrity is not supported for it.
CodeEntry()835 void CodeEntry() {}
836 // Define an exception handler.
ExceptionHandler()837 void ExceptionHandler() {}
838 // Define an exception handler and bind a label.
BindExceptionHandler(Label * label)839 void BindExceptionHandler(Label* label) { bind(label); }
840
841 protected:
842 inline Register GetRtAsRegisterHelper(const Operand& rt, Register scratch);
843 inline int32_t GetOffset(int32_t offset, Label* L, OffsetSize bits);
844
845 private:
846 bool has_double_zero_reg_set_ = false;
847
848 // Performs a truncating conversion of a floating point number as used by
849 // the JS bitwise operations. See ECMA-262 9.5: ToInt32. Goes to 'done' if it
850 // succeeds, otherwise falls through if result is saturated. On return
851 // 'result' either holds answer, or is clobbered on fall through.
852 void TryInlineTruncateDoubleToI(Register result, DoubleRegister input,
853 Label* done);
854
855 void CompareF(SecondaryField sizeField, FPUCondition cc, FPURegister cmp1,
856 FPURegister cmp2);
857
858 void CompareIsNanF(SecondaryField sizeField, FPURegister cmp1,
859 FPURegister cmp2);
860
861 void BranchShortMSA(MSABranchDF df, Label* target, MSABranchCondition cond,
862 MSARegister wt, BranchDelaySlot bd = PROTECT);
863
864 void CallCFunctionHelper(Register function, int num_reg_arguments,
865 int num_double_arguments);
866
867 // TODO(mips) Reorder parameters so out parameters come last.
868 bool CalculateOffset(Label* L, int32_t* offset, OffsetSize bits);
869 bool CalculateOffset(Label* L, int32_t* offset, OffsetSize bits,
870 Register* scratch, const Operand& rt);
871
872 void BranchShortHelperR6(int32_t offset, Label* L);
873 void BranchShortHelper(int16_t offset, Label* L, BranchDelaySlot bdslot);
874 bool BranchShortHelperR6(int32_t offset, Label* L, Condition cond,
875 Register rs, const Operand& rt);
876 bool BranchShortHelper(int16_t offset, Label* L, Condition cond, Register rs,
877 const Operand& rt, BranchDelaySlot bdslot);
878 bool BranchShortCheck(int32_t offset, Label* L, Condition cond, Register rs,
879 const Operand& rt, BranchDelaySlot bdslot);
880
881 void BranchAndLinkShortHelperR6(int32_t offset, Label* L);
882 void BranchAndLinkShortHelper(int16_t offset, Label* L,
883 BranchDelaySlot bdslot);
884 void BranchAndLinkShort(int32_t offset, BranchDelaySlot bdslot = PROTECT);
885 void BranchAndLinkShort(Label* L, BranchDelaySlot bdslot = PROTECT);
886 bool BranchAndLinkShortHelperR6(int32_t offset, Label* L, Condition cond,
887 Register rs, const Operand& rt);
888 bool BranchAndLinkShortHelper(int16_t offset, Label* L, Condition cond,
889 Register rs, const Operand& rt,
890 BranchDelaySlot bdslot);
891 bool BranchAndLinkShortCheck(int32_t offset, Label* L, Condition cond,
892 Register rs, const Operand& rt,
893 BranchDelaySlot bdslot);
894 void BranchLong(Label* L, BranchDelaySlot bdslot);
895 void BranchAndLinkLong(Label* L, BranchDelaySlot bdslot);
896
897 template <typename RoundFunc>
898 void RoundDouble(FPURegister dst, FPURegister src, FPURoundingMode mode,
899 RoundFunc round);
900
901 template <typename RoundFunc>
902 void RoundFloat(FPURegister dst, FPURegister src, FPURoundingMode mode,
903 RoundFunc round);
904
905 // Push a fixed frame, consisting of ra, fp.
906 void PushCommonFrame(Register marker_reg = no_reg);
907
908 void CallRecordWriteStub(Register object, Register address,
909 RememberedSetAction remembered_set_action,
910 SaveFPRegsMode fp_mode, Handle<Code> code_target,
911 Address wasm_target);
912 };
913
914 // MacroAssembler implements a collection of frequently used macros.
915 class V8_EXPORT_PRIVATE MacroAssembler : public TurboAssembler {
916 public:
917 using TurboAssembler::TurboAssembler;
918
919 // It assumes that the arguments are located below the stack pointer.
920 // argc is the number of arguments not including the receiver.
921 // TODO(victorgomes): Remove this function once we stick with the reversed
922 // arguments order.
LoadReceiver(Register dest,Register argc)923 void LoadReceiver(Register dest, Register argc) {
924 Ld(dest, MemOperand(sp, 0));
925 }
926
StoreReceiver(Register rec,Register argc,Register scratch)927 void StoreReceiver(Register rec, Register argc, Register scratch) {
928 Sd(rec, MemOperand(sp, 0));
929 }
930
931 bool IsNear(Label* L, Condition cond, int rs_reg);
932
933 // Swap two registers. If the scratch register is omitted then a slightly
934 // less efficient form using xor instead of mov is emitted.
935 void Swap(Register reg1, Register reg2, Register scratch = no_reg);
936
PushRoot(RootIndex index)937 void PushRoot(RootIndex index) {
938 UseScratchRegisterScope temps(this);
939 Register scratch = temps.Acquire();
940 LoadRoot(scratch, index);
941 Push(scratch);
942 }
943
944 // Compare the object in a register to a value and jump if they are equal.
JumpIfRoot(Register with,RootIndex index,Label * if_equal)945 void JumpIfRoot(Register with, RootIndex index, Label* if_equal) {
946 UseScratchRegisterScope temps(this);
947 Register scratch = temps.Acquire();
948 LoadRoot(scratch, index);
949 Branch(if_equal, eq, with, Operand(scratch));
950 }
951
952 // 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)953 void JumpIfNotRoot(Register with, RootIndex index, Label* if_not_equal) {
954 UseScratchRegisterScope temps(this);
955 Register scratch = temps.Acquire();
956 LoadRoot(scratch, index);
957 Branch(if_not_equal, ne, with, Operand(scratch));
958 }
959
960 // Checks if value is in range [lower_limit, higher_limit] using a single
961 // comparison.
962 void JumpIfIsInRange(Register value, unsigned lower_limit,
963 unsigned higher_limit, Label* on_in_range);
964
965 // ---------------------------------------------------------------------------
966 // GC Support
967
968 // Notify the garbage collector that we wrote a pointer into an object.
969 // |object| is the object being stored into, |value| is the object being
970 // stored. value and scratch registers are clobbered by the operation.
971 // The offset is the offset from the start of the object, not the offset from
972 // the tagged HeapObject pointer. For use with FieldOperand(reg, off).
973 void RecordWriteField(
974 Register object, int offset, Register value, Register scratch,
975 RAStatus ra_status, SaveFPRegsMode save_fp,
976 RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET,
977 SmiCheck smi_check = INLINE_SMI_CHECK);
978
979 // For a given |object| notify the garbage collector that the slot |address|
980 // has been written. |value| is the object being stored. The value and
981 // address registers are clobbered by the operation.
982 void RecordWrite(
983 Register object, Register address, Register value, RAStatus ra_status,
984 SaveFPRegsMode save_fp,
985 RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET,
986 SmiCheck smi_check = INLINE_SMI_CHECK);
987
988 void Pref(int32_t hint, const MemOperand& rs);
989
990 // ---------------------------------------------------------------------------
991 // Pseudo-instructions.
992
993 void LoadWordPair(Register rd, const MemOperand& rs, Register scratch = at);
994 void StoreWordPair(Register rd, const MemOperand& rs, Register scratch = at);
995
996 // Convert double to unsigned long.
997 void Trunc_l_ud(FPURegister fd, FPURegister fs, FPURegister scratch);
998
999 void Trunc_l_d(FPURegister fd, FPURegister fs);
1000 void Round_l_d(FPURegister fd, FPURegister fs);
1001 void Floor_l_d(FPURegister fd, FPURegister fs);
1002 void Ceil_l_d(FPURegister fd, FPURegister fs);
1003
1004 void Trunc_w_d(FPURegister fd, FPURegister fs);
1005 void Round_w_d(FPURegister fd, FPURegister fs);
1006 void Floor_w_d(FPURegister fd, FPURegister fs);
1007 void Ceil_w_d(FPURegister fd, FPURegister fs);
1008
1009 void Madd_s(FPURegister fd, FPURegister fr, FPURegister fs, FPURegister ft,
1010 FPURegister scratch);
1011 void Madd_d(FPURegister fd, FPURegister fr, FPURegister fs, FPURegister ft,
1012 FPURegister scratch);
1013 void Msub_s(FPURegister fd, FPURegister fr, FPURegister fs, FPURegister ft,
1014 FPURegister scratch);
1015 void Msub_d(FPURegister fd, FPURegister fr, FPURegister fs, FPURegister ft,
1016 FPURegister scratch);
1017
1018 void BranchShortMSA(MSABranchDF df, Label* target, MSABranchCondition cond,
1019 MSARegister wt, BranchDelaySlot bd = PROTECT);
1020
1021 // Truncates a double using a specific rounding mode, and writes the value
1022 // to the result register.
1023 // The except_flag will contain any exceptions caused by the instruction.
1024 // If check_inexact is kDontCheckForInexactConversion, then the inexact
1025 // exception is masked.
1026 void EmitFPUTruncate(
1027 FPURoundingMode rounding_mode, Register result,
1028 DoubleRegister double_input, Register scratch,
1029 DoubleRegister double_scratch, Register except_flag,
1030 CheckForInexactConversion check_inexact = kDontCheckForInexactConversion);
1031
1032 // Enter exit frame.
1033 // argc - argument count to be dropped by LeaveExitFrame.
1034 // save_doubles - saves FPU registers on stack, currently disabled.
1035 // stack_space - extra stack space.
1036 void EnterExitFrame(bool save_doubles, int stack_space = 0,
1037 StackFrame::Type frame_type = StackFrame::EXIT);
1038
1039 // Leave the current exit frame.
1040 void LeaveExitFrame(bool save_doubles, Register arg_count,
1041 bool do_return = NO_EMIT_RETURN,
1042 bool argument_count_is_length = false);
1043
1044 void LoadMap(Register destination, Register object);
1045
1046 // Make sure the stack is aligned. Only emits code in debug mode.
1047 void AssertStackIsAligned();
1048
1049 // Load the global proxy from the current context.
LoadGlobalProxy(Register dst)1050 void LoadGlobalProxy(Register dst) {
1051 LoadNativeContextSlot(Context::GLOBAL_PROXY_INDEX, dst);
1052 }
1053
1054 void LoadNativeContextSlot(int index, Register dst);
1055
1056 // Load the initial map from the global function. The registers
1057 // function and map can be the same, function is then overwritten.
1058 void LoadGlobalFunctionInitialMap(Register function, Register map,
1059 Register scratch);
1060
1061 // -------------------------------------------------------------------------
1062 // JavaScript invokes.
1063
1064 // Invoke the JavaScript function code by either calling or jumping.
1065 void InvokeFunctionCode(Register function, Register new_target,
1066 Register expected_parameter_count,
1067 Register actual_parameter_count, InvokeFlag flag);
1068
1069 // On function call, call into the debugger if necessary.
1070 void CheckDebugHook(Register fun, Register new_target,
1071 Register expected_parameter_count,
1072 Register actual_parameter_count);
1073
1074 // Invoke the JavaScript function in the given register. Changes the
1075 // current context to the context in the function before invoking.
1076 void InvokeFunctionWithNewTarget(Register function, Register new_target,
1077 Register actual_parameter_count,
1078 InvokeFlag flag);
1079 void InvokeFunction(Register function, Register expected_parameter_count,
1080 Register actual_parameter_count, InvokeFlag flag);
1081
1082 // Frame restart support.
1083 void MaybeDropFrames();
1084
1085 // Exception handling.
1086
1087 // Push a new stack handler and link into stack handler chain.
1088 void PushStackHandler();
1089
1090 // Unlink the stack handler on top of the stack from the stack handler chain.
1091 // Must preserve the result register.
1092 void PopStackHandler();
1093
1094 // -------------------------------------------------------------------------
1095 // Support functions.
1096
1097 void GetObjectType(Register function, Register map, Register type_reg);
1098
1099 // -------------------------------------------------------------------------
1100 // Runtime calls.
1101
1102 // Call a runtime routine.
1103 void CallRuntime(const Runtime::Function* f, int num_arguments,
1104 SaveFPRegsMode save_doubles = kDontSaveFPRegs);
1105
1106 // Convenience function: Same as above, but takes the fid instead.
1107 void CallRuntime(Runtime::FunctionId fid,
1108 SaveFPRegsMode save_doubles = kDontSaveFPRegs) {
1109 const Runtime::Function* function = Runtime::FunctionForId(fid);
1110 CallRuntime(function, function->nargs, save_doubles);
1111 }
1112
1113 // Convenience function: Same as above, but takes the fid instead.
1114 void CallRuntime(Runtime::FunctionId fid, int num_arguments,
1115 SaveFPRegsMode save_doubles = kDontSaveFPRegs) {
1116 CallRuntime(Runtime::FunctionForId(fid), num_arguments, save_doubles);
1117 }
1118
1119 // Convenience function: tail call a runtime routine (jump).
1120 void TailCallRuntime(Runtime::FunctionId fid);
1121
1122 // Jump to the builtin routine.
1123 void JumpToExternalReference(const ExternalReference& builtin,
1124 BranchDelaySlot bd = PROTECT,
1125 bool builtin_exit_frame = false);
1126
1127 // Generates a trampoline to jump to the off-heap instruction stream.
1128 void JumpToInstructionStream(Address entry);
1129
1130 // ---------------------------------------------------------------------------
1131 // In-place weak references.
1132 void LoadWeakValue(Register out, Register in, Label* target_if_cleared);
1133
1134 // -------------------------------------------------------------------------
1135 // StatsCounter support.
1136
1137 void IncrementCounter(StatsCounter* counter, int value, Register scratch1,
1138 Register scratch2);
1139 void DecrementCounter(StatsCounter* counter, int value, Register scratch1,
1140 Register scratch2);
1141
1142 // -------------------------------------------------------------------------
1143 // Stack limit utilities
1144
1145 enum StackLimitKind { kInterruptStackLimit, kRealStackLimit };
1146 void LoadStackLimit(Register destination, StackLimitKind kind);
1147 void StackOverflowCheck(Register num_args, Register scratch1,
1148 Register scratch2, Label* stack_overflow);
1149
1150 // ---------------------------------------------------------------------------
1151 // Smi utilities.
1152
SmiTag(Register dst,Register src)1153 void SmiTag(Register dst, Register src) {
1154 STATIC_ASSERT(kSmiTag == 0);
1155 if (SmiValuesAre32Bits()) {
1156 dsll32(dst, src, 0);
1157 } else {
1158 DCHECK(SmiValuesAre31Bits());
1159 Addu(dst, src, src);
1160 }
1161 }
1162
SmiTag(Register reg)1163 void SmiTag(Register reg) { SmiTag(reg, reg); }
1164
1165 // Left-shifted from int32 equivalent of Smi.
SmiScale(Register dst,Register src,int scale)1166 void SmiScale(Register dst, Register src, int scale) {
1167 if (SmiValuesAre32Bits()) {
1168 // The int portion is upper 32-bits of 64-bit word.
1169 dsra(dst, src, kSmiShift - scale);
1170 } else {
1171 DCHECK(SmiValuesAre31Bits());
1172 DCHECK_GE(scale, kSmiTagSize);
1173 sll(dst, src, scale - kSmiTagSize);
1174 }
1175 }
1176
1177 // Test if the register contains a smi.
SmiTst(Register value,Register scratch)1178 inline void SmiTst(Register value, Register scratch) {
1179 And(scratch, value, Operand(kSmiTagMask));
1180 }
1181
1182 // Jump if the register contains a non-smi.
1183 void JumpIfNotSmi(Register value, Label* not_smi_label, Register scratch = at,
1184 BranchDelaySlot bd = PROTECT);
1185
1186 // Abort execution if argument is a smi, enabled via --debug-code.
1187 void AssertNotSmi(Register object);
1188 void AssertSmi(Register object);
1189
1190 // Abort execution if argument is not a Constructor, enabled via --debug-code.
1191 void AssertConstructor(Register object);
1192
1193 // Abort execution if argument is not a JSFunction, enabled via --debug-code.
1194 void AssertFunction(Register object);
1195
1196 // Abort execution if argument is not a JSBoundFunction,
1197 // enabled via --debug-code.
1198 void AssertBoundFunction(Register object);
1199
1200 // Abort execution if argument is not a JSGeneratorObject (or subclass),
1201 // enabled via --debug-code.
1202 void AssertGeneratorObject(Register object);
1203
1204 // Abort execution if argument is not undefined or an AllocationSite, enabled
1205 // via --debug-code.
1206 void AssertUndefinedOrAllocationSite(Register object, Register scratch);
1207
1208 template <typename Field>
DecodeField(Register dst,Register src)1209 void DecodeField(Register dst, Register src) {
1210 Ext(dst, src, Field::kShift, Field::kSize);
1211 }
1212
1213 template <typename Field>
DecodeField(Register reg)1214 void DecodeField(Register reg) {
1215 DecodeField<Field>(reg, reg);
1216 }
1217
1218 private:
1219 // Helper functions for generating invokes.
1220 void InvokePrologue(Register expected_parameter_count,
1221 Register actual_parameter_count, Label* done,
1222 InvokeFlag flag);
1223
1224 // Compute memory operands for safepoint stack slots.
1225 static int SafepointRegisterStackIndex(int reg_code);
1226
1227 // Needs access to SafepointRegisterStackIndex for compiled frame
1228 // traversal.
1229 friend class CommonFrame;
1230
1231 DISALLOW_IMPLICIT_CONSTRUCTORS(MacroAssembler);
1232 };
1233
1234 template <typename Func>
GenerateSwitchTable(Register index,size_t case_count,Func GetLabelFunction)1235 void TurboAssembler::GenerateSwitchTable(Register index, size_t case_count,
1236 Func GetLabelFunction) {
1237 // Ensure that dd-ed labels following this instruction use 8 bytes aligned
1238 // addresses.
1239 BlockTrampolinePoolFor(static_cast<int>(case_count) * 2 +
1240 kSwitchTablePrologueSize);
1241 UseScratchRegisterScope temps(this);
1242 Register scratch = temps.Acquire();
1243 if (kArchVariant >= kMips64r6) {
1244 // Opposite of Align(8) as we have odd number of instructions in this case.
1245 if ((pc_offset() & 7) == 0) {
1246 nop();
1247 }
1248 addiupc(scratch, 5);
1249 Dlsa(scratch, scratch, index, kPointerSizeLog2);
1250 Ld(scratch, MemOperand(scratch));
1251 } else {
1252 Label here;
1253 Align(8);
1254 push(ra);
1255 bal(&here);
1256 dsll(scratch, index, kPointerSizeLog2); // Branch delay slot.
1257 bind(&here);
1258 daddu(scratch, scratch, ra);
1259 pop(ra);
1260 Ld(scratch, MemOperand(scratch, 6 * v8::internal::kInstrSize));
1261 }
1262 jr(scratch);
1263 nop(); // Branch delay slot nop.
1264 for (size_t index = 0; index < case_count; ++index) {
1265 dd(GetLabelFunction(index));
1266 }
1267 }
1268
1269 #define ACCESS_MASM(masm) masm->
1270
1271 } // namespace internal
1272 } // namespace v8
1273
1274 #endif // V8_CODEGEN_MIPS64_MACRO_ASSEMBLER_MIPS64_H_
1275