macro-assembler-mips64.h 48.2 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 253 254 255 256 257
  void LoadCodeObjectEntry(Register destination,
                           Register code_object) override {
    // TODO(mips): Implement.
    UNIMPLEMENTED();
  }
  void CallCodeObject(Register code_object) override {
    // TODO(mips): Implement.
    UNIMPLEMENTED();
  }
  void JumpCodeObject(Register code_object) override {
    // TODO(mips): Implement.
    UNIMPLEMENTED();
  }

258 259 260 261 262
  // 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);

263
  void CallForDeoptimization(Address target, int deopt_id);
264

265 266
  void Ret(COND_ARGS);
  inline void Ret(BranchDelaySlot bd, Condition cond = al,
267 268
                  Register rs = zero_reg,
                  const Operand& rt = Operand(zero_reg)) {
269 270 271 272 273
    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.
274
  void Drop(int count, Condition cond = cc_always, Register reg = no_reg,
275 276 277 278 279 280
            const Operand& op = Operand(no_reg));

  // Trivial case of DropAndRet that utilizes the delay slot and only emits
  // 2 instructions.
  void DropAndRet(int drop);

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

283 284
  void Ld(Register rd, const MemOperand& rs);
  void Sd(Register rd, const MemOperand& rs);
285

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

294 295 296 297 298
  // 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));
299 300
  }

301 302 303 304 305 306
  // 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));
307 308
  }

309 310 311 312 313 314 315
  // 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));
316 317
  }

318 319 320 321 322 323 324 325 326
  // 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));
327 328
  }

329 330 331 332 333
  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));
334 335
  }

336 337 338 339 340 341
  void SaveRegisters(RegList registers);
  void RestoreRegisters(RegList registers);

  void CallRecordWriteStub(Register object, Register address,
                           RememberedSetAction remembered_set_action,
                           SaveFPRegsMode fp_mode);
342 343 344
  void CallRecordWriteStub(Register object, Register address,
                           RememberedSetAction remembered_set_action,
                           SaveFPRegsMode fp_mode, Address wasm_target);
345
  void CallEphemeronKeyBarrier(Register object, Register address,
346
                               SaveFPRegsMode fp_mode);
347

348 349 350 351 352
  // 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);
353

354 355 356 357 358 359 360 361 362 363
  // 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,
364 365
                      Register exclusion2 = no_reg,
                      Register exclusion3 = no_reg);
366 367 368 369 370
  // 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);
371

372 373 374 375 376
  void pop(Register dst) {
    Ld(dst, MemOperand(sp, 0));
    Daddu(sp, sp, Operand(kPointerSize));
  }
  void Pop(Register dst) { pop(dst); }
377

378 379
  // Pop two registers. Pops rightmost register first (from lower address).
  void Pop(Register src1, Register src2) {
380
    DCHECK(src1 != src2);
381 382 383
    Ld(src2, MemOperand(sp, 0 * kPointerSize));
    Ld(src1, MemOperand(sp, 1 * kPointerSize));
    Daddu(sp, sp, 2 * kPointerSize);
384 385
  }

386 387 388 389 390 391 392
  // 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);
  }
393

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

396 397 398 399
  // 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);
400

401 402 403 404 405 406
#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)); }
407

408 409 410 411
#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)); }
412

Yu Yin's avatar
Yu Yin committed
413 414 415 416 417 418 419 420 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
  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)
453 454

  // MIPS32 R2 instruction macro.
Yu Yin's avatar
Yu Yin committed
455 456
  DEFINE_INSTRUCTION(Ror)
  DEFINE_INSTRUCTION(Dror)
457 458 459 460 461

#undef DEFINE_INSTRUCTION
#undef DEFINE_INSTRUCTION2
#undef DEFINE_INSTRUCTION3

462
  void SmiUntag(Register dst, const MemOperand& src);
463 464
  void SmiUntag(Register dst, Register src) {
    if (SmiValuesAre32Bits()) {
465
      dsra32(dst, src, kSmiShift - 32);
466
    } else {
467 468
      DCHECK(SmiValuesAre31Bits());
      sra(dst, src, kSmiShift);
469 470 471 472 473 474 475
    }
  }

  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.
476 477
  // Both |callee_args_count| and |caller_args_count| do not include
  // receiver. |callee_args_count| is not modified. |caller_args_count|
478
  // is trashed.
479 480
  void PrepareForTailCall(Register callee_args_count,
                          Register caller_args_count, Register scratch0,
481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498
                          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);

499
  // Arguments 1-4 are placed in registers a0 through a3 respectively.
500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524
  // 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);

525
  // See comments at the beginning of Builtins::Generate_CEntry.
526 527
  inline void PrepareCEntryArgs(int num_args) { li(a0, num_args); }
  inline void PrepareCEntryFunction(const ExternalReference& ref) {
528
    li(a1, ref);
529 530 531 532 533 534 535 536 537
  }

  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.
538
  void TruncateDoubleToI(Isolate* isolate, Zone* zone, Register result,
539
                         DoubleRegister double_input, StubCallMode stub_mode);
540 541 542 543 544 545 546

  // 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);

547 548 549
  void LoadZeroIfFPUCondition(Register dest);
  void LoadZeroIfNotFPUCondition(Register dest);

550 551 552 553 554
  void LoadZeroIfConditionNotZero(Register dest, Register condition);
  void LoadZeroIfConditionZero(Register dest, Register condition);
  void LoadZeroOnCondition(Register rd, Register rs, const Operand& rt,
                           Condition cond);

555
  void Clz(Register rd, Register rs);
556
  void Dclz(Register rd, Register rs);
557 558 559 560
  void Ctz(Register rd, Register rs);
  void Dctz(Register rd, Register rs);
  void Popcnt(Register rd, Register rs);
  void Dpopcnt(Register rd, Register rs);
561 562 563 564 565 566

  // 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);
567 568 569
  void ExtractBits(Register dest, Register source, Register pos, int size,
                   bool sign_extend = false);
  void InsertBits(Register dest, Register source, Register pos, int size);
570 571 572 573 574 575 576 577 578
  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);
579
  void Trunc_uw_s(Register rd, FPURegister fs, FPURegister scratch);
580 581 582 583 584 585 586 587 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

  // 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);

620 621 622 623 624 625
  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);

626 627 628 629 630 631
  // 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.
  //
632
  // These functions assume (and assert) that src1!=src2. It is permitted
633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653
  // 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); }
654
  inline void Move(Register dst, Smi smi) { li(dst, Operand(smi)); }
655 656

  inline void Move(Register dst, Register src) {
657
    if (dst != src) {
658 659 660 661 662 663 664 665 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
      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) {
693
    if (dst != src) {
694 695 696 697 698
      mov_d(dst, src);
    }
  }

  inline void Move_s(FPURegister dst, FPURegister src) {
699
    if (dst != src) {
700 701 702 703
      mov_s(dst, src);
    }
  }

704 705 706 707
  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);
708

709 710 711 712 713 714 715 716 717 718 719
  // 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);
720 721 722 723 724 725 726 727 728 729 730 731 732 733 734

// 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.
735 736
  void LoadRoot(Register destination, RootIndex index) override;
  void LoadRoot(Register destination, RootIndex index, Condition cond,
737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762
                Register src1, const Operand& src2);

  // 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);
763
  void Trunc_uw_d(Register rd, FPURegister fs, FPURegister scratch);
764 765 766 767

  // Convert double to unsigned long.
  void Trunc_ul_d(FPURegister fd, FPURegister fs, FPURegister scratch,
                  Register result = no_reg);
768
  void Trunc_ul_d(Register rd, FPURegister fs, FPURegister scratch,
769 770 771 772 773
                  Register result = no_reg);

  // Convert single to unsigned long.
  void Trunc_ul_s(FPURegister fd, FPURegister fs, FPURegister scratch,
                  Register result = no_reg);
774
  void Trunc_ul_s(Register rd, FPURegister fs, FPURegister scratch,
775 776
                  Register result = no_reg);

777 778 779 780 781 782 783 784 785 786 787 788
  // 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);

789 790 791 792
  // Jump the register contains a smi.
  void JumpIfSmi(Register value, Label* smi_label, Register scratch = at,
                 BranchDelaySlot bd = PROTECT);

793 794 795 796 797 798 799 800 801 802
  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));
  }

803 804 805 806 807 808 809 810 811 812 813 814 815 816
  // 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);

817 818 819 820
  // 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);

821 822
  void ResetSpeculationPoisonRegister();

823 824 825 826 827
 protected:
  inline Register GetRtAsRegisterHelper(const Operand& rt, Register scratch);
  inline int32_t GetOffset(int32_t offset, Label* L, OffsetSize bits);

 private:
828
  bool has_double_zero_reg_set_ = false;
829

830 831 832 833 834 835 836
  // 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);

837 838 839 840 841 842
  void CompareF(SecondaryField sizeField, FPUCondition cc, FPURegister cmp1,
                FPURegister cmp2);

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

843 844 845 846 847 848
  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);

849 850 851 852
  // 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);
853

854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878
  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);

879 880 881 882 883 884 885 886
  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);

887 888
  // Push a fixed frame, consisting of ra, fp.
  void PushCommonFrame(Register marker_reg = no_reg);
889 890 891 892 893

  void CallRecordWriteStub(Register object, Register address,
                           RememberedSetAction remembered_set_action,
                           SaveFPRegsMode fp_mode, Handle<Code> code_target,
                           Address wasm_target);
894 895 896
};

// MacroAssembler implements a collection of frequently used macros.
897
class V8_EXPORT_PRIVATE MacroAssembler : public TurboAssembler {
898
 public:
899
  using TurboAssembler::TurboAssembler;
900 901 902 903 904 905 906

  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);

907
  void PushRoot(RootIndex index) {
908 909 910 911
    UseScratchRegisterScope temps(this);
    Register scratch = temps.Acquire();
    LoadRoot(scratch, index);
    Push(scratch);
912
  }
913 914

  // Compare the object in a register to a value and jump if they are equal.
915
  void JumpIfRoot(Register with, RootIndex index, Label* if_equal) {
916 917 918 919
    UseScratchRegisterScope temps(this);
    Register scratch = temps.Acquire();
    LoadRoot(scratch, index);
    Branch(if_equal, eq, with, Operand(scratch));
920 921 922
  }

  // Compare the object in a register to a value and jump if they are not equal.
923
  void JumpIfNotRoot(Register with, RootIndex index, Label* if_not_equal) {
924 925 926 927
    UseScratchRegisterScope temps(this);
    Register scratch = temps.Acquire();
    LoadRoot(scratch, index);
    Branch(if_not_equal, ne, with, Operand(scratch));
928 929
  }

Yu Yin's avatar
Yu Yin committed
930 931 932 933 934
  // 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);

935 936 937 938 939 940 941 942 943
  // ---------------------------------------------------------------------------
  // 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(
944 945
      Register object, int offset, Register value, Register scratch,
      RAStatus ra_status, SaveFPRegsMode save_fp,
946
      RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET,
947
      SmiCheck smi_check = INLINE_SMI_CHECK);
948 949 950 951 952

  // 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(
953
      Register object, Register address, Register value, RAStatus ra_status,
954 955
      SaveFPRegsMode save_fp,
      RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET,
956
      SmiCheck smi_check = INLINE_SMI_CHECK);
957

958
  void Pref(int32_t hint, const MemOperand& rs);
959

960 961
  // ---------------------------------------------------------------------------
  // Pseudo-instructions.
962

963 964 965
  void LoadWordPair(Register rd, const MemOperand& rs, Register scratch = at);
  void StoreWordPair(Register rd, const MemOperand& rs, Register scratch = at);

966 967 968 969 970 971 972 973 974 975 976 977 978
  // 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);

979 980
  void Madd_s(FPURegister fd, FPURegister fr, FPURegister fs, FPURegister ft,
              FPURegister scratch);
981 982 983 984 985 986
  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);
987 988 989 990

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

991 992 993 994 995
  // Truncates a double using a specific rounding mode, and writes the value
  // to the result register.
  // The except_flag will contain any exceptions caused by the instruction.
  // If check_inexact is kDontCheckForInexactConversion, then the inexact
  // exception is masked.
996 997 998 999 1000
  void EmitFPUTruncate(
      FPURoundingMode rounding_mode, Register result,
      DoubleRegister double_input, Register scratch,
      DoubleRegister double_scratch, Register except_flag,
      CheckForInexactConversion check_inexact = kDontCheckForInexactConversion);
1001 1002 1003 1004 1005

  // 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.
1006 1007
  void EnterExitFrame(bool save_doubles, int stack_space = 0,
                      StackFrame::Type frame_type = StackFrame::EXIT);
1008 1009

  // Leave the current exit frame.
1010
  void LeaveExitFrame(bool save_doubles, Register arg_count,
1011
                      bool do_return = NO_EMIT_RETURN,
1012
                      bool argument_count_is_length = false);
1013

1014 1015
  void LoadMap(Register destination, Register object);

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

1019
  // Load the global proxy from the current context.
1020 1021 1022
  void LoadGlobalProxy(Register dst) {
    LoadNativeContextSlot(Context::GLOBAL_PROXY_INDEX, dst);
  }
1023

1024
  void LoadNativeContextSlot(int index, Register dst);
1025 1026 1027

  // Load the initial map from the global function. The registers
  // function and map can be the same, function is then overwritten.
1028
  void LoadGlobalFunctionInitialMap(Register function, Register map,
1029 1030 1031 1032 1033 1034
                                    Register scratch);

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

  // Invoke the JavaScript function code by either calling or jumping.
1035
  void InvokeFunctionCode(Register function, Register new_target,
1036 1037
                          Register expected_parameter_count,
                          Register actual_parameter_count, InvokeFlag flag);
1038

1039 1040
  // On function call, call into the debugger if necessary.
  void CheckDebugHook(Register fun, Register new_target,
1041 1042
                      Register expected_parameter_count,
                      Register actual_parameter_count);
1043

1044 1045
  // Invoke the JavaScript function in the given register. Changes the
  // current context to the context in the function before invoking.
1046 1047 1048 1049 1050
  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);
1051

1052
  // Frame restart support.
1053
  void MaybeDropFrames();
1054 1055 1056

  // Exception handling.

1057 1058
  // Push a new stack handler and link into stack handler chain.
  void PushStackHandler();
1059

1060
  // Unlink the stack handler on top of the stack from the stack handler chain.
1061
  // Must preserve the result register.
1062
  void PopStackHandler();
1063 1064 1065 1066

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

1067
  void GetObjectType(Register function, Register map, Register type_reg);
1068 1069 1070 1071 1072

  // -------------------------------------------------------------------------
  // Runtime calls.

  // Call a runtime routine.
1073
  void CallRuntime(const Runtime::Function* f, int num_arguments,
1074
                   SaveFPRegsMode save_doubles = kDontSaveFPRegs);
1075 1076

  // Convenience function: Same as above, but takes the fid instead.
1077
  void CallRuntime(Runtime::FunctionId fid,
1078
                   SaveFPRegsMode save_doubles = kDontSaveFPRegs) {
1079
    const Runtime::Function* function = Runtime::FunctionForId(fid);
1080
    CallRuntime(function, function->nargs, save_doubles);
1081 1082 1083 1084
  }

  // Convenience function: Same as above, but takes the fid instead.
  void CallRuntime(Runtime::FunctionId fid, int num_arguments,
1085 1086
                   SaveFPRegsMode save_doubles = kDontSaveFPRegs) {
    CallRuntime(Runtime::FunctionForId(fid), num_arguments, save_doubles);
1087 1088 1089
  }

  // Convenience function: tail call a runtime routine (jump).
1090
  void TailCallRuntime(Runtime::FunctionId fid);
1091 1092 1093

  // Jump to the builtin routine.
  void JumpToExternalReference(const ExternalReference& builtin,
1094 1095
                               BranchDelaySlot bd = PROTECT,
                               bool builtin_exit_frame = false);
1096

1097
  // Generates a trampoline to jump to the off-heap instruction stream.
1098
  void JumpToInstructionStream(Address entry);
1099

1100 1101 1102 1103
  // ---------------------------------------------------------------------------
  // In-place weak references.
  void LoadWeakValue(Register out, Register in, Label* target_if_cleared);

1104 1105 1106
  // -------------------------------------------------------------------------
  // StatsCounter support.

1107 1108 1109 1110
  void IncrementCounter(StatsCounter* counter, int value, Register scratch1,
                        Register scratch2);
  void DecrementCounter(StatsCounter* counter, int value, Register scratch1,
                        Register scratch2);
1111 1112 1113 1114 1115 1116 1117 1118 1119

  // -------------------------------------------------------------------------
  // Smi utilities.

  void SmiTag(Register dst, Register src) {
    STATIC_ASSERT(kSmiTag == 0);
    if (SmiValuesAre32Bits()) {
      dsll32(dst, src, 0);
    } else {
1120
      DCHECK(SmiValuesAre31Bits());
1121 1122 1123 1124
      Addu(dst, src, src);
    }
  }

1125
  void SmiTag(Register reg) { SmiTag(reg, reg); }
1126 1127 1128 1129 1130 1131 1132

  // 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 {
1133
      DCHECK(SmiValuesAre31Bits());
1134
      DCHECK_GE(scale, kSmiTagSize);
1135 1136 1137 1138 1139 1140 1141 1142 1143 1144
      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.
1145
  void JumpIfNotSmi(Register value, Label* not_smi_label, Register scratch = at,
1146 1147 1148 1149 1150 1151
                    BranchDelaySlot bd = PROTECT);

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

1152 1153 1154
  // Abort execution if argument is not a Constructor, enabled via --debug-code.
  void AssertConstructor(Register object);

1155 1156 1157
  // Abort execution if argument is not a JSFunction, enabled via --debug-code.
  void AssertFunction(Register object);

1158 1159 1160 1161
  // Abort execution if argument is not a JSBoundFunction,
  // enabled via --debug-code.
  void AssertBoundFunction(Register object);

1162
  // Abort execution if argument is not a JSGeneratorObject (or subclass),
1163
  // enabled via --debug-code.
1164
  void AssertGeneratorObject(Register object);
1165

1166 1167 1168 1169
  // Abort execution if argument is not undefined or an AllocationSite, enabled
  // via --debug-code.
  void AssertUndefinedOrAllocationSite(Register object, Register scratch);

1170
  template <typename Field>
1171 1172 1173 1174
  void DecodeField(Register dst, Register src) {
    Ext(dst, src, Field::kShift, Field::kSize);
  }

1175
  template <typename Field>
1176 1177 1178 1179 1180 1181
  void DecodeField(Register reg) {
    DecodeField<Field>(reg, reg);
  }

 private:
  // Helper functions for generating invokes.
1182 1183 1184
  void InvokePrologue(Register expected_parameter_count,
                      Register actual_parameter_count, Label* done,
                      InvokeFlag flag);
1185 1186 1187 1188 1189 1190 1191

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

  // Needs access to SafepointRegisterStackIndex for compiled frame
  // traversal.
  friend class StandardFrame;
1192 1193

  DISALLOW_IMPLICIT_CONSTRUCTORS(MacroAssembler);
1194 1195
};

1196
template <typename Func>
1197
void TurboAssembler::GenerateSwitchTable(Register index, size_t case_count,
1198 1199 1200
                                         Func GetLabelFunction) {
  // Ensure that dd-ed labels following this instruction use 8 bytes aligned
  // addresses.
1201 1202 1203 1204
  BlockTrampolinePoolFor(static_cast<int>(case_count) * 2 +
                         kSwitchTablePrologueSize);
  UseScratchRegisterScope temps(this);
  Register scratch = temps.Acquire();
1205 1206 1207 1208 1209
  if (kArchVariant >= kMips64r6) {
    // Opposite of Align(8) as we have odd number of instructions in this case.
    if ((pc_offset() & 7) == 0) {
      nop();
    }
1210 1211 1212
    addiupc(scratch, 5);
    Dlsa(scratch, scratch, index, kPointerSizeLog2);
    Ld(scratch, MemOperand(scratch));
1213 1214 1215
  } else {
    Label here;
    Align(8);
1216
    push(ra);
1217
    bal(&here);
1218
    dsll(scratch, index, kPointerSizeLog2);  // Branch delay slot.
1219
    bind(&here);
1220
    daddu(scratch, scratch, ra);
1221
    pop(ra);
1222
    Ld(scratch, MemOperand(scratch, 6 * v8::internal::kInstrSize));
1223
  }
1224
  jr(scratch);
1225 1226 1227 1228 1229
  nop();  // Branch delay slot nop.
  for (size_t index = 0; index < case_count; ++index) {
    dd(GetLabelFunction(index));
  }
}
1230 1231 1232

#define ACCESS_MASM(masm) masm->

1233 1234
}  // namespace internal
}  // namespace v8
1235

1236
#endif  // V8_CODEGEN_MIPS64_MACRO_ASSEMBLER_MIPS64_H_