blob: d4463008c8fe83ff3e583cac5bbc5e1789fc1fbb [file] [log] [blame]
// Copyright 2014 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.
#include "src/assembler-inl.h"
#include "src/callable.h"
#include "src/compilation-info.h"
#include "src/compiler/code-generator-impl.h"
#include "src/compiler/code-generator.h"
#include "src/compiler/gap-resolver.h"
#include "src/compiler/node-matchers.h"
#include "src/compiler/osr.h"
#include "src/heap/heap-inl.h"
#include "src/mips64/macro-assembler-mips64.h"
namespace v8 {
namespace internal {
namespace compiler {
#define __ tasm()->
// TODO(plind): Possibly avoid using these lithium names.
#define kScratchReg kLithiumScratchReg
#define kScratchReg2 kLithiumScratchReg2
#define kScratchDoubleReg kLithiumScratchDouble
// TODO(plind): consider renaming these macros.
#define TRACE_MSG(msg) \
PrintF("code_gen: \'%s\' in function %s at line %d\n", msg, __FUNCTION__, \
__LINE__)
#define TRACE_UNIMPL() \
PrintF("UNIMPLEMENTED code_generator_mips: %s at line %d\n", __FUNCTION__, \
__LINE__)
// Adds Mips-specific methods to convert InstructionOperands.
class MipsOperandConverter final : public InstructionOperandConverter {
public:
MipsOperandConverter(CodeGenerator* gen, Instruction* instr)
: InstructionOperandConverter(gen, instr) {}
FloatRegister OutputSingleRegister(size_t index = 0) {
return ToSingleRegister(instr_->OutputAt(index));
}
FloatRegister InputSingleRegister(size_t index) {
return ToSingleRegister(instr_->InputAt(index));
}
FloatRegister ToSingleRegister(InstructionOperand* op) {
// Single (Float) and Double register namespace is same on MIPS,
// both are typedefs of FPURegister.
return ToDoubleRegister(op);
}
Register InputOrZeroRegister(size_t index) {
if (instr_->InputAt(index)->IsImmediate()) {
DCHECK_EQ(0, InputInt32(index));
return zero_reg;
}
return InputRegister(index);
}
DoubleRegister InputOrZeroDoubleRegister(size_t index) {
if (instr_->InputAt(index)->IsImmediate()) return kDoubleRegZero;
return InputDoubleRegister(index);
}
DoubleRegister InputOrZeroSingleRegister(size_t index) {
if (instr_->InputAt(index)->IsImmediate()) return kDoubleRegZero;
return InputSingleRegister(index);
}
Operand InputImmediate(size_t index) {
Constant constant = ToConstant(instr_->InputAt(index));
switch (constant.type()) {
case Constant::kInt32:
return Operand(constant.ToInt32());
case Constant::kInt64:
return Operand(constant.ToInt64());
case Constant::kFloat32:
return Operand::EmbeddedNumber(constant.ToFloat32());
case Constant::kFloat64:
return Operand::EmbeddedNumber(constant.ToFloat64().value());
case Constant::kExternalReference:
case Constant::kHeapObject:
// TODO(plind): Maybe we should handle ExtRef & HeapObj here?
// maybe not done on arm due to const pool ??
break;
case Constant::kRpoNumber:
UNREACHABLE(); // TODO(titzer): RPO immediates on mips?
break;
}
UNREACHABLE();
}
Operand InputOperand(size_t index) {
InstructionOperand* op = instr_->InputAt(index);
if (op->IsRegister()) {
return Operand(ToRegister(op));
}
return InputImmediate(index);
}
MemOperand MemoryOperand(size_t* first_index) {
const size_t index = *first_index;
switch (AddressingModeField::decode(instr_->opcode())) {
case kMode_None:
break;
case kMode_MRI:
*first_index += 2;
return MemOperand(InputRegister(index + 0), InputInt32(index + 1));
case kMode_MRR:
// TODO(plind): r6 address mode, to be implemented ...
UNREACHABLE();
}
UNREACHABLE();
}
MemOperand MemoryOperand(size_t index = 0) { return MemoryOperand(&index); }
MemOperand ToMemOperand(InstructionOperand* op) const {
DCHECK_NOT_NULL(op);
DCHECK(op->IsStackSlot() || op->IsFPStackSlot());
return SlotToMemOperand(AllocatedOperand::cast(op)->index());
}
MemOperand SlotToMemOperand(int slot) const {
FrameOffset offset = frame_access_state()->GetFrameOffset(slot);
return MemOperand(offset.from_stack_pointer() ? sp : fp, offset.offset());
}
};
static inline bool HasRegisterInput(Instruction* instr, size_t index) {
return instr->InputAt(index)->IsRegister();
}
namespace {
class OutOfLineRound : public OutOfLineCode {
public:
OutOfLineRound(CodeGenerator* gen, DoubleRegister result)
: OutOfLineCode(gen), result_(result) {}
void Generate() final {
// Handle rounding to zero case where sign has to be preserved.
// High bits of double input already in kScratchReg.
__ dsrl(at, kScratchReg, 31);
__ dsll(at, at, 31);
__ mthc1(at, result_);
}
private:
DoubleRegister const result_;
};
class OutOfLineRound32 : public OutOfLineCode {
public:
OutOfLineRound32(CodeGenerator* gen, DoubleRegister result)
: OutOfLineCode(gen), result_(result) {}
void Generate() final {
// Handle rounding to zero case where sign has to be preserved.
// High bits of float input already in kScratchReg.
__ srl(at, kScratchReg, 31);
__ sll(at, at, 31);
__ mtc1(at, result_);
}
private:
DoubleRegister const result_;
};
class OutOfLineRecordWrite final : public OutOfLineCode {
public:
OutOfLineRecordWrite(CodeGenerator* gen, Register object, Register index,
Register value, Register scratch0, Register scratch1,
RecordWriteMode mode)
: OutOfLineCode(gen),
object_(object),
index_(index),
value_(value),
scratch0_(scratch0),
scratch1_(scratch1),
mode_(mode),
must_save_lr_(!gen->frame_access_state()->has_frame()),
zone_(gen->zone()) {}
void SaveRegisters(RegList registers) {
DCHECK_LT(0, NumRegs(registers));
RegList regs = 0;
for (int i = 0; i < Register::kNumRegisters; ++i) {
if ((registers >> i) & 1u) {
regs |= Register::from_code(i).bit();
}
}
__ MultiPush(regs | ra.bit());
}
void RestoreRegisters(RegList registers) {
DCHECK_LT(0, NumRegs(registers));
RegList regs = 0;
for (int i = 0; i < Register::kNumRegisters; ++i) {
if ((registers >> i) & 1u) {
regs |= Register::from_code(i).bit();
}
}
__ MultiPop(regs | ra.bit());
}
void Generate() final {
if (mode_ > RecordWriteMode::kValueIsPointer) {
__ JumpIfSmi(value_, exit());
}
__ CheckPageFlag(value_, scratch0_,
MemoryChunk::kPointersToHereAreInterestingMask, eq,
exit());
__ Daddu(scratch1_, object_, index_);
RememberedSetAction const remembered_set_action =
mode_ > RecordWriteMode::kValueIsMap ? EMIT_REMEMBERED_SET
: OMIT_REMEMBERED_SET;
SaveFPRegsMode const save_fp_mode =
frame()->DidAllocateDoubleRegisters() ? kSaveFPRegs : kDontSaveFPRegs;
if (must_save_lr_) {
// We need to save and restore ra if the frame was elided.
__ Push(ra);
}
__ CallRecordWriteStub(object_, scratch1_, remembered_set_action,
save_fp_mode);
if (must_save_lr_) {
__ Pop(ra);
}
}
private:
Register const object_;
Register const index_;
Register const value_;
Register const scratch0_;
Register const scratch1_;
RecordWriteMode const mode_;
bool must_save_lr_;
Zone* zone_;
};
#define CREATE_OOL_CLASS(ool_name, tasm_ool_name, T) \
class ool_name final : public OutOfLineCode { \
public: \
ool_name(CodeGenerator* gen, T dst, T src1, T src2) \
: OutOfLineCode(gen), dst_(dst), src1_(src1), src2_(src2) {} \
\
void Generate() final { __ tasm_ool_name(dst_, src1_, src2_); } \
\
private: \
T const dst_; \
T const src1_; \
T const src2_; \
}
CREATE_OOL_CLASS(OutOfLineFloat32Max, Float32MaxOutOfLine, FPURegister);
CREATE_OOL_CLASS(OutOfLineFloat32Min, Float32MinOutOfLine, FPURegister);
CREATE_OOL_CLASS(OutOfLineFloat64Max, Float64MaxOutOfLine, FPURegister);
CREATE_OOL_CLASS(OutOfLineFloat64Min, Float64MinOutOfLine, FPURegister);
#undef CREATE_OOL_CLASS
Condition FlagsConditionToConditionCmp(FlagsCondition condition) {
switch (condition) {
case kEqual:
return eq;
case kNotEqual:
return ne;
case kSignedLessThan:
return lt;
case kSignedGreaterThanOrEqual:
return ge;
case kSignedLessThanOrEqual:
return le;
case kSignedGreaterThan:
return gt;
case kUnsignedLessThan:
return lo;
case kUnsignedGreaterThanOrEqual:
return hs;
case kUnsignedLessThanOrEqual:
return ls;
case kUnsignedGreaterThan:
return hi;
case kUnorderedEqual:
case kUnorderedNotEqual:
break;
default:
break;
}
UNREACHABLE();
}
Condition FlagsConditionToConditionTst(FlagsCondition condition) {
switch (condition) {
case kNotEqual:
return ne;
case kEqual:
return eq;
default:
break;
}
UNREACHABLE();
}
Condition FlagsConditionToConditionOvf(FlagsCondition condition) {
switch (condition) {
case kOverflow:
return ne;
case kNotOverflow:
return eq;
default:
break;
}
UNREACHABLE();
}
FPUCondition FlagsConditionToConditionCmpFPU(bool& predicate,
FlagsCondition condition) {
switch (condition) {
case kEqual:
predicate = true;
return EQ;
case kNotEqual:
predicate = false;
return EQ;
case kUnsignedLessThan:
predicate = true;
return OLT;
case kUnsignedGreaterThanOrEqual:
predicate = false;
return ULT;
case kUnsignedLessThanOrEqual:
predicate = true;
return OLE;
case kUnsignedGreaterThan:
predicate = false;
return ULE;
case kUnorderedEqual:
case kUnorderedNotEqual:
predicate = true;
break;
default:
predicate = true;
break;
}
UNREACHABLE();
}
} // namespace
#define ASSEMBLE_ROUND_DOUBLE_TO_DOUBLE(mode) \
if (kArchVariant == kMips64r6) { \
__ cfc1(kScratchReg, FCSR); \
__ li(at, Operand(mode_##mode)); \
__ ctc1(at, FCSR); \
__ rint_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0)); \
__ ctc1(kScratchReg, FCSR); \
} else { \
auto ool = new (zone()) OutOfLineRound(this, i.OutputDoubleRegister()); \
Label done; \
__ mfhc1(kScratchReg, i.InputDoubleRegister(0)); \
__ Ext(at, kScratchReg, HeapNumber::kExponentShift, \
HeapNumber::kExponentBits); \
__ Branch(USE_DELAY_SLOT, &done, hs, at, \
Operand(HeapNumber::kExponentBias + HeapNumber::kMantissaBits)); \
__ mov_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0)); \
__ mode##_l_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0)); \
__ dmfc1(at, i.OutputDoubleRegister()); \
__ Branch(USE_DELAY_SLOT, ool->entry(), eq, at, Operand(zero_reg)); \
__ cvt_d_l(i.OutputDoubleRegister(), i.OutputDoubleRegister()); \
__ bind(ool->exit()); \
__ bind(&done); \
}
#define ASSEMBLE_ROUND_FLOAT_TO_FLOAT(mode) \
if (kArchVariant == kMips64r6) { \
__ cfc1(kScratchReg, FCSR); \
__ li(at, Operand(mode_##mode)); \
__ ctc1(at, FCSR); \
__ rint_s(i.OutputDoubleRegister(), i.InputDoubleRegister(0)); \
__ ctc1(kScratchReg, FCSR); \
} else { \
int32_t kFloat32ExponentBias = 127; \
int32_t kFloat32MantissaBits = 23; \
int32_t kFloat32ExponentBits = 8; \
auto ool = new (zone()) OutOfLineRound32(this, i.OutputDoubleRegister()); \
Label done; \
__ mfc1(kScratchReg, i.InputDoubleRegister(0)); \
__ Ext(at, kScratchReg, kFloat32MantissaBits, kFloat32ExponentBits); \
__ Branch(USE_DELAY_SLOT, &done, hs, at, \
Operand(kFloat32ExponentBias + kFloat32MantissaBits)); \
__ mov_s(i.OutputDoubleRegister(), i.InputDoubleRegister(0)); \
__ mode##_w_s(i.OutputDoubleRegister(), i.InputDoubleRegister(0)); \
__ mfc1(at, i.OutputDoubleRegister()); \
__ Branch(USE_DELAY_SLOT, ool->entry(), eq, at, Operand(zero_reg)); \
__ cvt_s_w(i.OutputDoubleRegister(), i.OutputDoubleRegister()); \
__ bind(ool->exit()); \
__ bind(&done); \
}
#define ASSEMBLE_ATOMIC_LOAD_INTEGER(asm_instr) \
do { \
__ asm_instr(i.OutputRegister(), i.MemoryOperand()); \
__ sync(); \
} while (0)
#define ASSEMBLE_ATOMIC_STORE_INTEGER(asm_instr) \
do { \
__ sync(); \
__ asm_instr(i.InputOrZeroRegister(2), i.MemoryOperand()); \
__ sync(); \
} while (0)
#define ASSEMBLE_ATOMIC_BINOP(bin_instr) \
do { \
Label binop; \
__ Daddu(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1)); \
__ sync(); \
__ bind(&binop); \
__ Ll(i.OutputRegister(0), MemOperand(i.TempRegister(0), 0)); \
__ bin_instr(i.TempRegister(1), i.OutputRegister(0), \
Operand(i.InputRegister(2))); \
__ Sc(i.TempRegister(1), MemOperand(i.TempRegister(0), 0)); \
__ BranchShort(&binop, eq, i.TempRegister(1), Operand(zero_reg)); \
__ sync(); \
} while (0)
#define ASSEMBLE_ATOMIC_BINOP_EXT(sign_extend, size, bin_instr) \
do { \
Label binop; \
__ daddu(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1)); \
__ andi(i.TempRegister(3), i.TempRegister(0), 0x3); \
__ Dsubu(i.TempRegister(0), i.TempRegister(0), \
Operand(i.TempRegister(3))); \
__ sll(i.TempRegister(3), i.TempRegister(3), 3); \
__ sync(); \
__ bind(&binop); \
__ Ll(i.TempRegister(1), MemOperand(i.TempRegister(0), 0)); \
__ ExtractBits(i.OutputRegister(0), i.TempRegister(1), i.TempRegister(3), \
size, sign_extend); \
__ bin_instr(i.TempRegister(2), i.OutputRegister(0), \
Operand(i.InputRegister(2))); \
__ InsertBits(i.TempRegister(1), i.TempRegister(2), i.TempRegister(3), \
size); \
__ Sc(i.TempRegister(1), MemOperand(i.TempRegister(0), 0)); \
__ BranchShort(&binop, eq, i.TempRegister(1), Operand(zero_reg)); \
__ sync(); \
} while (0)
#define ASSEMBLE_ATOMIC_EXCHANGE_INTEGER() \
do { \
Label exchange; \
__ sync(); \
__ bind(&exchange); \
__ daddu(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1)); \
__ Ll(i.OutputRegister(0), MemOperand(i.TempRegister(0), 0)); \
__ mov(i.TempRegister(1), i.InputRegister(2)); \
__ Sc(i.TempRegister(1), MemOperand(i.TempRegister(0), 0)); \
__ BranchShort(&exchange, eq, i.TempRegister(1), Operand(zero_reg)); \
__ sync(); \
} while (0)
#define ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT(sign_extend, size) \
do { \
Label exchange; \
__ daddu(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1)); \
__ andi(i.TempRegister(1), i.TempRegister(0), 0x3); \
__ Dsubu(i.TempRegister(0), i.TempRegister(0), \
Operand(i.TempRegister(1))); \
__ sll(i.TempRegister(1), i.TempRegister(1), 3); \
__ sync(); \
__ bind(&exchange); \
__ Ll(i.TempRegister(2), MemOperand(i.TempRegister(0), 0)); \
__ ExtractBits(i.OutputRegister(0), i.TempRegister(2), i.TempRegister(1), \
size, sign_extend); \
__ InsertBits(i.TempRegister(2), i.InputRegister(2), i.TempRegister(1), \
size); \
__ Sc(i.TempRegister(2), MemOperand(i.TempRegister(0), 0)); \
__ BranchShort(&exchange, eq, i.TempRegister(2), Operand(zero_reg)); \
__ sync(); \
} while (0)
#define ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER() \
do { \
Label compareExchange; \
Label exit; \
__ daddu(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1)); \
__ sync(); \
__ bind(&compareExchange); \
__ Ll(i.OutputRegister(0), MemOperand(i.TempRegister(0), 0)); \
__ BranchShort(&exit, ne, i.InputRegister(2), \
Operand(i.OutputRegister(0))); \
__ mov(i.TempRegister(2), i.InputRegister(3)); \
__ Sc(i.TempRegister(2), MemOperand(i.TempRegister(0), 0)); \
__ BranchShort(&compareExchange, eq, i.TempRegister(2), \
Operand(zero_reg)); \
__ bind(&exit); \
__ sync(); \
} while (0)
#define ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER_EXT(sign_extend, size) \
do { \
Label compareExchange; \
Label exit; \
__ daddu(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1)); \
__ andi(i.TempRegister(1), i.TempRegister(0), 0x3); \
__ Dsubu(i.TempRegister(0), i.TempRegister(0), \
Operand(i.TempRegister(1))); \
__ sll(i.TempRegister(1), i.TempRegister(1), 3); \
__ sync(); \
__ bind(&compareExchange); \
__ Ll(i.TempRegister(2), MemOperand(i.TempRegister(0), 0)); \
__ ExtractBits(i.OutputRegister(0), i.TempRegister(2), i.TempRegister(1), \
size, sign_extend); \
__ BranchShort(&exit, ne, i.InputRegister(2), \
Operand(i.OutputRegister(0))); \
__ InsertBits(i.TempRegister(2), i.InputRegister(3), i.TempRegister(1), \
size); \
__ Sc(i.TempRegister(2), MemOperand(i.TempRegister(0), 0)); \
__ BranchShort(&compareExchange, eq, i.TempRegister(2), \
Operand(zero_reg)); \
__ bind(&exit); \
__ sync(); \
} while (0)
#define ASSEMBLE_IEEE754_BINOP(name) \
do { \
FrameScope scope(tasm(), StackFrame::MANUAL); \
__ PrepareCallCFunction(0, 2, kScratchReg); \
__ MovToFloatParameters(i.InputDoubleRegister(0), \
i.InputDoubleRegister(1)); \
__ CallCFunction( \
ExternalReference::ieee754_##name##_function(tasm()->isolate()), 0, \
2); \
/* Move the result in the double result register. */ \
__ MovFromFloatResult(i.OutputDoubleRegister()); \
} while (0)
#define ASSEMBLE_IEEE754_UNOP(name) \
do { \
FrameScope scope(tasm(), StackFrame::MANUAL); \
__ PrepareCallCFunction(0, 1, kScratchReg); \
__ MovToFloatParameter(i.InputDoubleRegister(0)); \
__ CallCFunction( \
ExternalReference::ieee754_##name##_function(tasm()->isolate()), 0, \
1); \
/* Move the result in the double result register. */ \
__ MovFromFloatResult(i.OutputDoubleRegister()); \
} while (0)
void CodeGenerator::AssembleDeconstructFrame() {
__ mov(sp, fp);
__ Pop(ra, fp);
}
void CodeGenerator::AssemblePrepareTailCall() {
if (frame_access_state()->has_frame()) {
__ Ld(ra, MemOperand(fp, StandardFrameConstants::kCallerPCOffset));
__ Ld(fp, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
}
frame_access_state()->SetFrameAccessToSP();
}
void CodeGenerator::AssemblePopArgumentsAdaptorFrame(Register args_reg,
Register scratch1,
Register scratch2,
Register scratch3) {
DCHECK(!AreAliased(args_reg, scratch1, scratch2, scratch3));
Label done;
// Check if current frame is an arguments adaptor frame.
__ Ld(scratch3, MemOperand(fp, StandardFrameConstants::kContextOffset));
__ Branch(&done, ne, scratch3,
Operand(StackFrame::TypeToMarker(StackFrame::ARGUMENTS_ADAPTOR)));
// Load arguments count from current arguments adaptor frame (note, it
// does not include receiver).
Register caller_args_count_reg = scratch1;
__ Ld(caller_args_count_reg,
MemOperand(fp, ArgumentsAdaptorFrameConstants::kLengthOffset));
__ SmiUntag(caller_args_count_reg);
ParameterCount callee_args_count(args_reg);
__ PrepareForTailCall(callee_args_count, caller_args_count_reg, scratch2,
scratch3);
__ bind(&done);
}
namespace {
void AdjustStackPointerForTailCall(TurboAssembler* tasm,
FrameAccessState* state,
int new_slot_above_sp,
bool allow_shrinkage = true) {
int current_sp_offset = state->GetSPToFPSlotCount() +
StandardFrameConstants::kFixedSlotCountAboveFp;
int stack_slot_delta = new_slot_above_sp - current_sp_offset;
if (stack_slot_delta > 0) {
tasm->Dsubu(sp, sp, stack_slot_delta * kPointerSize);
state->IncreaseSPDelta(stack_slot_delta);
} else if (allow_shrinkage && stack_slot_delta < 0) {
tasm->Daddu(sp, sp, -stack_slot_delta * kPointerSize);
state->IncreaseSPDelta(stack_slot_delta);
}
}
} // namespace
void CodeGenerator::AssembleTailCallBeforeGap(Instruction* instr,
int first_unused_stack_slot) {
AdjustStackPointerForTailCall(tasm(), frame_access_state(),
first_unused_stack_slot, false);
}
void CodeGenerator::AssembleTailCallAfterGap(Instruction* instr,
int first_unused_stack_slot) {
AdjustStackPointerForTailCall(tasm(), frame_access_state(),
first_unused_stack_slot);
}
// Check if the code object is marked for deoptimization. If it is, then it
// jumps to the CompileLazyDeoptimizedCode builtin. In order to do this we need
// to:
// 1. load the address of the current instruction;
// 2. read from memory the word that contains that bit, which can be found in
// the flags in the referenced {CodeDataContainer} object;
// 3. test kMarkedForDeoptimizationBit in those flags; and
// 4. if it is not zero then it jumps to the builtin.
void CodeGenerator::BailoutIfDeoptimized() {
Label current;
// This push on ra and the pop below together ensure that we restore the
// register ra, which is needed while computing frames for deoptimization.
__ push(ra);
// The bal instruction puts the address of the current instruction into
// the return address (ra) register, which we can use later on.
__ bal(&current);
__ nop();
int pc = __ pc_offset();
__ bind(&current);
int offset = Code::kCodeDataContainerOffset - (Code::kHeaderSize + pc);
__ Ld(a2, MemOperand(ra, offset));
__ pop(ra);
__ Lw(a2, FieldMemOperand(a2, CodeDataContainer::kKindSpecificFlagsOffset));
__ And(a2, a2, Operand(1 << Code::kMarkedForDeoptimizationBit));
Handle<Code> code = isolate()->builtins()->builtin_handle(
Builtins::kCompileLazyDeoptimizedCode);
__ Jump(code, RelocInfo::CODE_TARGET, ne, a2, Operand(zero_reg));
}
// Assembles an instruction after register allocation, producing machine code.
CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction(
Instruction* instr) {
MipsOperandConverter i(this, instr);
InstructionCode opcode = instr->opcode();
ArchOpcode arch_opcode = ArchOpcodeField::decode(opcode);
switch (arch_opcode) {
case kArchCallCodeObject: {
if (instr->InputAt(0)->IsImmediate()) {
__ Call(i.InputCode(0), RelocInfo::CODE_TARGET);
} else {
__ daddiu(at, i.InputRegister(0), Code::kHeaderSize - kHeapObjectTag);
__ Call(at);
}
RecordCallPosition(instr);
frame_access_state()->ClearSPDelta();
break;
}
case kArchCallWasmFunction: {
if (arch_opcode == kArchTailCallCodeObjectFromJSFunction) {
AssemblePopArgumentsAdaptorFrame(kJavaScriptCallArgCountRegister,
i.TempRegister(0), i.TempRegister(1),
i.TempRegister(2));
}
if (instr->InputAt(0)->IsImmediate()) {
Address wasm_code = reinterpret_cast<Address>(
i.ToConstant(instr->InputAt(0)).ToInt64());
__ Call(wasm_code, info()->IsWasm() ? RelocInfo::WASM_CALL
: RelocInfo::JS_TO_WASM_CALL);
} else {
__ daddiu(at, i.InputRegister(0), 0);
__ Call(at);
}
RecordCallPosition(instr);
frame_access_state()->ClearSPDelta();
break;
}
case kArchTailCallCodeObjectFromJSFunction:
case kArchTailCallCodeObject: {
if (arch_opcode == kArchTailCallCodeObjectFromJSFunction) {
AssemblePopArgumentsAdaptorFrame(kJavaScriptCallArgCountRegister,
i.TempRegister(0), i.TempRegister(1),
i.TempRegister(2));
}
if (instr->InputAt(0)->IsImmediate()) {
__ Jump(i.InputCode(0), RelocInfo::CODE_TARGET);
} else {
__ daddiu(at, i.InputRegister(0), Code::kHeaderSize - kHeapObjectTag);
__ Jump(at);
}
frame_access_state()->ClearSPDelta();
frame_access_state()->SetFrameAccessToDefault();
break;
}
case kArchTailCallWasm: {
if (instr->InputAt(0)->IsImmediate()) {
Address wasm_code = reinterpret_cast<Address>(
i.ToConstant(instr->InputAt(0)).ToInt64());
__ Jump(wasm_code, info()->IsWasm() ? RelocInfo::WASM_CALL
: RelocInfo::JS_TO_WASM_CALL);
} else {
__ daddiu(at, i.InputRegister(0), 0);
__ Jump(at);
}
frame_access_state()->ClearSPDelta();
frame_access_state()->SetFrameAccessToDefault();
break;
}
case kArchTailCallAddress: {
CHECK(!instr->InputAt(0)->IsImmediate());
__ Jump(i.InputRegister(0));
frame_access_state()->ClearSPDelta();
frame_access_state()->SetFrameAccessToDefault();
break;
}
case kArchCallJSFunction: {
Register func = i.InputRegister(0);
if (FLAG_debug_code) {
// Check the function's context matches the context argument.
__ Ld(kScratchReg, FieldMemOperand(func, JSFunction::kContextOffset));
__ Assert(eq, AbortReason::kWrongFunctionContext, cp,
Operand(kScratchReg));
}
__ Ld(at, FieldMemOperand(func, JSFunction::kCodeOffset));
__ Daddu(at, at, Operand(Code::kHeaderSize - kHeapObjectTag));
__ Call(at);
RecordCallPosition(instr);
frame_access_state()->ClearSPDelta();
break;
}
case kArchPrepareCallCFunction: {
int const num_parameters = MiscField::decode(instr->opcode());
__ PrepareCallCFunction(num_parameters, kScratchReg);
// Frame alignment requires using FP-relative frame addressing.
frame_access_state()->SetFrameAccessToFP();
break;
}
case kArchSaveCallerRegisters: {
fp_mode_ =
static_cast<SaveFPRegsMode>(MiscField::decode(instr->opcode()));
DCHECK(fp_mode_ == kDontSaveFPRegs || fp_mode_ == kSaveFPRegs);
// kReturnRegister0 should have been saved before entering the stub.
int bytes = __ PushCallerSaved(fp_mode_, kReturnRegister0);
DCHECK_EQ(0, bytes % kPointerSize);
DCHECK_EQ(0, frame_access_state()->sp_delta());
frame_access_state()->IncreaseSPDelta(bytes / kPointerSize);
DCHECK(!caller_registers_saved_);
caller_registers_saved_ = true;
break;
}
case kArchRestoreCallerRegisters: {
DCHECK(fp_mode_ ==
static_cast<SaveFPRegsMode>(MiscField::decode(instr->opcode())));
DCHECK(fp_mode_ == kDontSaveFPRegs || fp_mode_ == kSaveFPRegs);
// Don't overwrite the returned value.
int bytes = __ PopCallerSaved(fp_mode_, kReturnRegister0);
frame_access_state()->IncreaseSPDelta(-(bytes / kPointerSize));
DCHECK_EQ(0, frame_access_state()->sp_delta());
DCHECK(caller_registers_saved_);
caller_registers_saved_ = false;
break;
}
case kArchPrepareTailCall:
AssemblePrepareTailCall();
break;
case kArchCallCFunction: {
int const num_parameters = MiscField::decode(instr->opcode());
if (instr->InputAt(0)->IsImmediate()) {
ExternalReference ref = i.InputExternalReference(0);
__ CallCFunction(ref, num_parameters);
} else {
Register func = i.InputRegister(0);
__ CallCFunction(func, num_parameters);
}
frame_access_state()->SetFrameAccessToDefault();
// Ideally, we should decrement SP delta to match the change of stack
// pointer in CallCFunction. However, for certain architectures (e.g.
// ARM), there may be more strict alignment requirement, causing old SP
// to be saved on the stack. In those cases, we can not calculate the SP
// delta statically.
frame_access_state()->ClearSPDelta();
if (caller_registers_saved_) {
// Need to re-sync SP delta introduced in kArchSaveCallerRegisters.
// Here, we assume the sequence to be:
// kArchSaveCallerRegisters;
// kArchCallCFunction;
// kArchRestoreCallerRegisters;
int bytes =
__ RequiredStackSizeForCallerSaved(fp_mode_, kReturnRegister0);
frame_access_state()->IncreaseSPDelta(bytes / kPointerSize);
}
break;
}
case kArchJmp:
AssembleArchJump(i.InputRpo(0));
break;
case kArchLookupSwitch:
AssembleArchLookupSwitch(instr);
break;
case kArchTableSwitch:
AssembleArchTableSwitch(instr);
break;
case kArchDebugAbort:
DCHECK(i.InputRegister(0) == a0);
if (!frame_access_state()->has_frame()) {
// We don't actually want to generate a pile of code for this, so just
// claim there is a stack frame, without generating one.
FrameScope scope(tasm(), StackFrame::NONE);
__ Call(isolate()->builtins()->builtin_handle(Builtins::kAbortJS),
RelocInfo::CODE_TARGET);
} else {
__ Call(isolate()->builtins()->builtin_handle(Builtins::kAbortJS),
RelocInfo::CODE_TARGET);
}
__ stop("kArchDebugAbort");
break;
case kArchDebugBreak:
__ stop("kArchDebugBreak");
break;
case kArchComment: {
Address comment_string = i.InputExternalReference(0).address();
__ RecordComment(reinterpret_cast<const char*>(comment_string));
break;
}
case kArchNop:
case kArchThrowTerminator:
// don't emit code for nops.
break;
case kArchDeoptimize: {
int deopt_state_id =
BuildTranslation(instr, -1, 0, OutputFrameStateCombine::Ignore());
CodeGenResult result =
AssembleDeoptimizerCall(deopt_state_id, current_source_position_);
if (result != kSuccess) return result;
break;
}
case kArchRet:
AssembleReturn(instr->InputAt(0));
break;
case kArchStackPointer:
__ mov(i.OutputRegister(), sp);
break;
case kArchFramePointer:
__ mov(i.OutputRegister(), fp);
break;
case kArchParentFramePointer:
if (frame_access_state()->has_frame()) {
__ Ld(i.OutputRegister(), MemOperand(fp, 0));
} else {
__ mov(i.OutputRegister(), fp);
}
break;
case kArchTruncateDoubleToI:
__ TruncateDoubleToIDelayed(zone(), i.OutputRegister(),
i.InputDoubleRegister(0));
break;
case kArchStoreWithWriteBarrier: {
RecordWriteMode mode =
static_cast<RecordWriteMode>(MiscField::decode(instr->opcode()));
Register object = i.InputRegister(0);
Register index = i.InputRegister(1);
Register value = i.InputRegister(2);
Register scratch0 = i.TempRegister(0);
Register scratch1 = i.TempRegister(1);
auto ool = new (zone()) OutOfLineRecordWrite(this, object, index, value,
scratch0, scratch1, mode);
__ Daddu(at, object, index);
__ Sd(value, MemOperand(at));
__ CheckPageFlag(object, scratch0,
MemoryChunk::kPointersFromHereAreInterestingMask, ne,
ool->entry());
__ bind(ool->exit());
break;
}
case kArchStackSlot: {
FrameOffset offset =
frame_access_state()->GetFrameOffset(i.InputInt32(0));
Register base_reg = offset.from_stack_pointer() ? sp : fp;
__ Daddu(i.OutputRegister(), base_reg, Operand(offset.offset()));
int alignment = i.InputInt32(1);
DCHECK(alignment == 0 || alignment == 4 || alignment == 8 ||
alignment == 16);
if (FLAG_debug_code && alignment > 0) {
// Verify that the output_register is properly aligned
__ And(kScratchReg, i.OutputRegister(), Operand(kPointerSize - 1));
__ Assert(eq, AbortReason::kAllocationIsNotDoubleAligned, kScratchReg,
Operand(zero_reg));
}
if (alignment == 2 * kPointerSize) {
Label done;
__ Daddu(kScratchReg, base_reg, Operand(offset.offset()));
__ And(kScratchReg, kScratchReg, Operand(alignment - 1));
__ BranchShort(&done, eq, kScratchReg, Operand(zero_reg));
__ Daddu(i.OutputRegister(), i.OutputRegister(), kPointerSize);
__ bind(&done);
} else if (alignment > 2 * kPointerSize) {
Label done;
__ Daddu(kScratchReg, base_reg, Operand(offset.offset()));
__ And(kScratchReg, kScratchReg, Operand(alignment - 1));
__ BranchShort(&done, eq, kScratchReg, Operand(zero_reg));
__ li(kScratchReg2, alignment);
__ Dsubu(kScratchReg2, kScratchReg2, Operand(kScratchReg));
__ Daddu(i.OutputRegister(), i.OutputRegister(), kScratchReg2);
__ bind(&done);
}
break;
}
case kIeee754Float64Acos:
ASSEMBLE_IEEE754_UNOP(acos);
break;
case kIeee754Float64Acosh:
ASSEMBLE_IEEE754_UNOP(acosh);
break;
case kIeee754Float64Asin:
ASSEMBLE_IEEE754_UNOP(asin);
break;
case kIeee754Float64Asinh:
ASSEMBLE_IEEE754_UNOP(asinh);
break;
case kIeee754Float64Atan:
ASSEMBLE_IEEE754_UNOP(atan);
break;
case kIeee754Float64Atanh:
ASSEMBLE_IEEE754_UNOP(atanh);
break;
case kIeee754Float64Atan2:
ASSEMBLE_IEEE754_BINOP(atan2);
break;
case kIeee754Float64Cos:
ASSEMBLE_IEEE754_UNOP(cos);
break;
case kIeee754Float64Cosh:
ASSEMBLE_IEEE754_UNOP(cosh);
break;
case kIeee754Float64Cbrt:
ASSEMBLE_IEEE754_UNOP(cbrt);
break;
case kIeee754Float64Exp:
ASSEMBLE_IEEE754_UNOP(exp);
break;
case kIeee754Float64Expm1:
ASSEMBLE_IEEE754_UNOP(expm1);
break;
case kIeee754Float64Log:
ASSEMBLE_IEEE754_UNOP(log);
break;
case kIeee754Float64Log1p:
ASSEMBLE_IEEE754_UNOP(log1p);
break;
case kIeee754Float64Log2:
ASSEMBLE_IEEE754_UNOP(log2);
break;
case kIeee754Float64Log10:
ASSEMBLE_IEEE754_UNOP(log10);
break;
case kIeee754Float64Pow: {
__ CallStubDelayed(new (zone())
MathPowStub(nullptr, MathPowStub::DOUBLE));
break;
}
case kIeee754Float64Sin:
ASSEMBLE_IEEE754_UNOP(sin);
break;
case kIeee754Float64Sinh:
ASSEMBLE_IEEE754_UNOP(sinh);
break;
case kIeee754Float64Tan:
ASSEMBLE_IEEE754_UNOP(tan);
break;
case kIeee754Float64Tanh:
ASSEMBLE_IEEE754_UNOP(tanh);
break;
case kMips64Add:
__ Addu(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
break;
case kMips64Dadd:
__ Daddu(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
break;
case kMips64DaddOvf:
// Pseudo-instruction used for overflow/branch. No opcode emitted here.
break;
case kMips64Sub:
__ Subu(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
break;
case kMips64Dsub:
__ Dsubu(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
break;
case kMips64DsubOvf:
// Pseudo-instruction used for overflow/branch. No opcode emitted here.
break;
case kMips64Mul:
__ Mul(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
break;
case kMips64MulOvf:
// Pseudo-instruction used for overflow/branch. No opcode emitted here.
break;
case kMips64MulHigh:
__ Mulh(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
break;
case kMips64MulHighU:
__ Mulhu(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
break;
case kMips64DMulHigh:
__ Dmulh(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
break;
case kMips64Div:
__ Div(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
if (kArchVariant == kMips64r6) {
__ selnez(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1));
} else {
__ Movz(i.OutputRegister(), i.InputRegister(1), i.InputRegister(1));
}
break;
case kMips64DivU:
__ Divu(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
if (kArchVariant == kMips64r6) {
__ selnez(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1));
} else {
__ Movz(i.OutputRegister(), i.InputRegister(1), i.InputRegister(1));
}
break;
case kMips64Mod:
__ Mod(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
break;
case kMips64ModU:
__ Modu(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
break;
case kMips64Dmul:
__ Dmul(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
break;
case kMips64Ddiv:
__ Ddiv(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
if (kArchVariant == kMips64r6) {
__ selnez(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1));
} else {
__ Movz(i.OutputRegister(), i.InputRegister(1), i.InputRegister(1));
}
break;
case kMips64DdivU:
__ Ddivu(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
if (kArchVariant == kMips64r6) {
__ selnez(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1));
} else {
__ Movz(i.OutputRegister(), i.InputRegister(1), i.InputRegister(1));
}
break;
case kMips64Dmod:
__ Dmod(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
break;
case kMips64DmodU:
__ Dmodu(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
break;
case kMips64Dlsa:
DCHECK(instr->InputAt(2)->IsImmediate());
__ Dlsa(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1),
i.InputInt8(2));
break;
case kMips64Lsa:
DCHECK(instr->InputAt(2)->IsImmediate());
__ Lsa(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1),
i.InputInt8(2));
break;
case kMips64And:
__ And(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
break;
case kMips64And32:
if (instr->InputAt(1)->IsRegister()) {
__ sll(i.InputRegister(0), i.InputRegister(0), 0x0);
__ sll(i.InputRegister(1), i.InputRegister(1), 0x0);
__ And(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
} else {
__ sll(i.InputRegister(0), i.InputRegister(0), 0x0);
__ And(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
}
break;
case kMips64Or:
__ Or(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
break;
case kMips64Or32:
if (instr->InputAt(1)->IsRegister()) {
__ sll(i.InputRegister(0), i.InputRegister(0), 0x0);
__ sll(i.InputRegister(1), i.InputRegister(1), 0x0);
__ Or(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
} else {
__ sll(i.InputRegister(0), i.InputRegister(0), 0x0);
__ Or(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
}
break;
case kMips64Nor:
if (instr->InputAt(1)->IsRegister()) {
__ Nor(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
} else {
DCHECK_EQ(0, i.InputOperand(1).immediate());
__ Nor(i.OutputRegister(), i.InputRegister(0), zero_reg);
}
break;
case kMips64Nor32:
if (instr->InputAt(1)->IsRegister()) {
__ sll(i.InputRegister(0), i.InputRegister(0), 0x0);
__ sll(i.InputRegister(1), i.InputRegister(1), 0x0);
__ Nor(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
} else {
DCHECK_EQ(0, i.InputOperand(1).immediate());
__ sll(i.InputRegister(0), i.InputRegister(0), 0x0);
__ Nor(i.OutputRegister(), i.InputRegister(0), zero_reg);
}
break;
case kMips64Xor:
__ Xor(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
break;
case kMips64Xor32:
if (instr->InputAt(1)->IsRegister()) {
__ sll(i.InputRegister(0), i.InputRegister(0), 0x0);
__ sll(i.InputRegister(1), i.InputRegister(1), 0x0);
__ Xor(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
} else {
__ sll(i.InputRegister(0), i.InputRegister(0), 0x0);
__ Xor(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
}
break;
case kMips64Clz:
__ Clz(i.OutputRegister(), i.InputRegister(0));
break;
case kMips64Dclz:
__ dclz(i.OutputRegister(), i.InputRegister(0));
break;
case kMips64Ctz: {
Register src = i.InputRegister(0);
Register dst = i.OutputRegister();
if (kArchVariant == kMips64r6) {
// We don't have an instruction to count the number of trailing zeroes.
// Start by flipping the bits end-for-end so we can count the number of
// leading zeroes instead.
__ rotr(dst, src, 16);
__ wsbh(dst, dst);
__ bitswap(dst, dst);
__ Clz(dst, dst);
} else {
// Convert trailing zeroes to trailing ones, and bits to their left
// to zeroes.
__ Daddu(kScratchReg, src, -1);
__ Xor(dst, kScratchReg, src);
__ And(dst, dst, kScratchReg);
// Count number of leading zeroes.
__ Clz(dst, dst);
// Subtract number of leading zeroes from 32 to get number of trailing
// ones. Remember that the trailing ones were formerly trailing zeroes.
__ li(kScratchReg, 32);
__ Subu(dst, kScratchReg, dst);
}
} break;
case kMips64Dctz: {
Register src = i.InputRegister(0);
Register dst = i.OutputRegister();
if (kArchVariant == kMips64r6) {
// We don't have an instruction to count the number of trailing zeroes.
// Start by flipping the bits end-for-end so we can count the number of
// leading zeroes instead.
__ dsbh(dst, src);
__ dshd(dst, dst);
__ dbitswap(dst, dst);
__ dclz(dst, dst);
} else {
// Convert trailing zeroes to trailing ones, and bits to their left
// to zeroes.
__ Daddu(kScratchReg, src, -1);
__ Xor(dst, kScratchReg, src);
__ And(dst, dst, kScratchReg);
// Count number of leading zeroes.
__ dclz(dst, dst);
// Subtract number of leading zeroes from 64 to get number of trailing
// ones. Remember that the trailing ones were formerly trailing zeroes.
__ li(kScratchReg, 64);
__ Dsubu(dst, kScratchReg, dst);
}
} break;
case kMips64Popcnt: {
// https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel
//
// A generalization of the best bit counting method to integers of
// bit-widths up to 128 (parameterized by type T) is this:
//
// v = v - ((v >> 1) & (T)~(T)0/3); // temp
// v = (v & (T)~(T)0/15*3) + ((v >> 2) & (T)~(T)0/15*3); // temp
// v = (v + (v >> 4)) & (T)~(T)0/255*15; // temp
// c = (T)(v * ((T)~(T)0/255)) >> (sizeof(T) - 1) * BITS_PER_BYTE; //count
//
// For comparison, for 32-bit quantities, this algorithm can be executed
// using 20 MIPS instructions (the calls to LoadConst32() generate two
// machine instructions each for the values being used in this algorithm).
// A(n unrolled) loop-based algorithm requires 25 instructions.
//
// For a 64-bit operand this can be performed in 24 instructions compared
// to a(n unrolled) loop based algorithm which requires 38 instructions.
//
// There are algorithms which are faster in the cases where very few
// bits are set but the algorithm here attempts to minimize the total
// number of instructions executed even when a large number of bits
// are set.
Register src = i.InputRegister(0);
Register dst = i.OutputRegister();
uint32_t B0 = 0x55555555; // (T)~(T)0/3
uint32_t B1 = 0x33333333; // (T)~(T)0/15*3
uint32_t B2 = 0x0F0F0F0F; // (T)~(T)0/255*15
uint32_t value = 0x01010101; // (T)~(T)0/255
uint32_t shift = 24; // (sizeof(T) - 1) * BITS_PER_BYTE
__ srl(kScratchReg, src, 1);
__ li(kScratchReg2, B0);
__ And(kScratchReg, kScratchReg, kScratchReg2);
__ Subu(kScratchReg, src, kScratchReg);
__ li(kScratchReg2, B1);
__ And(dst, kScratchReg, kScratchReg2);
__ srl(kScratchReg, kScratchReg, 2);
__ And(kScratchReg, kScratchReg, kScratchReg2);
__ Addu(kScratchReg, dst, kScratchReg);
__ srl(dst, kScratchReg, 4);
__ Addu(dst, dst, kScratchReg);
__ li(kScratchReg2, B2);
__ And(dst, dst, kScratchReg2);
__ li(kScratchReg, value);
__ Mul(dst, dst, kScratchReg);
__ srl(dst, dst, shift);
} break;
case kMips64Dpopcnt: {
Register src = i.InputRegister(0);
Register dst = i.OutputRegister();
uint64_t B0 = 0x5555555555555555l; // (T)~(T)0/3
uint64_t B1 = 0x3333333333333333l; // (T)~(T)0/15*3
uint64_t B2 = 0x0F0F0F0F0F0F0F0Fl; // (T)~(T)0/255*15
uint64_t value = 0x0101010101010101l; // (T)~(T)0/255
uint64_t shift = 24; // (sizeof(T) - 1) * BITS_PER_BYTE
__ dsrl(kScratchReg, src, 1);
__ li(kScratchReg2, B0);
__ And(kScratchReg, kScratchReg, kScratchReg2);
__ Dsubu(kScratchReg, src, kScratchReg);
__ li(kScratchReg2, B1);
__ And(dst, kScratchReg, kScratchReg2);
__ dsrl(kScratchReg, kScratchReg, 2);
__ And(kScratchReg, kScratchReg, kScratchReg2);
__ Daddu(kScratchReg, dst, kScratchReg);
__ dsrl(dst, kScratchReg, 4);
__ Daddu(dst, dst, kScratchReg);
__ li(kScratchReg2, B2);
__ And(dst, dst, kScratchReg2);
__ li(kScratchReg, value);
__ Dmul(dst, dst, kScratchReg);
__ dsrl32(dst, dst, shift);
} break;
case kMips64Shl:
if (instr->InputAt(1)->IsRegister()) {
__ sllv(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1));
} else {
int64_t imm = i.InputOperand(1).immediate();
__ sll(i.OutputRegister(), i.InputRegister(0),
static_cast<uint16_t>(imm));
}
break;
case kMips64Shr:
if (instr->InputAt(1)->IsRegister()) {
__ sll(i.InputRegister(0), i.InputRegister(0), 0x0);
__ srlv(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1));
} else {
int64_t imm = i.InputOperand(1).immediate();
__ sll(i.InputRegister(0), i.InputRegister(0), 0x0);
__ srl(i.OutputRegister(), i.InputRegister(0),
static_cast<uint16_t>(imm));
}
break;
case kMips64Sar:
if (instr->InputAt(1)->IsRegister()) {
__ sll(i.InputRegister(0), i.InputRegister(0), 0x0);
__ srav(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1));
} else {
int64_t imm = i.InputOperand(1).immediate();
__ sll(i.InputRegister(0), i.InputRegister(0), 0x0);
__ sra(i.OutputRegister(), i.InputRegister(0),
static_cast<uint16_t>(imm));
}
break;
case kMips64Ext:
__ Ext(i.OutputRegister(), i.InputRegister(0), i.InputInt8(1),
i.InputInt8(2));
break;
case kMips64Ins:
if (instr->InputAt(1)->IsImmediate() && i.InputInt8(1) == 0) {
__ Ins(i.OutputRegister(), zero_reg, i.InputInt8(1), i.InputInt8(2));
} else {
__ Ins(i.OutputRegister(), i.InputRegister(0), i.InputInt8(1),
i.InputInt8(2));
}
break;
case kMips64Dext: {
__ Dext(i.OutputRegister(), i.InputRegister(0), i.InputInt8(1),
i.InputInt8(2));
break;
}
case kMips64Dins:
if (instr->InputAt(1)->IsImmediate() && i.InputInt8(1) == 0) {
__ Dins(i.OutputRegister(), zero_reg, i.InputInt8(1), i.InputInt8(2));
} else {
__ Dins(i.OutputRegister(), i.InputRegister(0), i.InputInt8(1),
i.InputInt8(2));
}
break;
case kMips64Dshl:
if (instr->InputAt(1)->IsRegister()) {
__ dsllv(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1));
} else {
int64_t imm = i.InputOperand(1).immediate();
if (imm < 32) {
__ dsll(i.OutputRegister(), i.InputRegister(0),
static_cast<uint16_t>(imm));
} else {
__ dsll32(i.OutputRegister(), i.InputRegister(0),
static_cast<uint16_t>(imm - 32));
}
}
break;
case kMips64Dshr:
if (instr->InputAt(1)->IsRegister()) {
__ dsrlv(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1));
} else {
int64_t imm = i.InputOperand(1).immediate();
if (imm < 32) {
__ dsrl(i.OutputRegister(), i.InputRegister(0),
static_cast<uint16_t>(imm));
} else {
__ dsrl32(i.OutputRegister(), i.InputRegister(0),
static_cast<uint16_t>(imm - 32));
}
}
break;
case kMips64Dsar:
if (instr->InputAt(1)->IsRegister()) {
__ dsrav(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1));
} else {
int64_t imm = i.InputOperand(1).immediate();
if (imm < 32) {
__ dsra(i.OutputRegister(), i.InputRegister(0), imm);
} else {
__ dsra32(i.OutputRegister(), i.InputRegister(0), imm - 32);
}
}
break;
case kMips64Ror:
__ Ror(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
break;
case kMips64Dror:
__ Dror(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
break;
case kMips64Tst:
// Pseudo-instruction used for cmp/branch. No opcode emitted here.
break;
case kMips64Cmp:
// Pseudo-instruction used for cmp/branch. No opcode emitted here.
break;
case kMips64Mov:
// TODO(plind): Should we combine mov/li like this, or use separate instr?
// - Also see x64 ASSEMBLE_BINOP & RegisterOrOperandType
if (HasRegisterInput(instr, 0)) {
__ mov(i.OutputRegister(), i.InputRegister(0));
} else {
__ li(i.OutputRegister(), i.InputOperand(0));
}
break;
case kMips64CmpS:
// Pseudo-instruction used for FP cmp/branch. No opcode emitted here.
break;
case kMips64AddS:
// TODO(plind): add special case: combine mult & add.
__ add_s(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
i.InputDoubleRegister(1));
break;
case kMips64SubS:
__ sub_s(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
i.InputDoubleRegister(1));
break;
case kMips64MulS:
// TODO(plind): add special case: right op is -1.0, see arm port.
__ mul_s(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
i.InputDoubleRegister(1));
break;
case kMips64DivS:
__ div_s(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
i.InputDoubleRegister(1));
break;
case kMips64ModS: {
// TODO(bmeurer): We should really get rid of this special instruction,
// and generate a CallAddress instruction instead.
FrameScope scope(tasm(), StackFrame::MANUAL);
__ PrepareCallCFunction(0, 2, kScratchReg);
__ MovToFloatParameters(i.InputDoubleRegister(0),
i.InputDoubleRegister(1));
// TODO(balazs.kilvady): implement mod_two_floats_operation(isolate())
__ CallCFunction(
ExternalReference::mod_two_doubles_operation(tasm()->isolate()), 0,
2);
// Move the result in the double result register.
__ MovFromFloatResult(i.OutputSingleRegister());
break;
}
case kMips64AbsS:
__ abs_s(i.OutputSingleRegister(), i.InputSingleRegister(0));
break;
case kMips64NegS:
__ Neg_s(i.OutputSingleRegister(), i.InputSingleRegister(0));
break;
case kMips64SqrtS: {
__ sqrt_s(i.OutputDoubleRegister(), i.InputDoubleRegister(0));
break;
}
case kMips64MaxS:
__ max_s(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
i.InputDoubleRegister(1));
break;
case kMips64MinS:
__ min_s(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
i.InputDoubleRegister(1));
break;
case kMips64CmpD:
// Pseudo-instruction used for FP cmp/branch. No opcode emitted here.
break;
case kMips64AddD:
// TODO(plind): add special case: combine mult & add.
__ add_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
i.InputDoubleRegister(1));
break;
case kMips64SubD:
__ sub_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
i.InputDoubleRegister(1));
break;
case kMips64MulD:
// TODO(plind): add special case: right op is -1.0, see arm port.
__ mul_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
i.InputDoubleRegister(1));
break;
case kMips64DivD:
__ div_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
i.InputDoubleRegister(1));
break;
case kMips64ModD: {
// TODO(bmeurer): We should really get rid of this special instruction,
// and generate a CallAddress instruction instead.
FrameScope scope(tasm(), StackFrame::MANUAL);
__ PrepareCallCFunction(0, 2, kScratchReg);
__ MovToFloatParameters(i.InputDoubleRegister(0),
i.InputDoubleRegister(1));
__ CallCFunction(
ExternalReference::mod_two_doubles_operation(tasm()->isolate()), 0,
2);
// Move the result in the double result register.
__ MovFromFloatResult(i.OutputDoubleRegister());
break;
}
case kMips64AbsD:
__ abs_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0));
break;
case kMips64NegD:
__ Neg_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0));
break;
case kMips64SqrtD: {
__ sqrt_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0));
break;
}
case kMips64MaxD:
__ max_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
i.InputDoubleRegister(1));
break;
case kMips64MinD:
__ min_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
i.InputDoubleRegister(1));
break;
case kMips64Float64RoundDown: {
ASSEMBLE_ROUND_DOUBLE_TO_DOUBLE(floor);
break;
}
case kMips64Float32RoundDown: {
ASSEMBLE_ROUND_FLOAT_TO_FLOAT(floor);
break;
}
case kMips64Float64RoundTruncate: {
ASSEMBLE_ROUND_DOUBLE_TO_DOUBLE(trunc);
break;
}
case kMips64Float32RoundTruncate: {
ASSEMBLE_ROUND_FLOAT_TO_FLOAT(trunc);
break;
}
case kMips64Float64RoundUp: {
ASSEMBLE_ROUND_DOUBLE_TO_DOUBLE(ceil);
break;
}
case kMips64Float32RoundUp: {
ASSEMBLE_ROUND_FLOAT_TO_FLOAT(ceil);
break;
}
case kMips64Float64RoundTiesEven: {
ASSEMBLE_ROUND_DOUBLE_TO_DOUBLE(round);
break;
}
case kMips64Float32RoundTiesEven: {
ASSEMBLE_ROUND_FLOAT_TO_FLOAT(round);
break;
}
case kMips64Float32Max: {
FPURegister dst = i.OutputSingleRegister();
FPURegister src1 = i.InputSingleRegister(0);
FPURegister src2 = i.InputSingleRegister(1);
auto ool = new (zone()) OutOfLineFloat32Max(this, dst, src1, src2);
__ Float32Max(dst, src1, src2, ool->entry());
__ bind(ool->exit());
break;
}
case kMips64Float64Max: {
FPURegister dst = i.OutputDoubleRegister();
FPURegister src1 = i.InputDoubleRegister(0);
FPURegister src2 = i.InputDoubleRegister(1);
auto ool = new (zone()) OutOfLineFloat64Max(this, dst, src1, src2);
__ Float64Max(dst, src1, src2, ool->entry());
__ bind(ool->exit());
break;
}
case kMips64Float32Min: {
FPURegister dst = i.OutputSingleRegister();
FPURegister src1 = i.InputSingleRegister(0);
FPURegister src2 = i.InputSingleRegister(1);
auto ool = new (zone()) OutOfLineFloat32Min(this, dst, src1, src2);
__ Float32Min(dst, src1, src2, ool->entry());
__ bind(ool->exit());
break;
}
case kMips64Float64Min: {
FPURegister dst = i.OutputDoubleRegister();
FPURegister src1 = i.InputDoubleRegister(0);
FPURegister src2 = i.InputDoubleRegister(1);
auto ool = new (zone()) OutOfLineFloat64Min(this, dst, src1, src2);
__ Float64Min(dst, src1, src2, ool->entry());
__ bind(ool->exit());
break;
}
case kMips64Float64SilenceNaN:
__ FPUCanonicalizeNaN(i.OutputDoubleRegister(), i.InputDoubleRegister(0));
break;
case kMips64CvtSD:
__ cvt_s_d(i.OutputSingleRegister(), i.InputDoubleRegister(0));
break;
case kMips64CvtDS:
__ cvt_d_s(i.OutputDoubleRegister(), i.InputSingleRegister(0));
break;
case kMips64CvtDW: {
FPURegister scratch = kScratchDoubleReg;
__ mtc1(i.InputRegister(0), scratch);
__ cvt_d_w(i.OutputDoubleRegister(), scratch);
break;
}
case kMips64CvtSW: {
FPURegister scratch = kScratchDoubleReg;
__ mtc1(i.InputRegister(0), scratch);
__ cvt_s_w(i.OutputDoubleRegister(), scratch);
break;
}
case kMips64CvtSUw: {
__ Cvt_s_uw(i.OutputDoubleRegister(), i.InputRegister(0));
break;
}
case kMips64CvtSL: {
FPURegister scratch = kScratchDoubleReg;
__ dmtc1(i.InputRegister(0), scratch);
__ cvt_s_l(i.OutputDoubleRegister(), scratch);
break;
}
case kMips64CvtDL: {
FPURegister scratch = kScratchDoubleReg;
__ dmtc1(i.InputRegister(0), scratch);
__ cvt_d_l(i.OutputDoubleRegister(), scratch);
break;
}
case kMips64CvtDUw: {
__ Cvt_d_uw(i.OutputDoubleRegister(), i.InputRegister(0));
break;
}
case kMips64CvtDUl: {
__ Cvt_d_ul(i.OutputDoubleRegister(), i.InputRegister(0));
break;
}
case kMips64CvtSUl: {
__ Cvt_s_ul(i.OutputDoubleRegister(), i.InputRegister(0));
break;
}
case kMips64FloorWD: {
FPURegister scratch = kScratchDoubleReg;
__ floor_w_d(scratch, i.InputDoubleRegister(0));
__ mfc1(i.OutputRegister(), scratch);
break;
}
case kMips64CeilWD: {
FPURegister scratch = kScratchDoubleReg;
__ ceil_w_d(scratch, i.InputDoubleRegister(0));
__ mfc1(i.OutputRegister(), scratch);
break;
}
case kMips64RoundWD: {
FPURegister scratch = kScratchDoubleReg;
__ round_w_d(scratch, i.InputDoubleRegister(0));
__ mfc1(i.OutputRegister(), scratch);
break;
}
case kMips64TruncWD: {
FPURegister scratch = kScratchDoubleReg;
// Other arches use round to zero here, so we follow.
__ trunc_w_d(scratch, i.InputDoubleRegister(0));
__ mfc1(i.OutputRegister(), scratch);
break;
}
case kMips64FloorWS: {
FPURegister scratch = kScratchDoubleReg;
__ floor_w_s(scratch, i.InputDoubleRegister(0));
__ mfc1(i.OutputRegister(), scratch);
break;
}
case kMips64CeilWS: {
FPURegister scratch = kScratchDoubleReg;
__ ceil_w_s(scratch, i.InputDoubleRegister(0));
__ mfc1(i.OutputRegister(), scratch);
break;
}
case kMips64RoundWS: {
FPURegister scratch = kScratchDoubleReg;
__ round_w_s(scratch, i.InputDoubleRegister(0));
__ mfc1(i.OutputRegister(), scratch);
break;
}
case kMips64TruncWS: {
FPURegister scratch = kScratchDoubleReg;
__ trunc_w_s(scratch, i.InputDoubleRegister(0));
__ mfc1(i.OutputRegister(), scratch);
// Avoid INT32_MAX as an overflow indicator and use INT32_MIN instead,
// because INT32_MIN allows easier out-of-bounds detection.
__ addiu(kScratchReg, i.OutputRegister(), 1);
__ slt(kScratchReg2, kScratchReg, i.OutputRegister());
__ Movn(i.OutputRegister(), kScratchReg, kScratchReg2);
break;
}
case kMips64TruncLS: {
FPURegister scratch = kScratchDoubleReg;
Register tmp_fcsr = kScratchReg;
Register result = kScratchReg2;
bool load_status = instr->OutputCount() > 1;
if (load_status) {
// Save FCSR.
__ cfc1(tmp_fcsr, FCSR);
// Clear FPU flags.
__ ctc1(zero_reg, FCSR);
}
// Other arches use round to zero here, so we follow.
__ trunc_l_s(scratch, i.InputDoubleRegister(0));
__ dmfc1(i.OutputRegister(), scratch);
if (load_status) {
__ cfc1(result, FCSR);
// Check for overflow and NaNs.
__ andi(result, result,
(kFCSROverflowFlagMask | kFCSRInvalidOpFlagMask));
__ Slt(result, zero_reg, result);
__ xori(result, result, 1);
__ mov(i.OutputRegister(1), result);
// Restore FCSR
__ ctc1(tmp_fcsr, FCSR);
}
break;
}
case kMips64TruncLD: {
FPURegister scratch = kScratchDoubleReg;
Register tmp_fcsr = kScratchReg;
Register result = kScratchReg2;
bool load_status = instr->OutputCount() > 1;
if (load_status) {
// Save FCSR.
__ cfc1(tmp_fcsr, FCSR);
// Clear FPU flags.
__ ctc1(zero_reg, FCSR);
}
// Other arches use round to zero here, so we follow.
__ trunc_l_d(scratch, i.InputDoubleRegister(0));
__ dmfc1(i.OutputRegister(0), scratch);
if (load_status) {
__ cfc1(result, FCSR);
// Check for overflow and NaNs.
__ andi(result, result,
(kFCSROverflowFlagMask | kFCSRInvalidOpFlagMask));
__ Slt(result, zero_reg, result);
__ xori(result, result, 1);
__ mov(i.OutputRegister(1), result);
// Restore FCSR
__ ctc1(tmp_fcsr, FCSR);
}
break;
}
case kMips64TruncUwD: {
FPURegister scratch = kScratchDoubleReg;
// TODO(plind): Fix wrong param order of Trunc_uw_d() macro-asm function.
__ Trunc_uw_d(i.InputDoubleRegister(0), i.OutputRegister(), scratch);
break;
}
case kMips64TruncUwS: {
FPURegister scratch = kScratchDoubleReg;
// TODO(plind): Fix wrong param order of Trunc_uw_d() macro-asm function.
__ Trunc_uw_s(i.InputDoubleRegister(0), i.OutputRegister(), scratch);
// Avoid UINT32_MAX as an overflow indicator and use 0 instead,
// because 0 allows easier out-of-bounds detection.
__ addiu(kScratchReg, i.OutputRegister(), 1);
__ Movz(i.OutputRegister(), zero_reg, kScratchReg);
break;
}
case kMips64TruncUlS: {
FPURegister scratch = kScratchDoubleReg;
Register result = instr->OutputCount() > 1 ? i.OutputRegister(1) : no_reg;
// TODO(plind): Fix wrong param order of Trunc_ul_s() macro-asm function.
__ Trunc_ul_s(i.InputDoubleRegister(0), i.OutputRegister(), scratch,
result);
break;
}
case kMips64TruncUlD: {
FPURegister scratch = kScratchDoubleReg;
Register result = instr->OutputCount() > 1 ? i.OutputRegister(1) : no_reg;
// TODO(plind): Fix wrong param order of Trunc_ul_d() macro-asm function.
__ Trunc_ul_d(i.InputDoubleRegister(0), i.OutputRegister(0), scratch,
result);
break;
}
case kMips64BitcastDL:
__ dmfc1(i.OutputRegister(), i.InputDoubleRegister(0));
break;
case kMips64BitcastLD:
__ dmtc1(i.InputRegister(0), i.OutputDoubleRegister());
break;
case kMips64Float64ExtractLowWord32:
__ FmoveLow(i.OutputRegister(), i.InputDoubleRegister(0));
break;
case kMips64Float64ExtractHighWord32:
__ FmoveHigh(i.OutputRegister(), i.InputDoubleRegister(0));
break;
case kMips64Float64InsertLowWord32:
__ FmoveLow(i.OutputDoubleRegister(), i.InputRegister(1));
break;
case kMips64Float64InsertHighWord32:
__ FmoveHigh(i.OutputDoubleRegister(), i.InputRegister(1));
break;
// ... more basic instructions ...
case kMips64Seb:
__ seb(i.OutputRegister(), i.InputRegister(0));
break;
case kMips64Seh:
__ seh(i.OutputRegister(), i.InputRegister(0));
break;
case kMips64Lbu:
__ Lbu(i.OutputRegister(), i.MemoryOperand());
break;
case kMips64Lb:
__ Lb(i.OutputRegister(), i.MemoryOperand());
break;
case kMips64Sb:
__ Sb(i.InputOrZeroRegister(2), i.MemoryOperand());
break;
case kMips64Lhu:
__ Lhu(i.OutputRegister(), i.MemoryOperand());
break;
case kMips64Ulhu:
__ Ulhu(i.OutputRegister(), i.MemoryOperand());
break;
case kMips64Lh:
__ Lh(i.OutputRegister(), i.MemoryOperand());
break;
case kMips64Ulh:
__ Ulh(i.OutputRegister(), i.MemoryOperand());
break;
case kMips64Sh:
__ Sh(i.InputOrZeroRegister(2), i.MemoryOperand());
break;
case kMips64Ush:
__ Ush(i.InputOrZeroRegister(2), i.MemoryOperand(), kScratchReg);
break;
case kMips64Lw:
__ Lw(i.OutputRegister(), i.MemoryOperand());
break;
case kMips64Ulw:
__ Ulw(i.OutputRegister(), i.MemoryOperand());
break;
case kMips64Lwu:
__ Lwu(i.OutputRegister(), i.MemoryOperand());
break;
case kMips64Ulwu:
__ Ulwu(i.OutputRegister(), i.MemoryOperand());
break;
case kMips64Ld:
__ Ld(i.OutputRegister(), i.MemoryOperand());
break;
case kMips64Uld:
__ Uld(i.OutputRegister(), i.MemoryOperand());
break;
case kMips64Sw:
__ Sw(i.InputOrZeroRegister(2), i.MemoryOperand());
break;
case kMips64Usw:
__ Usw(i.InputOrZeroRegister(2), i.MemoryOperand());
break;
case kMips64Sd:
__ Sd(i.InputOrZeroRegister(2), i.MemoryOperand());
break;
case kMips64Usd:
__ Usd(i.InputOrZeroRegister(2), i.MemoryOperand());
break;
case kMips64Lwc1: {
__ Lwc1(i.OutputSingleRegister(), i.MemoryOperand());
break;
}
case kMips64Ulwc1: {
__ Ulwc1(i.OutputSingleRegister(), i.MemoryOperand(), kScratchReg);
break;
}
case kMips64Swc1: {
size_t index = 0;
MemOperand operand = i.MemoryOperand(&index);
FPURegister ft = i.InputOrZeroSingleRegister(index);
if (ft == kDoubleRegZero && !__ IsDoubleZeroRegSet()) {
__ Move(kDoubleRegZero, 0.0);
}
__ Swc1(ft, operand);
break;
}
case kMips64Uswc1: {
size_t index = 0;
MemOperand operand = i.MemoryOperand(&index);
FPURegister ft = i.InputOrZeroSingleRegister(index);
if (ft == kDoubleRegZero && !__ IsDoubleZeroRegSet()) {
__ Move(kDoubleRegZero, 0.0);
}
__ Uswc1(ft, operand, kScratchReg);
break;
}
case kMips64Ldc1:
__ Ldc1(i.OutputDoubleRegister(), i.MemoryOperand());
break;
case kMips64Uldc1:
__ Uldc1(i.OutputDoubleRegister(), i.MemoryOperand(), kScratchReg);
break;
case kMips64Sdc1: {
FPURegister ft = i.InputOrZeroDoubleRegister(2);
if (ft == kDoubleRegZero && !__ IsDoubleZeroRegSet()) {
__ Move(kDoubleRegZero, 0.0);
}
__ Sdc1(ft, i.MemoryOperand());
break;
}
case kMips64Usdc1: {
FPURegister ft = i.InputOrZeroDoubleRegister(2);
if (ft == kDoubleRegZero && !__ IsDoubleZeroRegSet()) {
__ Move(kDoubleRegZero, 0.0);
}
__ Usdc1(ft, i.MemoryOperand(), kScratchReg);
break;
}
case kMips64Push:
if (instr->InputAt(0)->IsFPRegister()) {
__ Sdc1(i.InputDoubleRegister(0), MemOperand(sp, -kDoubleSize));
__ Subu(sp, sp, Operand(kDoubleSize));
frame_access_state()->IncreaseSPDelta(kDoubleSize / kPointerSize);
} else {
__ Push(i.InputRegister(0));
frame_access_state()->IncreaseSPDelta(1);
}
break;
case kMips64Peek: {
// The incoming value is 0-based, but we need a 1-based value.
int reverse_slot = MiscField::decode(instr->opcode()) + 1;
int offset =
FrameSlotToFPOffset(frame()->GetTotalFrameSlotCount() - reverse_slot);
if (instr->OutputAt(0)->IsFPRegister()) {
LocationOperand* op = LocationOperand::cast(instr->OutputAt(0));
if (op->representation() == MachineRepresentation::kFloat64) {
__ Ldc1(i.OutputDoubleRegister(), MemOperand(fp, offset));
} else {
DCHECK_EQ(op->representation(), MachineRepresentation::kFloat32);
__ lwc1(i.OutputSingleRegister(0), MemOperand(fp, offset));
}
} else {
__ Ld(i.OutputRegister(0), MemOperand(fp, offset));
}
break;
}
case kMips64StackClaim: {
__ Dsubu(sp, sp, Operand(i.InputInt32(0)));
frame_access_state()->IncreaseSPDelta(i.InputInt32(0) / kPointerSize);
break;
}
case kMips64StoreToStackSlot: {
if (instr->InputAt(0)->IsFPRegister()) {
__ Sdc1(i.InputDoubleRegister(0), MemOperand(sp, i.InputInt32(1)));
} else {
__ Sd(i.InputRegister(0), MemOperand(sp, i.InputInt32(1)));
}
break;
}
case kMips64ByteSwap64: {
__ ByteSwapSigned(i.OutputRegister(0), i.InputRegister(0), 8);
break;
}
case kMips64ByteSwap32: {
__ ByteSwapUnsigned(i.OutputRegister(0), i.InputRegister(0), 4);
__ dsrl32(i.OutputRegister(0), i.OutputRegister(0), 0);
break;
}
case kAtomicLoadInt8:
ASSEMBLE_ATOMIC_LOAD_INTEGER(Lb);
break;
case kAtomicLoadUint8:
ASSEMBLE_ATOMIC_LOAD_INTEGER(Lbu);
break;
case kAtomicLoadInt16:
ASSEMBLE_ATOMIC_LOAD_INTEGER(Lh);
break;
case kAtomicLoadUint16:
ASSEMBLE_ATOMIC_LOAD_INTEGER(Lhu);
break;
case kAtomicLoadWord32:
ASSEMBLE_ATOMIC_LOAD_INTEGER(Lw);
break;
case kAtomicStoreWord8:
ASSEMBLE_ATOMIC_STORE_INTEGER(Sb);
break;
case kAtomicStoreWord16:
ASSEMBLE_ATOMIC_STORE_INTEGER(Sh);
break;
case kAtomicStoreWord32:
ASSEMBLE_ATOMIC_STORE_INTEGER(Sw);
break;
case kAtomicExchangeInt8:
ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT(true, 8);
break;
case kAtomicExchangeUint8:
ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT(false, 8);
break;
case kAtomicExchangeInt16:
ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT(true, 16);
break;
case kAtomicExchangeUint16:
ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT(false, 16);
break;
case kAtomicExchangeWord32:
ASSEMBLE_ATOMIC_EXCHANGE_INTEGER();
break;
case kAtomicCompareExchangeInt8:
ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER_EXT(true, 8);
break;
case kAtomicCompareExchangeUint8:
ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER_EXT(false, 8);
break;
case kAtomicCompareExchangeInt16:
ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER_EXT(true, 16);
break;
case kAtomicCompareExchangeUint16:
ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER_EXT(false, 16);
break;
case kAtomicCompareExchangeWord32:
__ sll(i.InputRegister(2), i.InputRegister(2), 0);
ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER();
break;
#define ATOMIC_BINOP_CASE(op, inst) \
case kAtomic##op##Int8: \
ASSEMBLE_ATOMIC_BINOP_EXT(true, 8, inst); \
break; \
case kAtomic##op##Uint8: \
ASSEMBLE_ATOMIC_BINOP_EXT(false, 8, inst); \
break; \
case kAtomic##op##Int16: \
ASSEMBLE_ATOMIC_BINOP_EXT(true, 16, inst); \
break; \
case kAtomic##op##Uint16: \
ASSEMBLE_ATOMIC_BINOP_EXT(false, 16, inst); \
break; \
case kAtomic##op##Word32: \
ASSEMBLE_ATOMIC_BINOP(inst); \
break;
ATOMIC_BINOP_CASE(Add, Addu)
ATOMIC_BINOP_CASE(Sub, Subu)
ATOMIC_BINOP_CASE(And, And)
ATOMIC_BINOP_CASE(Or, Or)
ATOMIC_BINOP_CASE(Xor, Xor)
#undef ATOMIC_BINOP_CASE
case kMips64AssertEqual:
__ Assert(eq, static_cast<AbortReason>(i.InputOperand(2).immediate()),
i.InputRegister(0), Operand(i.InputRegister(1)));
break;
case kMips64S128Zero: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ xor_v(i.OutputSimd128Register(), i.OutputSimd128Register(),
i.OutputSimd128Register());
break;
}
case kMips64I32x4Splat: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ fill_w(i.OutputSimd128Register(), i.InputRegister(0));
break;
}
case kMips64I32x4ExtractLane: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ copy_s_w(i.OutputRegister(), i.InputSimd128Register(0),
i.InputInt8(1));
break;
}
case kMips64I32x4ReplaceLane: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Simd128Register src = i.InputSimd128Register(0);
Simd128Register dst = i.OutputSimd128Register();
if (src != dst) {
__ move_v(dst, src);
}
__ insert_w(dst, i.InputInt8(1), i.InputRegister(2));
break;
}
case kMips64I32x4Add: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ addv_w(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMips64I32x4Sub: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ subv_w(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMips64F32x4Splat: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ FmoveLow(kScratchReg, i.InputSingleRegister(0));
__ fill_w(i.OutputSimd128Register(), kScratchReg);
break;
}
case kMips64F32x4ExtractLane: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ copy_u_w(kScratchReg, i.InputSimd128Register(0), i.InputInt8(1));
__ FmoveLow(i.OutputSingleRegister(), kScratchReg);
break;
}
case kMips64F32x4ReplaceLane: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Simd128Register src = i.InputSimd128Register(0);
Simd128Register dst = i.OutputSimd128Register();
if (src != dst) {
__ move_v(dst, src);
}
__ FmoveLow(kScratchReg, i.InputSingleRegister(2));
__ insert_w(dst, i.InputInt8(1), kScratchReg);
break;
}
case kMips64F32x4SConvertI32x4: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ ffint_s_w(i.OutputSimd128Register(), i.InputSimd128Register(0));
break;
}
case kMips64F32x4UConvertI32x4: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ ffint_u_w(i.OutputSimd128Register(), i.InputSimd128Register(0));
break;
}
case kMips64I32x4Mul: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ mulv_w(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMips64I32x4MaxS: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ max_s_w(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMips64I32x4MinS: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ min_s_w(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMips64I32x4Eq: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ ceq_w(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMips64I32x4Ne: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Simd128Register dst = i.OutputSimd128Register();
__ ceq_w(dst, i.InputSimd128Register(0), i.InputSimd128Register(1));
__ nor_v(dst, dst, dst);
break;
}
case kMips64I32x4Shl: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ slli_w(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputInt5(1));
break;
}
case kMips64I32x4ShrS: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ srai_w(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputInt5(1));
break;
}
case kMips64I32x4ShrU: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ srli_w(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputInt5(1));
break;
}
case kMips64I32x4MaxU: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ max_u_w(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMips64I32x4MinU: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ min_u_w(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMips64S128Select: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
DCHECK(i.OutputSimd128Register() == i.InputSimd128Register(0));
__ bsel_v(i.OutputSimd128Register(), i.InputSimd128Register(2),
i.InputSimd128Register(1));
break;
}
case kMips64F32x4Abs: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ bclri_w(i.OutputSimd128Register(), i.InputSimd128Register(0), 31);
break;
}
case kMips64F32x4Neg: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ bnegi_w(i.OutputSimd128Register(), i.InputSimd128Register(0), 31);
break;
}
case kMips64F32x4RecipApprox: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ frcp_w(i.OutputSimd128Register(), i.InputSimd128Register(0));
break;
}
case kMips64F32x4RecipSqrtApprox: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ frsqrt_w(i.OutputSimd128Register(), i.InputSimd128Register(0));
break;
}
case kMips64F32x4Add: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ fadd_w(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMips64F32x4Sub: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ fsub_w(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMips64F32x4Mul: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ fmul_w(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMips64F32x4Max: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ fmax_w(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMips64F32x4Min: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ fmin_w(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMips64F32x4Eq: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ fceq_w(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMips64F32x4Ne: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ fcne_w(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMips64F32x4Lt: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ fclt_w(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMips64F32x4Le: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ fcle_w(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMips64I32x4SConvertF32x4: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ ftrunc_s_w(i.OutputSimd128Register(), i.InputSimd128Register(0));
break;
}
case kMips64I32x4UConvertF32x4: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ ftrunc_u_w(i.OutputSimd128Register(), i.InputSimd128Register(0));
break;
}
case kMips64I32x4Neg: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ xor_v(kSimd128RegZero, kSimd128RegZero, kSimd128RegZero);
__ subv_w(i.OutputSimd128Register(), kSimd128RegZero,
i.InputSimd128Register(0));
break;
}
case kMips64I32x4GtS: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ clt_s_w(i.OutputSimd128Register(), i.InputSimd128Register(1),
i.InputSimd128Register(0));
break;
}
case kMips64I32x4GeS: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ cle_s_w(i.OutputSimd128Register(), i.InputSimd128Register(1),
i.InputSimd128Register(0));
break;
}
case kMips64I32x4GtU: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ clt_u_w(i.OutputSimd128Register(), i.InputSimd128Register(1),
i.InputSimd128Register(0));
break;
}
case kMips64I32x4GeU: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ cle_u_w(i.OutputSimd128Register(), i.InputSimd128Register(1),
i.InputSimd128Register(0));
break;
}
case kMips64I16x8Splat: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ fill_h(i.OutputSimd128Register(), i.InputRegister(0));
break;
}
case kMips64I16x8ExtractLane: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ copy_s_h(i.OutputRegister(), i.InputSimd128Register(0),
i.InputInt8(1));
break;
}
case kMips64I16x8ReplaceLane: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Simd128Register src = i.InputSimd128Register(0);
Simd128Register dst = i.OutputSimd128Register();
if (src != dst) {
__ move_v(dst, src);
}
__ insert_h(dst, i.InputInt8(1), i.InputRegister(2));
break;
}
case kMips64I16x8Neg: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ xor_v(kSimd128RegZero, kSimd128RegZero, kSimd128RegZero);
__ subv_h(i.OutputSimd128Register(), kSimd128RegZero,
i.InputSimd128Register(0));
break;
}
case kMips64I16x8Shl: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ slli_h(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputInt4(1));
break;
}
case kMips64I16x8ShrS: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ srai_h(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputInt4(1));
break;
}
case kMips64I16x8ShrU: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ srli_h(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputInt4(1));
break;
}
case kMips64I16x8Add: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ addv_h(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMips64I16x8AddSaturateS: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ adds_s_h(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMips64I16x8Sub: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ subv_h(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMips64I16x8SubSaturateS: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ subs_s_h(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMips64I16x8Mul: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ mulv_h(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMips64I16x8MaxS: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ max_s_h(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMips64I16x8MinS: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ min_s_h(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMips64I16x8Eq: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ ceq_h(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMips64I16x8Ne: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Simd128Register dst = i.OutputSimd128Register();
__ ceq_h(dst, i.InputSimd128Register(0), i.InputSimd128Register(1));
__ nor_v(dst, dst, dst);
break;
}
case kMips64I16x8GtS: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ clt_s_h(i.OutputSimd128Register(), i.InputSimd128Register(1),
i.InputSimd128Register(0));
break;
}
case kMips64I16x8GeS: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ cle_s_h(i.OutputSimd128Register(), i.InputSimd128Register(1),
i.InputSimd128Register(0));
break;
}
case kMips64I16x8AddSaturateU: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ adds_u_h(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMips64I16x8SubSaturateU: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ subs_u_h(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMips64I16x8MaxU: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ max_u_h(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMips64I16x8MinU: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ min_u_h(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMips64I16x8GtU: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ clt_u_h(i.OutputSimd128Register(), i.InputSimd128Register(1),
i.InputSimd128Register(0));
break;
}
case kMips64I16x8GeU: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ cle_u_h(i.OutputSimd128Register(), i.InputSimd128Register(1),
i.InputSimd128Register(0));
break;
}
case kMips64I8x16Splat: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ fill_b(i.OutputSimd128Register(), i.InputRegister(0));
break;
}
case kMips64I8x16ExtractLane: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ copy_s_b(i.OutputRegister(), i.InputSimd128Register(0),
i.InputInt8(1));
break;
}
case kMips64I8x16ReplaceLane: {
CpuFeatureScope msa_scope