macro-assembler-mips64.h 49.9 KB
Newer Older
1 2 3 4
// Copyright 2012 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5 6 7 8
#ifndef INCLUDED_FROM_MACRO_ASSEMBLER_H
#error This header must be included via macro-assembler.h
#endif

9 10
#ifndef V8_CODEGEN_MIPS64_MACRO_ASSEMBLER_MIPS64_H_
#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 17 18

namespace v8 {
namespace internal {

Marja Hölttä's avatar
Marja Hölttä committed
19
// Forward declarations.
20
enum class AbortReason : uint8_t;
21 22 23 24 25 26 27 28 29 30 31 32 33 34

// Reserved Register Usage Summary.
//
// Registers t8, t9, and at are reserved for use by the MacroAssembler.
//
// The programmer should know that the MacroAssembler may clobber these three,
// but won't touch other registers except in special cases.
//
// Per the MIPS ABI, register t9 must be used for indirect function call
// via 'jalr t9' or 'jr t9' instructions. This is relied upon by gcc when
// trying to update gp register for position-independent-code. Whenever
// MIPS generated code calls C code, it must be via t9 register.

// 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 41 42 43

// Flags used for the li macro-assembler function.
enum LiFlags {
  // If the constant value can be represented in just 16 bits, then
  // optimize the li to use a single instruction, rather than lui/ori/dsll
44 45
  // sequence. A number of other optimizations that emits less than
  // maximum number of instructions exists.
46
  OPTIMIZE_SIZE = 0,
47 48
  // Always use 6 instructions (lui/ori/dsll sequence) for release 2 or 4
  // instructions for release 6 (lui/ori/dahi/dati), even if the constant
49 50 51 52 53 54
  // could be loaded with just one, so that this value is patchable later.
  CONSTANT_SIZE = 1,
  // For address loads only 4 instruction are required. Used to mark
  // constant load that will be used as address without relocation
  // information. It ensures predictable code size, so specific sites
  // in code are patchable.
55
  ADDRESS_LOAD = 2
56 57 58 59 60 61
};

enum RememberedSetAction { EMIT_REMEMBERED_SET, OMIT_REMEMBERED_SET };
enum SmiCheck { INLINE_SMI_CHECK, OMIT_SMI_CHECK };
enum RAStatus { kRAHasNotBeenSaved, kRAHasBeenSaved };

62
Register GetRegisterThatIsNotOneOf(Register reg1, Register reg2 = no_reg,
63 64 65 66 67 68 69 70
                                   Register reg3 = no_reg,
                                   Register reg4 = no_reg,
                                   Register reg5 = no_reg,
                                   Register reg6 = no_reg);

// -----------------------------------------------------------------------------
// Static helper functions.

71 72 73 74 75 76
#if defined(V8_TARGET_LITTLE_ENDIAN)
#define SmiWordOffset(offset) (offset + kPointerSize / 2)
#else
#define SmiWordOffset(offset) offset
#endif

77 78 79 80 81 82 83 84 85 86 87
// Generate a MemOperand for loading a field from an object.
inline MemOperand FieldMemOperand(Register object, int offset) {
  return MemOperand(object, offset - kHeapObjectTag);
}

// Generate a MemOperand for storing arguments 5..N on the stack
// when calling CallCFunction().
// TODO(plind): Currently ONLY used for O32. Should be fixed for
//              n64, and used in RegExp code, and other places
//              with more than 8 arguments.
inline MemOperand CFunctionArgumentOperand(int index) {
88
  DCHECK_GT(index, kCArgSlotCount);
89 90 91 92 93
  // Argument 5 takes the slot just past the four Arg-slots.
  int offset = (index - 5) * kPointerSize + kCArgsSlotsSize;
  return MemOperand(sp, offset);
}

94
class V8_EXPORT_PRIVATE TurboAssembler : public TurboAssemblerBase {
95
 public:
96
  using TurboAssemblerBase::TurboAssemblerBase;
97 98 99 100 101 102 103 104 105 106 107

  // Activation support.
  void EnterFrame(StackFrame::Type type);
  void EnterFrame(StackFrame::Type type, bool load_constant_pool_pointer_reg) {
    // Out-of-line constant pool not implemented on mips.
    UNREACHABLE();
  }
  void LeaveFrame(StackFrame::Type type);

  // Generates function and stub prologue code.
  void StubPrologue(StackFrame::Type type);
108
  void Prologue();
109 110

  void InitializeRootRegister() {
111 112
    ExternalReference isolate_root = ExternalReference::isolate_root(isolate());
    li(kRootRegister, Operand(isolate_root));
113 114 115 116 117 118 119 120 121 122 123 124
  }

  // Jump unconditionally to given label.
  // We NEED a nop in the branch delay slot, as it used by v8, for example in
  // CodeGenerator::ProcessDeferred().
  // Currently the branch delay slot is filled by the MacroAssembler.
  // Use rather b(Label) for code generation.
  void jmp(Label* L) { Branch(L); }

  // -------------------------------------------------------------------------
  // Debugging.

125
  void Trap() override;
126
  void DebugBreak() override;
127

128 129
  // Calls Abort(msg) if the condition cc is not satisfied.
  // 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 141 142
#define COND_ARGS cond, r1, r2

  // Cases when relocation is not needed.
143 144 145 146 147 148 149 150 151
#define DECLARE_NORELOC_PROTOTYPE(Name, target_type)                          \
  void Name(target_type target, BranchDelaySlot bd = PROTECT);                \
  inline void Name(BranchDelaySlot bd, target_type target) {                  \
    Name(target, bd);                                                         \
  }                                                                           \
  void Name(target_type target, COND_TYPED_ARGS,                              \
            BranchDelaySlot bd = PROTECT);                                    \
  inline void Name(BranchDelaySlot bd, target_type target, COND_TYPED_ARGS) { \
    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 159 160 161 162 163 164 165

  DECLARE_BRANCH_PROTOTYPES(Branch)
  DECLARE_BRANCH_PROTOTYPES(BranchAndLink)
  DECLARE_BRANCH_PROTOTYPES(BranchShort)

#undef DECLARE_BRANCH_PROTOTYPES
#undef COND_TYPED_ARGS
#undef COND_ARGS

166 167 168
  // Floating point branches
  void CompareF32(FPUCondition cc, FPURegister cmp1, FPURegister cmp2) {
    CompareF(S, cc, cmp1, cmp2);
169 170
  }

171 172
  void CompareIsNanF32(FPURegister cmp1, FPURegister cmp2) {
    CompareIsNanF(S, cmp1, cmp2);
173 174
  }

175 176
  void CompareF64(FPUCondition cc, FPURegister cmp1, FPURegister cmp2) {
    CompareF(D, cc, cmp1, cmp2);
177 178
  }

179 180
  void CompareIsNanF64(FPURegister cmp1, FPURegister cmp2) {
    CompareIsNanF(D, cmp1, cmp2);
181 182
  }

183 184
  void BranchTrueShortF(Label* target, BranchDelaySlot bd = PROTECT);
  void BranchFalseShortF(Label* target, BranchDelaySlot bd = PROTECT);
185

186 187
  void BranchTrueF(Label* target, BranchDelaySlot bd = PROTECT);
  void BranchFalseF(Label* target, BranchDelaySlot bd = PROTECT);
188

189
  // MSA branches
190 191 192
  void BranchMSA(Label* target, MSABranchDF df, MSABranchCondition cond,
                 MSARegister wt, BranchDelaySlot bd = PROTECT);

193
  void Branch(Label* L, Condition cond, Register rs, RootIndex index,
194 195 196 197 198 199 200 201 202 203
              BranchDelaySlot bdslot = PROTECT);

  static int InstrCountForLi64Bit(int64_t value);
  inline void LiLower32BitHelper(Register rd, Operand j);
  void li_optimized(Register rd, Operand j, LiFlags mode = OPTIMIZE_SIZE);
  // Load int32 in the rd register.
  void li(Register rd, Operand j, LiFlags mode = OPTIMIZE_SIZE);
  inline void li(Register rd, int64_t j, LiFlags mode = OPTIMIZE_SIZE) {
    li(rd, Operand(j), mode);
  }
204 205 206
  // inline void li(Register rd, int32_t j, LiFlags mode = OPTIMIZE_SIZE) {
  //   li(rd, Operand(static_cast<int64_t>(j)), mode);
  // }
207
  void li(Register dst, Handle<HeapObject> value, LiFlags mode = OPTIMIZE_SIZE);
208
  void li(Register dst, ExternalReference value, LiFlags mode = OPTIMIZE_SIZE);
209 210
  void li(Register dst, const StringConstantBase* string,
          LiFlags mode = OPTIMIZE_SIZE);
211

212 213
  void LoadFromConstantsTable(Register destination,
                              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 219 220 221
#define COND_ARGS                                  \
  Condition cond = al, Register rs = zero_reg,     \
            const Operand &rt = Operand(zero_reg), \
            BranchDelaySlot bd = PROTECT
222 223 224 225

  void Jump(Register target, COND_ARGS);
  void Jump(intptr_t target, RelocInfo::Mode rmode, COND_ARGS);
  void Jump(Address target, RelocInfo::Mode rmode, COND_ARGS);
226 227 228 229
  // Deffer from li, this method save target to the memory, and then load
  // it to register use ld, it can be used in wasm jump table for concurrent
  // patching.
  void PatchAndJump(Address target);
230
  void Jump(Handle<Code> code, RelocInfo::Mode rmode, COND_ARGS);
231
  void Jump(const ExternalReference& reference) override;
232 233
  void Call(Register target, COND_ARGS);
  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 240 241
  // Load the builtin given by the Smi in |builtin_index| into the same
  // register.
  void LoadEntryFromBuiltinIndex(Register builtin_index);
242
  void CallBuiltinByIndex(Register builtin_index) override;
243

244 245 246 247 248 249 250 251 252
  void LoadCodeObjectEntry(Register destination,
                           Register code_object) override {
    // TODO(mips): Implement.
    UNIMPLEMENTED();
  }
  void CallCodeObject(Register code_object) override {
    // TODO(mips): Implement.
    UNIMPLEMENTED();
  }
253 254
  void JumpCodeObject(Register code_object,
                      JumpMode jump_mode = JumpMode::kJump) override {
255 256 257 258
    // TODO(mips): Implement.
    UNIMPLEMENTED();
  }

259 260 261 262 263
  // Generates an instruction sequence s.t. the return address points to the
  // instruction following the call.
  // The return address on the stack is used by frame iteration.
  void StoreReturnAddressAndCall(Register target);

264
  void CallForDeoptimization(Builtins::Name target, int deopt_id, Label* exit,
265
                             DeoptimizeKind kind, Label* ret,
266
                             Label* jump_deoptimization_entry_label);
267

268 269
  void Ret(COND_ARGS);
  inline void Ret(BranchDelaySlot bd, Condition cond = al,
270 271
                  Register rs = zero_reg,
                  const Operand& rt = Operand(zero_reg)) {
272 273 274 275 276
    Ret(cond, rs, rt, bd);
  }

  // Emit code to discard a non-negative number of pointer-sized elements
  // from the stack, clobbering only the sp register.
277
  void Drop(int count, Condition cond = cc_always, Register reg = no_reg,
278 279
            const Operand& op = Operand(no_reg));

280
  // Trivial case of DropAndRet that utilizes the delay slot.
281 282
  void DropAndRet(int drop);

283
  void DropAndRet(int drop, Condition cond, Register reg, const Operand& op);
284

285 286
  void Ld(Register rd, const MemOperand& rs);
  void Sd(Register rd, const MemOperand& rs);
287

288 289 290
  void push(Register src) {
    Daddu(sp, sp, Operand(-kPointerSize));
    Sd(src, MemOperand(sp, 0));
291
  }
292 293
  void Push(Register src) { push(src); }
  void Push(Handle<HeapObject> handle);
294
  void Push(Smi smi);
295

296 297 298 299 300
  // Push two registers. Pushes leftmost register first (to highest address).
  void Push(Register src1, Register src2) {
    Dsubu(sp, sp, Operand(2 * kPointerSize));
    Sd(src1, MemOperand(sp, 1 * kPointerSize));
    Sd(src2, MemOperand(sp, 0 * kPointerSize));
301 302
  }

303 304 305 306 307 308
  // Push three registers. Pushes leftmost register first (to highest address).
  void Push(Register src1, Register src2, Register src3) {
    Dsubu(sp, sp, Operand(3 * kPointerSize));
    Sd(src1, MemOperand(sp, 2 * kPointerSize));
    Sd(src2, MemOperand(sp, 1 * kPointerSize));
    Sd(src3, MemOperand(sp, 0 * kPointerSize));
309 310
  }

311 312 313 314 315 316 317
  // Push four registers. Pushes leftmost register first (to highest address).
  void Push(Register src1, Register src2, Register src3, Register src4) {
    Dsubu(sp, sp, Operand(4 * kPointerSize));
    Sd(src1, MemOperand(sp, 3 * kPointerSize));
    Sd(src2, MemOperand(sp, 2 * kPointerSize));
    Sd(src3, MemOperand(sp, 1 * kPointerSize));
    Sd(src4, MemOperand(sp, 0 * kPointerSize));
318 319
  }

320 321 322 323 324 325 326 327 328
  // Push five registers. Pushes leftmost register first (to highest address).
  void Push(Register src1, Register src2, Register src3, Register src4,
            Register src5) {
    Dsubu(sp, sp, Operand(5 * kPointerSize));
    Sd(src1, MemOperand(sp, 4 * kPointerSize));
    Sd(src2, MemOperand(sp, 3 * kPointerSize));
    Sd(src3, MemOperand(sp, 2 * kPointerSize));
    Sd(src4, MemOperand(sp, 1 * kPointerSize));
    Sd(src5, MemOperand(sp, 0 * kPointerSize));
329 330
  }

331 332 333 334 335
  void Push(Register src, Condition cond, Register tst1, Register tst2) {
    // Since we don't have conditional execution we use a Branch.
    Branch(3, cond, tst1, Operand(tst2));
    Dsubu(sp, sp, Operand(kPointerSize));
    Sd(src, MemOperand(sp, 0));
336 337
  }

338 339 340 341
  enum PushArrayOrder { kNormal, kReverse };
  void PushArray(Register array, Register size, Register scratch,
                 Register scratch2, PushArrayOrder order = kNormal);

342 343 344 345 346 347
  void SaveRegisters(RegList registers);
  void RestoreRegisters(RegList registers);

  void CallRecordWriteStub(Register object, Register address,
                           RememberedSetAction remembered_set_action,
                           SaveFPRegsMode fp_mode);
348 349 350
  void CallRecordWriteStub(Register object, Register address,
                           RememberedSetAction remembered_set_action,
                           SaveFPRegsMode fp_mode, Address wasm_target);
351
  void CallEphemeronKeyBarrier(Register object, Register address,
352
                               SaveFPRegsMode fp_mode);
353

354 355 356 357 358
  // Push multiple registers on the stack.
  // Registers are saved in numerical order, with higher numbered registers
  // saved in higher memory addresses.
  void MultiPush(RegList regs);
  void MultiPushFPU(RegList regs);
359
  void MultiPushMSA(RegList regs);
360

361 362 363 364 365 366 367 368 369 370
  // Calculate how much stack space (in bytes) are required to store caller
  // registers excluding those specified in the arguments.
  int RequiredStackSizeForCallerSaved(SaveFPRegsMode fp_mode,
                                      Register exclusion1 = no_reg,
                                      Register exclusion2 = no_reg,
                                      Register exclusion3 = no_reg) const;

  // Push caller saved registers on the stack, and return the number of bytes
  // stack pointer is adjusted.
  int PushCallerSaved(SaveFPRegsMode fp_mode, Register exclusion1 = no_reg,
371 372
                      Register exclusion2 = no_reg,
                      Register exclusion3 = no_reg);
373 374 375 376 377
  // Restore caller saved registers from the stack, and return the number of
  // bytes stack pointer is adjusted.
  int PopCallerSaved(SaveFPRegsMode fp_mode, Register exclusion1 = no_reg,
                     Register exclusion2 = no_reg,
                     Register exclusion3 = no_reg);
378

379 380 381 382 383
  void pop(Register dst) {
    Ld(dst, MemOperand(sp, 0));
    Daddu(sp, sp, Operand(kPointerSize));
  }
  void Pop(Register dst) { pop(dst); }
384

385 386
  // Pop two registers. Pops rightmost register first (from lower address).
  void Pop(Register src1, Register src2) {
387
    DCHECK(src1 != src2);
388 389 390
    Ld(src2, MemOperand(sp, 0 * kPointerSize));
    Ld(src1, MemOperand(sp, 1 * kPointerSize));
    Daddu(sp, sp, 2 * kPointerSize);
391 392
  }

393 394 395 396 397 398 399
  // Pop three registers. Pops rightmost register first (from lower address).
  void Pop(Register src1, Register src2, Register src3) {
    Ld(src3, MemOperand(sp, 0 * kPointerSize));
    Ld(src2, MemOperand(sp, 1 * kPointerSize));
    Ld(src1, MemOperand(sp, 2 * kPointerSize));
    Daddu(sp, sp, 3 * kPointerSize);
  }
400

401
  void Pop(uint32_t count = 1) { Daddu(sp, sp, Operand(count * kPointerSize)); }
402

403 404 405 406
  // Pops multiple values from the stack and load them in the
  // registers specified in regs. Pop order is the opposite as in MultiPush.
  void MultiPop(RegList regs);
  void MultiPopFPU(RegList regs);
407
  void MultiPopMSA(RegList regs);
408

409 410 411 412 413 414
#define DEFINE_INSTRUCTION(instr)                          \
  void instr(Register rd, Register rs, const Operand& rt); \
  void instr(Register rd, Register rs, Register rt) {      \
    instr(rd, rs, Operand(rt));                            \
  }                                                        \
  void instr(Register rs, Register rt, int32_t j) { instr(rs, rt, Operand(j)); }
415

416 417 418 419
#define DEFINE_INSTRUCTION2(instr)                                 \
  void instr(Register rs, const Operand& rt);                      \
  void instr(Register rs, Register rt) { instr(rs, Operand(rt)); } \
  void instr(Register rs, int32_t j) { instr(rs, Operand(j)); }
420

Yu Yin's avatar
Yu Yin committed
421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460
  DEFINE_INSTRUCTION(Addu)
  DEFINE_INSTRUCTION(Daddu)
  DEFINE_INSTRUCTION(Div)
  DEFINE_INSTRUCTION(Divu)
  DEFINE_INSTRUCTION(Ddivu)
  DEFINE_INSTRUCTION(Mod)
  DEFINE_INSTRUCTION(Modu)
  DEFINE_INSTRUCTION(Ddiv)
  DEFINE_INSTRUCTION(Subu)
  DEFINE_INSTRUCTION(Dsubu)
  DEFINE_INSTRUCTION(Dmod)
  DEFINE_INSTRUCTION(Dmodu)
  DEFINE_INSTRUCTION(Mul)
  DEFINE_INSTRUCTION(Mulh)
  DEFINE_INSTRUCTION(Mulhu)
  DEFINE_INSTRUCTION(Dmul)
  DEFINE_INSTRUCTION(Dmulh)
  DEFINE_INSTRUCTION2(Mult)
  DEFINE_INSTRUCTION2(Dmult)
  DEFINE_INSTRUCTION2(Multu)
  DEFINE_INSTRUCTION2(Dmultu)
  DEFINE_INSTRUCTION2(Div)
  DEFINE_INSTRUCTION2(Ddiv)
  DEFINE_INSTRUCTION2(Divu)
  DEFINE_INSTRUCTION2(Ddivu)

  DEFINE_INSTRUCTION(And)
  DEFINE_INSTRUCTION(Or)
  DEFINE_INSTRUCTION(Xor)
  DEFINE_INSTRUCTION(Nor)
  DEFINE_INSTRUCTION2(Neg)

  DEFINE_INSTRUCTION(Slt)
  DEFINE_INSTRUCTION(Sltu)
  DEFINE_INSTRUCTION(Sle)
  DEFINE_INSTRUCTION(Sleu)
  DEFINE_INSTRUCTION(Sgt)
  DEFINE_INSTRUCTION(Sgtu)
  DEFINE_INSTRUCTION(Sge)
  DEFINE_INSTRUCTION(Sgeu)
461 462

  // MIPS32 R2 instruction macro.
Yu Yin's avatar
Yu Yin committed
463 464
  DEFINE_INSTRUCTION(Ror)
  DEFINE_INSTRUCTION(Dror)
465 466 467 468 469

#undef DEFINE_INSTRUCTION
#undef DEFINE_INSTRUCTION2
#undef DEFINE_INSTRUCTION3

470
  void SmiUntag(Register dst, const MemOperand& src);
471 472
  void SmiUntag(Register dst, Register src) {
    if (SmiValuesAre32Bits()) {
473
      dsra32(dst, src, kSmiShift - 32);
474
    } else {
475 476
      DCHECK(SmiValuesAre31Bits());
      sra(dst, src, kSmiShift);
477 478 479 480 481 482 483
    }
  }

  void SmiUntag(Register reg) { SmiUntag(reg, reg); }

  // Removes current frame and its arguments from the stack preserving
  // the arguments and a return address pushed to the stack for the next call.
484 485
  // Both |callee_args_count| and |caller_args_count| do not include
  // receiver. |callee_args_count| is not modified. |caller_args_count|
486
  // is trashed.
487 488
  void PrepareForTailCall(Register callee_args_count,
                          Register caller_args_count, Register scratch0,
489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506
                          Register scratch1);

  int CalculateStackPassedWords(int num_reg_arguments,
                                int num_double_arguments);

  // Before calling a C-function from generated code, align arguments on stack
  // and add space for the four mips argument slots.
  // After aligning the frame, non-register arguments must be stored on the
  // stack, after the argument-slots using helper: CFunctionArgumentOperand().
  // The argument count assumes all arguments are word sized.
  // Some compilers/platforms require the stack to be aligned when calling
  // C++ code.
  // Needs a scratch register to do some arithmetic. This register will be
  // trashed.
  void PrepareCallCFunction(int num_reg_arguments, int num_double_registers,
                            Register scratch);
  void PrepareCallCFunction(int num_reg_arguments, Register scratch);

507
  // Arguments 1-4 are placed in registers a0 through a3 respectively.
508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532
  // Arguments 5..n are stored to stack using following:
  //  Sw(a4, CFunctionArgumentOperand(5));

  // Calls a C function and cleans up the space for arguments allocated
  // by PrepareCallCFunction. The called function is not allowed to trigger a
  // garbage collection, since that might move the code and invalidate the
  // return address (unless this is somehow accounted for by the called
  // function).
  void CallCFunction(ExternalReference function, int num_arguments);
  void CallCFunction(Register function, int num_arguments);
  void CallCFunction(ExternalReference function, int num_reg_arguments,
                     int num_double_arguments);
  void CallCFunction(Register function, int num_reg_arguments,
                     int num_double_arguments);
  void MovFromFloatResult(DoubleRegister dst);
  void MovFromFloatParameter(DoubleRegister dst);

  // There are two ways of passing double arguments on MIPS, depending on
  // whether soft or hard floating point ABI is used. These functions
  // abstract parameter passing for the three different ways we call
  // C functions from generated code.
  void MovToFloatParameter(DoubleRegister src);
  void MovToFloatParameters(DoubleRegister src1, DoubleRegister src2);
  void MovToFloatResult(DoubleRegister src);

533
  // See comments at the beginning of Builtins::Generate_CEntry.
534 535
  inline void PrepareCEntryArgs(int num_args) { li(a0, num_args); }
  inline void PrepareCEntryFunction(const ExternalReference& ref) {
536
    li(a1, ref);
537 538 539 540 541 542 543 544 545
  }

  void CheckPageFlag(Register object, Register scratch, int mask, Condition cc,
                     Label* condition_met);
#undef COND_ARGS

  // Performs a truncating conversion of a floating point number as used by
  // the JS bitwise operations. See ECMA-262 9.5: ToInt32.
  // Exits with 'result' holding the answer.
546
  void TruncateDoubleToI(Isolate* isolate, Zone* zone, Register result,
547
                         DoubleRegister double_input, StubCallMode stub_mode);
548 549 550 551 552 553 554

  // Conditional move.
  void Movz(Register rd, Register rs, Register rt);
  void Movn(Register rd, Register rs, Register rt);
  void Movt(Register rd, Register rs, uint16_t cc = 0);
  void Movf(Register rd, Register rs, uint16_t cc = 0);

555 556 557
  void LoadZeroIfFPUCondition(Register dest);
  void LoadZeroIfNotFPUCondition(Register dest);

558 559 560 561 562
  void LoadZeroIfConditionNotZero(Register dest, Register condition);
  void LoadZeroIfConditionZero(Register dest, Register condition);
  void LoadZeroOnCondition(Register rd, Register rs, const Operand& rt,
                           Condition cond);

563
  void Clz(Register rd, Register rs);
564
  void Dclz(Register rd, Register rs);
565 566 567 568
  void Ctz(Register rd, Register rs);
  void Dctz(Register rd, Register rs);
  void Popcnt(Register rd, Register rs);
  void Dpopcnt(Register rd, Register rs);
569 570 571 572 573 574

  // MIPS64 R2 instruction macro.
  void Ext(Register rt, Register rs, uint16_t pos, uint16_t size);
  void Dext(Register rt, Register rs, uint16_t pos, uint16_t size);
  void Ins(Register rt, Register rs, uint16_t pos, uint16_t size);
  void Dins(Register rt, Register rs, uint16_t pos, uint16_t size);
575 576 577
  void ExtractBits(Register dest, Register source, Register pos, int size,
                   bool sign_extend = false);
  void InsertBits(Register dest, Register source, Register pos, int size);
578 579 580 581 582 583 584 585 586
  void Neg_s(FPURegister fd, FPURegister fs);
  void Neg_d(FPURegister fd, FPURegister fs);

  // MIPS64 R6 instruction macros.
  void Bovc(Register rt, Register rs, Label* L);
  void Bnvc(Register rt, Register rs, Label* L);

  // Convert single to unsigned word.
  void Trunc_uw_s(FPURegister fd, FPURegister fs, FPURegister scratch);
587
  void Trunc_uw_s(Register rd, FPURegister fs, FPURegister scratch);
588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627

  // Change endianness
  void ByteSwapSigned(Register dest, Register src, int operand_size);
  void ByteSwapUnsigned(Register dest, Register src, int operand_size);

  void Ulh(Register rd, const MemOperand& rs);
  void Ulhu(Register rd, const MemOperand& rs);
  void Ush(Register rd, const MemOperand& rs, Register scratch);

  void Ulw(Register rd, const MemOperand& rs);
  void Ulwu(Register rd, const MemOperand& rs);
  void Usw(Register rd, const MemOperand& rs);

  void Uld(Register rd, const MemOperand& rs);
  void Usd(Register rd, const MemOperand& rs);

  void Ulwc1(FPURegister fd, const MemOperand& rs, Register scratch);
  void Uswc1(FPURegister fd, const MemOperand& rs, Register scratch);

  void Uldc1(FPURegister fd, const MemOperand& rs, Register scratch);
  void Usdc1(FPURegister fd, const MemOperand& rs, Register scratch);

  void Lb(Register rd, const MemOperand& rs);
  void Lbu(Register rd, const MemOperand& rs);
  void Sb(Register rd, const MemOperand& rs);

  void Lh(Register rd, const MemOperand& rs);
  void Lhu(Register rd, const MemOperand& rs);
  void Sh(Register rd, const MemOperand& rs);

  void Lw(Register rd, const MemOperand& rs);
  void Lwu(Register rd, const MemOperand& rs);
  void Sw(Register rd, const MemOperand& rs);

  void Lwc1(FPURegister fd, const MemOperand& src);
  void Swc1(FPURegister fs, const MemOperand& dst);

  void Ldc1(FPURegister fd, const MemOperand& src);
  void Sdc1(FPURegister fs, const MemOperand& dst);

628 629 630 631 632 633
  void Ll(Register rd, const MemOperand& rs);
  void Sc(Register rd, const MemOperand& rs);

  void Lld(Register rd, const MemOperand& rs);
  void Scd(Register rd, const MemOperand& rs);

634 635 636 637 638 639
  // Perform a floating-point min or max operation with the
  // (IEEE-754-compatible) semantics of MIPS32's Release 6 MIN.fmt/MAX.fmt.
  // Some cases, typically NaNs or +/-0.0, are expected to be rare and are
  // handled in out-of-line code. The specific behaviour depends on supported
  // instructions.
  //
640
  // These functions assume (and assert) that src1!=src2. It is permitted
641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661
  // for the result to alias either input register.
  void Float32Max(FPURegister dst, FPURegister src1, FPURegister src2,
                  Label* out_of_line);
  void Float32Min(FPURegister dst, FPURegister src1, FPURegister src2,
                  Label* out_of_line);
  void Float64Max(FPURegister dst, FPURegister src1, FPURegister src2,
                  Label* out_of_line);
  void Float64Min(FPURegister dst, FPURegister src1, FPURegister src2,
                  Label* out_of_line);

  // Generate out-of-line cases for the macros above.
  void Float32MaxOutOfLine(FPURegister dst, FPURegister src1, FPURegister src2);
  void Float32MinOutOfLine(FPURegister dst, FPURegister src1, FPURegister src2);
  void Float64MaxOutOfLine(FPURegister dst, FPURegister src1, FPURegister src2);
  void Float64MinOutOfLine(FPURegister dst, FPURegister src1, FPURegister src2);

  bool IsDoubleZeroRegSet() { return has_double_zero_reg_set_; }

  void mov(Register rd, Register rt) { or_(rd, rt, zero_reg); }

  inline void Move(Register dst, Handle<HeapObject> handle) { li(dst, handle); }
662
  inline void Move(Register dst, Smi smi) { li(dst, Operand(smi)); }
663 664

  inline void Move(Register dst, Register src) {
665
    if (dst != src) {
666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700
      mov(dst, src);
    }
  }

  inline void Move(FPURegister dst, FPURegister src) { Move_d(dst, src); }

  inline void Move(Register dst_low, Register dst_high, FPURegister src) {
    mfc1(dst_low, src);
    mfhc1(dst_high, src);
  }

  inline void Move(Register dst, FPURegister src) { dmfc1(dst, src); }

  inline void Move(FPURegister dst, Register src) { dmtc1(src, dst); }

  inline void FmoveHigh(Register dst_high, FPURegister src) {
    mfhc1(dst_high, src);
  }

  inline void FmoveHigh(FPURegister dst, Register src_high) {
    mthc1(src_high, dst);
  }

  inline void FmoveLow(Register dst_low, FPURegister src) {
    mfc1(dst_low, src);
  }

  void FmoveLow(FPURegister dst, Register src_low);

  inline void Move(FPURegister dst, Register src_low, Register src_high) {
    mtc1(src_low, dst);
    mthc1(src_high, dst);
  }

  inline void Move_d(FPURegister dst, FPURegister src) {
701
    if (dst != src) {
702 703 704 705 706
      mov_d(dst, src);
    }
  }

  inline void Move_s(FPURegister dst, FPURegister src) {
707
    if (dst != src) {
708 709 710 711
      mov_s(dst, src);
    }
  }

712 713 714 715
  void Move(FPURegister dst, float imm) { Move(dst, bit_cast<uint32_t>(imm)); }
  void Move(FPURegister dst, double imm) { Move(dst, bit_cast<uint64_t>(imm)); }
  void Move(FPURegister dst, uint32_t src);
  void Move(FPURegister dst, uint64_t src);
716

717 718 719 720 721 722 723 724 725 726 727
  // DaddOverflow sets overflow register to a negative value if
  // overflow occured, otherwise it is zero or positive
  void DaddOverflow(Register dst, Register left, const Operand& right,
                    Register overflow);
  // DsubOverflow sets overflow register to a negative value if
  // overflow occured, otherwise it is zero or positive
  void DsubOverflow(Register dst, Register left, const Operand& right,
                    Register overflow);
  // MulOverflow sets overflow register to zero if no overflow occured
  void MulOverflow(Register dst, Register left, const Operand& right,
                   Register overflow);
728 729 730 731 732 733 734 735 736 737 738 739 740 741 742

// Number of instructions needed for calculation of switch table entry address
#ifdef _MIPS_ARCH_MIPS64R6
  static const int kSwitchTablePrologueSize = 6;
#else
  static const int kSwitchTablePrologueSize = 11;
#endif

  // GetLabelFunction must be lambda '[](size_t index) -> Label*' or a
  // functor/function with 'Label *func(size_t index)' declaration.
  template <typename Func>
  void GenerateSwitchTable(Register index, size_t case_count,
                           Func GetLabelFunction);

  // Load an object from the root table.
743 744
  void LoadRoot(Register destination, RootIndex index) override;
  void LoadRoot(Register destination, RootIndex index, Condition cond,
745 746
                Register src1, const Operand& src2);

747 748
  void LoadMap(Register destination, Register object);

749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772
  // If the value is a NaN, canonicalize the value else, do nothing.
  void FPUCanonicalizeNaN(const DoubleRegister dst, const DoubleRegister src);

  // ---------------------------------------------------------------------------
  // FPU macros. These do not handle special cases like NaN or +- inf.

  // Convert unsigned word to double.
  void Cvt_d_uw(FPURegister fd, FPURegister fs);
  void Cvt_d_uw(FPURegister fd, Register rs);

  // Convert unsigned long to double.
  void Cvt_d_ul(FPURegister fd, FPURegister fs);
  void Cvt_d_ul(FPURegister fd, Register rs);

  // Convert unsigned word to float.
  void Cvt_s_uw(FPURegister fd, FPURegister fs);
  void Cvt_s_uw(FPURegister fd, Register rs);

  // Convert unsigned long to float.
  void Cvt_s_ul(FPURegister fd, FPURegister fs);
  void Cvt_s_ul(FPURegister fd, Register rs);

  // Convert double to unsigned word.
  void Trunc_uw_d(FPURegister fd, FPURegister fs, FPURegister scratch);
773
  void Trunc_uw_d(Register rd, FPURegister fs, FPURegister scratch);
774 775 776 777

  // Convert double to unsigned long.
  void Trunc_ul_d(FPURegister fd, FPURegister fs, FPURegister scratch,
                  Register result = no_reg);
778
  void Trunc_ul_d(Register rd, FPURegister fs, FPURegister scratch,
779 780 781 782 783
                  Register result = no_reg);

  // Convert single to unsigned long.
  void Trunc_ul_s(FPURegister fd, FPURegister fs, FPURegister scratch,
                  Register result = no_reg);
784
  void Trunc_ul_s(Register rd, FPURegister fs, FPURegister scratch,
785 786
                  Register result = no_reg);

787 788 789 790 791 792 793 794 795 796 797 798
  // Round double functions
  void Trunc_d_d(FPURegister fd, FPURegister fs);
  void Round_d_d(FPURegister fd, FPURegister fs);
  void Floor_d_d(FPURegister fd, FPURegister fs);
  void Ceil_d_d(FPURegister fd, FPURegister fs);

  // Round float functions
  void Trunc_s_s(FPURegister fd, FPURegister fs);
  void Round_s_s(FPURegister fd, FPURegister fs);
  void Floor_s_s(FPURegister fd, FPURegister fs);
  void Ceil_s_s(FPURegister fd, FPURegister fs);

799 800
  void LoadLane(MSASize sz, MSARegister dst, uint8_t laneidx, MemOperand src);
  void StoreLane(MSASize sz, MSARegister src, uint8_t laneidx, MemOperand dst);
801 802 803 804
  void ExtMulLow(MSADataType type, MSARegister dst, MSARegister src1,
                 MSARegister src2);
  void ExtMulHigh(MSADataType type, MSARegister dst, MSARegister src1,
                  MSARegister src2);
805
  void LoadSplat(MSASize sz, MSARegister dst, MemOperand src);
806 807 808
  void MSARoundW(MSARegister dst, MSARegister src, FPURoundingMode mode);
  void MSARoundD(MSARegister dst, MSARegister src, FPURoundingMode mode);

809 810 811 812
  // Jump the register contains a smi.
  void JumpIfSmi(Register value, Label* smi_label, Register scratch = at,
                 BranchDelaySlot bd = PROTECT);

813 814 815 816 817 818 819 820 821 822
  void JumpIfEqual(Register a, int32_t b, Label* dest) {
    li(kScratchReg, Operand(b));
    Branch(dest, eq, a, Operand(kScratchReg));
  }

  void JumpIfLessThan(Register a, int32_t b, Label* dest) {
    li(kScratchReg, Operand(b));
    Branch(dest, lt, a, Operand(kScratchReg));
  }

823 824 825 826 827 828 829 830 831 832 833 834 835 836
  // Push a standard frame, consisting of ra, fp, context and JS function.
  void PushStandardFrame(Register function_reg);

  // Get the actual activation frame alignment for target environment.
  static int ActivationFrameAlignment();

  // Load Scaled Address instructions. Parameter sa (shift argument) must be
  // between [1, 31] (inclusive). On pre-r6 architectures the scratch register
  // may be clobbered.
  void Lsa(Register rd, Register rs, Register rt, uint8_t sa,
           Register scratch = at);
  void Dlsa(Register rd, Register rs, Register rt, uint8_t sa,
            Register scratch = at);

837 838 839 840
  // Compute the start of the generated instruction stream from the current PC.
  // This is an alternative to embedding the {CodeObject} handle as a reference.
  void ComputeCodeStartAddress(Register dst);

841 842
  void ResetSpeculationPoisonRegister();

843 844 845 846 847 848 849 850 851 852
  // Control-flow integrity:

  // Define a function entrypoint. This doesn't emit any code for this
  // architecture, as control-flow integrity is not supported for it.
  void CodeEntry() {}
  // Define an exception handler.
  void ExceptionHandler() {}
  // Define an exception handler and bind a label.
  void BindExceptionHandler(Label* label) { bind(label); }

853 854 855 856 857
 protected:
  inline Register GetRtAsRegisterHelper(const Operand& rt, Register scratch);
  inline int32_t GetOffset(int32_t offset, Label* L, OffsetSize bits);

 private:
858
  bool has_double_zero_reg_set_ = false;
859

860 861 862 863 864 865 866
  // Performs a truncating conversion of a floating point number as used by
  // the JS bitwise operations. See ECMA-262 9.5: ToInt32. Goes to 'done' if it
  // succeeds, otherwise falls through if result is saturated. On return
  // 'result' either holds answer, or is clobbered on fall through.
  void TryInlineTruncateDoubleToI(Register result, DoubleRegister input,
                                  Label* done);

867 868 869 870 871 872
  void CompareF(SecondaryField sizeField, FPUCondition cc, FPURegister cmp1,
                FPURegister cmp2);

  void CompareIsNanF(SecondaryField sizeField, FPURegister cmp1,
                     FPURegister cmp2);

873 874 875 876 877 878
  void BranchShortMSA(MSABranchDF df, Label* target, MSABranchCondition cond,
                      MSARegister wt, BranchDelaySlot bd = PROTECT);

  void CallCFunctionHelper(Register function, int num_reg_arguments,
                           int num_double_arguments);

879 880 881 882
  // TODO(mips) Reorder parameters so out parameters come last.
  bool CalculateOffset(Label* L, int32_t* offset, OffsetSize bits);
  bool CalculateOffset(Label* L, int32_t* offset, OffsetSize bits,
                       Register* scratch, const Operand& rt);
883

884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908
  void BranchShortHelperR6(int32_t offset, Label* L);
  void BranchShortHelper(int16_t offset, Label* L, BranchDelaySlot bdslot);
  bool BranchShortHelperR6(int32_t offset, Label* L, Condition cond,
                           Register rs, const Operand& rt);
  bool BranchShortHelper(int16_t offset, Label* L, Condition cond, Register rs,
                         const Operand& rt, BranchDelaySlot bdslot);
  bool BranchShortCheck(int32_t offset, Label* L, Condition cond, Register rs,
                        const Operand& rt, BranchDelaySlot bdslot);

  void BranchAndLinkShortHelperR6(int32_t offset, Label* L);
  void BranchAndLinkShortHelper(int16_t offset, Label* L,
                                BranchDelaySlot bdslot);
  void BranchAndLinkShort(int32_t offset, BranchDelaySlot bdslot = PROTECT);
  void BranchAndLinkShort(Label* L, BranchDelaySlot bdslot = PROTECT);
  bool BranchAndLinkShortHelperR6(int32_t offset, Label* L, Condition cond,
                                  Register rs, const Operand& rt);
  bool BranchAndLinkShortHelper(int16_t offset, Label* L, Condition cond,
                                Register rs, const Operand& rt,
                                BranchDelaySlot bdslot);
  bool BranchAndLinkShortCheck(int32_t offset, Label* L, Condition cond,
                               Register rs, const Operand& rt,
                               BranchDelaySlot bdslot);
  void BranchLong(Label* L, BranchDelaySlot bdslot);
  void BranchAndLinkLong(Label* L, BranchDelaySlot bdslot);

909 910 911 912 913 914 915 916
  template <typename RoundFunc>
  void RoundDouble(FPURegister dst, FPURegister src, FPURoundingMode mode,
                   RoundFunc round);

  template <typename RoundFunc>
  void RoundFloat(FPURegister dst, FPURegister src, FPURoundingMode mode,
                  RoundFunc round);

917 918
  // Push a fixed frame, consisting of ra, fp.
  void PushCommonFrame(Register marker_reg = no_reg);
919 920 921

  void CallRecordWriteStub(Register object, Register address,
                           RememberedSetAction remembered_set_action,
922
                           SaveFPRegsMode fp_mode, int builtin_index,
923
                           Address wasm_target);
924 925 926
};

// MacroAssembler implements a collection of frequently used macros.
927
class V8_EXPORT_PRIVATE MacroAssembler : public TurboAssembler {
928
 public:
929
  using TurboAssembler::TurboAssembler;
930

931 932 933 934 935 936 937 938 939 940 941 942
  // It assumes that the arguments are located below the stack pointer.
  // argc is the number of arguments not including the receiver.
  // TODO(victorgomes): Remove this function once we stick with the reversed
  // arguments order.
  void LoadReceiver(Register dest, Register argc) {
    Ld(dest, MemOperand(sp, 0));
  }

  void StoreReceiver(Register rec, Register argc, Register scratch) {
    Sd(rec, MemOperand(sp, 0));
  }

943 944 945 946 947 948
  bool IsNear(Label* L, Condition cond, int rs_reg);

  // Swap two registers.  If the scratch register is omitted then a slightly
  // less efficient form using xor instead of mov is emitted.
  void Swap(Register reg1, Register reg2, Register scratch = no_reg);

949
  void PushRoot(RootIndex index) {
950 951 952 953
    UseScratchRegisterScope temps(this);
    Register scratch = temps.Acquire();
    LoadRoot(scratch, index);
    Push(scratch);
954
  }
955 956

  // Compare the object in a register to a value and jump if they are equal.
957
  void JumpIfRoot(Register with, RootIndex index, Label* if_equal) {
958 959 960 961
    UseScratchRegisterScope temps(this);
    Register scratch = temps.Acquire();
    LoadRoot(scratch, index);
    Branch(if_equal, eq, with, Operand(scratch));
962 963 964
  }

  // Compare the object in a register to a value and jump if they are not equal.
965
  void JumpIfNotRoot(Register with, RootIndex index, Label* if_not_equal) {
966 967 968 969
    UseScratchRegisterScope temps(this);
    Register scratch = temps.Acquire();
    LoadRoot(scratch, index);
    Branch(if_not_equal, ne, with, Operand(scratch));
970 971
  }

Yu Yin's avatar
Yu Yin committed
972 973 974 975 976
  // Checks if value is in range [lower_limit, higher_limit] using a single
  // comparison.
  void JumpIfIsInRange(Register value, unsigned lower_limit,
                       unsigned higher_limit, Label* on_in_range);

977 978 979 980 981 982 983 984 985
  // ---------------------------------------------------------------------------
  // GC Support

  // Notify the garbage collector that we wrote a pointer into an object.
  // |object| is the object being stored into, |value| is the object being
  // stored.  value and scratch registers are clobbered by the operation.
  // The offset is the offset from the start of the object, not the offset from
  // the tagged HeapObject pointer.  For use with FieldOperand(reg, off).
  void RecordWriteField(
986 987
      Register object, int offset, Register value, Register scratch,
      RAStatus ra_status, SaveFPRegsMode save_fp,
988
      RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET,
989
      SmiCheck smi_check = INLINE_SMI_CHECK);
990 991 992 993 994

  // For a given |object| notify the garbage collector that the slot |address|
  // has been written.  |value| is the object being stored. The value and
  // address registers are clobbered by the operation.
  void RecordWrite(
995
      Register object, Register address, Register value, RAStatus ra_status,
996 997
      SaveFPRegsMode save_fp,
      RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET,
998
      SmiCheck smi_check = INLINE_SMI_CHECK);
999

1000
  void Pref(int32_t hint, const MemOperand& rs);
1001

1002 1003
  // ---------------------------------------------------------------------------
  // Pseudo-instructions.
1004

1005 1006 1007
  void LoadWordPair(Register rd, const MemOperand& rs, Register scratch = at);
  void StoreWordPair(Register rd, const MemOperand& rs, Register scratch = at);

1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020
  // Convert double to unsigned long.
  void Trunc_l_ud(FPURegister fd, FPURegister fs, FPURegister scratch);

  void Trunc_l_d(FPURegister fd, FPURegister fs);
  void Round_l_d(FPURegister fd, FPURegister fs);
  void Floor_l_d(FPURegister fd, FPURegister fs);
  void Ceil_l_d(FPURegister fd, FPURegister fs);

  void Trunc_w_d(FPURegister fd, FPURegister fs);
  void Round_w_d(FPURegister fd, FPURegister fs);
  void Floor_w_d(FPURegister fd, FPURegister fs);
  void Ceil_w_d(FPURegister fd, FPURegister fs);

1021 1022
  void Madd_s(FPURegister fd, FPURegister fr, FPURegister fs, FPURegister ft,
              FPURegister scratch);
1023 1024 1025 1026 1027 1028
  void Madd_d(FPURegister fd, FPURegister fr, FPURegister fs, FPURegister ft,
              FPURegister scratch);
  void Msub_s(FPURegister fd, FPURegister fr, FPURegister fs, FPURegister ft,
              FPURegister scratch);
  void Msub_d(FPURegister fd, FPURegister fr, FPURegister fs, FPURegister ft,
              FPURegister scratch);
1029 1030 1031 1032

  void BranchShortMSA(MSABranchDF df, Label* target, MSABranchCondition cond,
                      MSARegister wt, BranchDelaySlot bd = PROTECT);

1033 1034 1035 1036
  // Enter exit frame.
  // argc - argument count to be dropped by LeaveExitFrame.
  // save_doubles - saves FPU registers on stack, currently disabled.
  // stack_space - extra stack space.
1037 1038
  void EnterExitFrame(bool save_doubles, int stack_space = 0,
                      StackFrame::Type frame_type = StackFrame::EXIT);
1039 1040

  // Leave the current exit frame.
1041
  void LeaveExitFrame(bool save_doubles, Register arg_count,
1042
                      bool do_return = NO_EMIT_RETURN,
1043
                      bool argument_count_is_length = false);
1044 1045 1046 1047

  // Make sure the stack is aligned. Only emits code in debug mode.
  void AssertStackIsAligned();

1048
  // Load the global proxy from the current context.
1049 1050 1051
  void LoadGlobalProxy(Register dst) {
    LoadNativeContextSlot(Context::GLOBAL_PROXY_INDEX, dst);
  }
1052

1053
  void LoadNativeContextSlot(int index, Register dst);
1054 1055 1056

  // Load the initial map from the global function. The registers
  // function and map can be the same, function is then overwritten.
1057
  void LoadGlobalFunctionInitialMap(Register function, Register map,
1058 1059 1060 1061 1062 1063
                                    Register scratch);

  // -------------------------------------------------------------------------
  // JavaScript invokes.

  // Invoke the JavaScript function code by either calling or jumping.
1064
  void InvokeFunctionCode(Register function, Register new_target,
1065 1066
                          Register expected_parameter_count,
                          Register actual_parameter_count, InvokeFlag flag);
1067

1068 1069
  // On function call, call into the debugger if necessary.
  void CheckDebugHook(Register fun, Register new_target,
1070 1071
                      Register expected_parameter_count,
                      Register actual_parameter_count);
1072

1073 1074
  // Invoke the JavaScript function in the given register. Changes the
  // current context to the context in the function before invoking.
1075 1076 1077 1078 1079
  void InvokeFunctionWithNewTarget(Register function, Register new_target,
                                   Register actual_parameter_count,
                                   InvokeFlag flag);
  void InvokeFunction(Register function, Register expected_parameter_count,
                      Register actual_parameter_count, InvokeFlag flag);
1080

1081
  // Frame restart support.
1082
  void MaybeDropFrames();
1083 1084 1085

  // Exception handling.

1086 1087
  // Push a new stack handler and link into stack handler chain.
  void PushStackHandler();
1088

1089
  // Unlink the stack handler on top of the stack from the stack handler chain.
1090
  // Must preserve the result register.
1091
  void PopStackHandler();
1092 1093 1094 1095

  // -------------------------------------------------------------------------
  // Support functions.

1096
  void GetObjectType(Register function, Register map, Register type_reg);
1097

1098 1099 1100
  void GetInstanceTypeRange(Register map, Register type_reg,
                            InstanceType lower_limit, Register range);

1101 1102 1103 1104
  // -------------------------------------------------------------------------
  // Runtime calls.

  // Call a runtime routine.
1105
  void CallRuntime(const Runtime::Function* f, int num_arguments,
1106
                   SaveFPRegsMode save_doubles = kDontSaveFPRegs);
1107 1108

  // Convenience function: Same as above, but takes the fid instead.
1109
  void CallRuntime(Runtime::FunctionId fid,
1110
                   SaveFPRegsMode save_doubles = kDontSaveFPRegs) {
1111
    const Runtime::Function* function = Runtime::FunctionForId(fid);
1112
    CallRuntime(function, function->nargs, save_doubles);
1113 1114 1115 1116
  }

  // Convenience function: Same as above, but takes the fid instead.
  void CallRuntime(Runtime::FunctionId fid, int num_arguments,
1117 1118
                   SaveFPRegsMode save_doubles = kDontSaveFPRegs) {
    CallRuntime(Runtime::FunctionForId(fid), num_arguments, save_doubles);
1119 1120 1121
  }

  // Convenience function: tail call a runtime routine (jump).
1122
  void TailCallRuntime(Runtime::FunctionId fid);
1123 1124 1125

  // Jump to the builtin routine.
  void JumpToExternalReference(const ExternalReference& builtin,
1126 1127
                               BranchDelaySlot bd = PROTECT,
                               bool builtin_exit_frame = false);
1128

1129
  // Generates a trampoline to jump to the off-heap instruction stream.
1130
  void JumpToInstructionStream(Address entry);
1131

1132 1133 1134 1135
  // ---------------------------------------------------------------------------
  // In-place weak references.
  void LoadWeakValue(Register out, Register in, Label* target_if_cleared);

1136 1137 1138
  // -------------------------------------------------------------------------
  // StatsCounter support.

1139 1140 1141 1142
  void IncrementCounter(StatsCounter* counter, int value, Register scratch1,
                        Register scratch2);
  void DecrementCounter(StatsCounter* counter, int value, Register scratch1,
                        Register scratch2);
1143 1144

  // -------------------------------------------------------------------------
1145 1146 1147 1148 1149 1150 1151 1152
  // Stack limit utilities

  enum StackLimitKind { kInterruptStackLimit, kRealStackLimit };
  void LoadStackLimit(Register destination, StackLimitKind kind);
  void StackOverflowCheck(Register num_args, Register scratch1,
                          Register scratch2, Label* stack_overflow);

  // ---------------------------------------------------------------------------
1153 1154 1155 1156 1157 1158 1159
  // Smi utilities.

  void SmiTag(Register dst, Register src) {
    STATIC_ASSERT(kSmiTag == 0);
    if (SmiValuesAre32Bits()) {
      dsll32(dst, src, 0);
    } else {
1160
      DCHECK(SmiValuesAre31Bits());
1161 1162 1163 1164
      Addu(dst, src, src);
    }
  }

1165
  void SmiTag(Register reg) { SmiTag(reg, reg); }
1166 1167 1168 1169 1170 1171 1172

  // Left-shifted from int32 equivalent of Smi.
  void SmiScale(Register dst, Register src, int scale) {
    if (SmiValuesAre32Bits()) {
      // The int portion is upper 32-bits of 64-bit word.
      dsra(dst, src, kSmiShift - scale);
    } else {
1173
      DCHECK(SmiValuesAre31Bits());
1174
      DCHECK_GE(scale, kSmiTagSize);
1175 1176 1177 1178 1179 1180 1181 1182 1183 1184
      sll(dst, src, scale - kSmiTagSize);
    }
  }

  // Test if the register contains a smi.
  inline void SmiTst(Register value, Register scratch) {
    And(scratch, value, Operand(kSmiTagMask));
  }

  // Jump if the register contains a non-smi.
1185
  void JumpIfNotSmi(Register value, Label* not_smi_label, Register scratch = at,
1186 1187 1188 1189 1190 1191
                    BranchDelaySlot bd = PROTECT);

  // Abort execution if argument is a smi, enabled via --debug-code.
  void AssertNotSmi(Register object);
  void AssertSmi(Register object);

1192 1193 1194
  // Abort execution if argument is not a Constructor, enabled via --debug-code.
  void AssertConstructor(Register object);

1195 1196 1197
  // Abort execution if argument is not a JSFunction, enabled via --debug-code.
  void AssertFunction(Register object);

1198 1199 1200 1201
  // Abort execution if argument is not a JSBoundFunction,
  // enabled via --debug-code.
  void AssertBoundFunction(Register object);

1202
  // Abort execution if argument is not a JSGeneratorObject (or subclass),
1203
  // enabled via --debug-code.
1204
  void AssertGeneratorObject(Register object);
1205

1206 1207 1208 1209
  // Abort execution if argument is not undefined or an AllocationSite, enabled
  // via --debug-code.
  void AssertUndefinedOrAllocationSite(Register object, Register scratch);

1210
  template <typename Field>
1211 1212 1213 1214
  void DecodeField(Register dst, Register src) {
    Ext(dst, src, Field::kShift, Field::kSize);
  }

1215
  template <typename Field>
1216 1217 1218 1219 1220 1221
  void DecodeField(Register reg) {
    DecodeField<Field>(reg, reg);
  }

 private:
  // Helper functions for generating invokes.
1222 1223 1224
  void InvokePrologue(Register expected_parameter_count,
                      Register actual_parameter_count, Label* done,
                      InvokeFlag flag);
1225 1226 1227 1228 1229 1230

  // Compute memory operands for safepoint stack slots.
  static int SafepointRegisterStackIndex(int reg_code);

  // Needs access to SafepointRegisterStackIndex for compiled frame
  // traversal.
1231
  friend class CommonFrame;
1232 1233

  DISALLOW_IMPLICIT_CONSTRUCTORS(MacroAssembler);
1234 1235
};

1236
template <typename Func>
1237
void TurboAssembler::GenerateSwitchTable(Register index, size_t case_count,
1238 1239 1240
                                         Func GetLabelFunction) {
  // Ensure that dd-ed labels following this instruction use 8 bytes aligned
  // addresses.
1241 1242 1243 1244
  BlockTrampolinePoolFor(static_cast<int>(case_count) * 2 +
                         kSwitchTablePrologueSize);
  UseScratchRegisterScope temps(this);
  Register scratch = temps.Acquire();
1245 1246 1247 1248 1249
  if (kArchVariant >= kMips64r6) {
    // Opposite of Align(8) as we have odd number of instructions in this case.
    if ((pc_offset() & 7) == 0) {
      nop();
    }
1250 1251 1252
    addiupc(scratch, 5);
    Dlsa(scratch, scratch, index, kPointerSizeLog2);
    Ld(scratch, MemOperand(scratch));
1253 1254 1255
  } else {
    Label here;
    Align(8);
1256
    push(ra);
1257
    bal(&here);
1258
    dsll(scratch, index, kPointerSizeLog2);  // Branch delay slot.
1259
    bind(&here);
1260
    daddu(scratch, scratch, ra);
1261
    pop(ra);
1262
    Ld(scratch, MemOperand(scratch, 6 * v8::internal::kInstrSize));
1263
  }
1264
  jr(scratch);
1265 1266 1267 1268 1269
  nop();  // Branch delay slot nop.
  for (size_t index = 0; index < case_count; ++index) {
    dd(GetLabelFunction(index));
  }
}
1270 1271 1272

#define ACCESS_MASM(masm) masm->

1273 1274
}  // namespace internal
}  // namespace v8
1275

1276
#endif  // V8_CODEGEN_MIPS64_MACRO_ASSEMBLER_MIPS64_H_