blob: 3b57081c9ec14a9dcf7ccbbd0803909b5e02547d [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/mips/macro-assembler-mips.h"
namespace v8 {
namespace internal {
namespace compiler {
#define __ tasm()->
// TODO(plind): Possibly avoid using these lithium names.
#define kScratchReg kLithiumScratchReg
#define kCompareReg kLithiumScratchReg2
#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::kFloat32:
return Operand::EmbeddedNumber(constant.ToFloat32());
case Constant::kFloat64:
return Operand::EmbeddedNumber(constant.ToFloat64().value());
case Constant::kInt64:
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.
__ srl(at, kScratchReg, 31);
__ sll(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());
__ Addu(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, DoubleRegister);
CREATE_OOL_CLASS(OutOfLineFloat64Min, Float64MinOutOfLine, DoubleRegister);
#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();
}
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 (IsMipsArchVariant(kMips32r6)) { \
__ 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)); \
__ Move(at, kScratchReg2, i.OutputDoubleRegister()); \
__ or_(at, at, kScratchReg2); \
__ 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 (IsMipsArchVariant(kMips32r6)) { \
__ 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; \
__ Addu(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; \
__ Addu(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1)); \
__ andi(i.TempRegister(3), i.TempRegister(0), 0x3); \
__ Subu(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); \
__ Addu(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; \
__ Addu(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1)); \
__ andi(i.TempRegister(1), i.TempRegister(0), 0x3); \
__ Subu(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; \
__ Addu(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; \
__ Addu(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1)); \
__ andi(i.TempRegister(1), i.TempRegister(0), 0x3); \
__ Subu(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(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(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()) {
__ lw(ra, MemOperand(fp, StandardFrameConstants::kCallerPCOffset));
__ lw(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.
__ lw(scratch1, MemOperand(fp, StandardFrameConstants::kContextOffset));
__ Branch(&done, ne, scratch1,
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;
__ lw(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->Subu(sp, sp, stack_slot_delta * kPointerSize);
state->IncreaseSPDelta(stack_slot_delta);
} else if (allow_shrinkage && stack_slot_delta < 0) {
tasm->Addu(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);
__ lw(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 {
__ Call(at, i.InputRegister(0), Code::kHeaderSize - kHeapObjectTag);
}
RecordCallPosition(instr);
frame_access_state()->ClearSPDelta();
break;
}
case kArchCallWasmFunction: {
if (instr->InputAt(0)->IsImmediate()) {
Address wasm_code = reinterpret_cast<Address>(
i.ToConstant(instr->InputAt(0)).ToInt32());
__ Call(wasm_code, info()->IsWasm() ? RelocInfo::WASM_CALL
: RelocInfo::JS_TO_WASM_CALL);
} else {
__ Call(i.InputRegister(0));
}
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 {
__ Jump(at, i.InputRegister(0), Code::kHeaderSize - kHeapObjectTag);
}
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)).ToInt32());
__ Jump(wasm_code, info()->IsWasm() ? RelocInfo::WASM_CALL
: RelocInfo::JS_TO_WASM_CALL);
} else {
__ Jump(i.InputRegister(0));
}
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.
__ lw(kScratchReg, FieldMemOperand(func, JSFunction::kContextOffset));
__ Assert(eq, AbortReason::kWrongFunctionContext, cp,
Operand(kScratchReg));
}
__ lw(at, FieldMemOperand(func, JSFunction::kCodeOffset));
__ Call(at, Code::kHeaderSize - kHeapObjectTag);
RecordCallPosition(instr);
frame_access_state()->ClearSPDelta();
frame_access_state()->SetFrameAccessToDefault();
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()) {
__ lw(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);
__ Addu(at, object, index);
__ sw(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;
__ Addu(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;
__ Addu(kScratchReg, base_reg, Operand(offset.offset()));
__ And(kScratchReg, kScratchReg, Operand(alignment - 1));
__ BranchShort(&done, eq, kScratchReg, Operand(zero_reg));
__ Addu(i.OutputRegister(), i.OutputRegister(), kPointerSize);
__ bind(&done);
} else if (alignment > 2 * kPointerSize) {
Label done;
__ Addu(kScratchReg, base_reg, Operand(offset.offset()));
__ And(kScratchReg, kScratchReg, Operand(alignment - 1));
__ BranchShort(&done, eq, kScratchReg, Operand(zero_reg));
__ li(kScratchReg2, alignment);
__ Subu(kScratchReg2, kScratchReg2, Operand(kScratchReg));
__ Addu(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 kIeee754Float64Log10:
ASSEMBLE_IEEE754_UNOP(log10);
break;
case kIeee754Float64Log2:
ASSEMBLE_IEEE754_UNOP(log2);
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 kMipsAdd:
__ Addu(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
break;
case kMipsAddOvf:
// Pseudo-instruction used for overflow/branch. No opcode emitted here.
break;
case kMipsSub:
__ Subu(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
break;
case kMipsSubOvf:
// Pseudo-instruction used for overflow/branch. No opcode emitted here.
break;
case kMipsMul:
__ Mul(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
break;
case kMipsMulOvf:
// Pseudo-instruction used for overflow/branch. No opcode emitted here.
break;
case kMipsMulHigh:
__ Mulh(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
break;
case kMipsMulHighU:
__ Mulhu(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
break;
case kMipsDiv:
__ Div(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
if (IsMipsArchVariant(kMips32r6)) {
__ selnez(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1));
} else {
__ Movz(i.OutputRegister(), i.InputRegister(1), i.InputRegister(1));
}
break;
case kMipsDivU:
__ Divu(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
if (IsMipsArchVariant(kMips32r6)) {
__ selnez(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1));
} else {
__ Movz(i.OutputRegister(), i.InputRegister(1), i.InputRegister(1));
}
break;
case kMipsMod:
__ Mod(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
break;
case kMipsModU:
__ Modu(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
break;
case kMipsAnd:
__ And(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
break;
case kMipsOr:
__ Or(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
break;
case kMipsNor:
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 kMipsXor:
__ Xor(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
break;
case kMipsClz:
__ Clz(i.OutputRegister(), i.InputRegister(0));
break;
case kMipsCtz: {
Register src = i.InputRegister(0);
Register dst = i.OutputRegister();
if (IsMipsArchVariant(kMips32r6)) {
// 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.
__ Ror(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.
__ Addu(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 kMipsPopcnt: {
// 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 64-bit quantities, this algorithm gets executed twice, (once
// for in_lo, and again for in_hi), but saves a few instructions
// because the mask values only have to be loaded once. Using this
// algorithm the count for a 64-bit operand can be performed in 29
// instructions compared to a loop-based algorithm which requires 47
// instructions.
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 kMipsShl:
if (instr->InputAt(1)->IsRegister()) {
__ sllv(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1));
} else {
int32_t imm = i.InputOperand(1).immediate();
__ sll(i.OutputRegister(), i.InputRegister(0), imm);
}
break;
case kMipsShr:
if (instr->InputAt(1)->IsRegister()) {
__ srlv(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1));
} else {
int32_t imm = i.InputOperand(1).immediate();
__ srl(i.OutputRegister(), i.InputRegister(0), imm);
}
break;
case kMipsSar:
if (instr->InputAt(1)->IsRegister()) {
__ srav(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1));
} else {
int32_t imm = i.InputOperand(1).immediate();
__ sra(i.OutputRegister(), i.InputRegister(0), imm);
}
break;
case kMipsShlPair: {
Register second_output =
instr->OutputCount() >= 2 ? i.OutputRegister(1) : i.TempRegister(0);
if (instr->InputAt(2)->IsRegister()) {
__ ShlPair(i.OutputRegister(0), second_output, i.InputRegister(0),
i.InputRegister(1), i.InputRegister(2));
} else {
uint32_t imm = i.InputOperand(2).immediate();
__ ShlPair(i.OutputRegister(0), second_output, i.InputRegister(0),
i.InputRegister(1), imm);
}
} break;
case kMipsShrPair: {
Register second_output =
instr->OutputCount() >= 2 ? i.OutputRegister(1) : i.TempRegister(0);
if (instr->InputAt(2)->IsRegister()) {
__ ShrPair(i.OutputRegister(0), second_output, i.InputRegister(0),
i.InputRegister(1), i.InputRegister(2));
} else {
uint32_t imm = i.InputOperand(2).immediate();
__ ShrPair(i.OutputRegister(0), second_output, i.InputRegister(0),
i.InputRegister(1), imm);
}
} break;
case kMipsSarPair: {
Register second_output =
instr->OutputCount() >= 2 ? i.OutputRegister(1) : i.TempRegister(0);
if (instr->InputAt(2)->IsRegister()) {
__ SarPair(i.OutputRegister(0), second_output, i.InputRegister(0),
i.InputRegister(1), i.InputRegister(2));
} else {
uint32_t imm = i.InputOperand(2).immediate();
__ SarPair(i.OutputRegister(0), second_output, i.InputRegister(0),
i.InputRegister(1), imm);
}
} break;
case kMipsExt:
__ Ext(i.OutputRegister(), i.InputRegister(0), i.InputInt8(1),
i.InputInt8(2));
break;
case kMipsIns:
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 kMipsRor:
__ Ror(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
break;
case kMipsTst:
// Pseudo-instruction used for tst/branch. No opcode emitted here.
break;
case kMipsCmp:
// Pseudo-instruction used for cmp/branch. No opcode emitted here.
break;
case kMipsMov:
// 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 kMipsLsa:
DCHECK(instr->InputAt(2)->IsImmediate());
__ Lsa(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1),
i.InputInt8(2));
break;
case kMipsCmpS:
// Pseudo-instruction used for FP cmp/branch. No opcode emitted here.
break;
case kMipsAddS:
// TODO(plind): add special case: combine mult & add.
__ add_s(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
i.InputDoubleRegister(1));
break;
case kMipsSubS:
__ sub_s(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
i.InputDoubleRegister(1));
break;
case kMipsMulS:
// 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 kMipsDivS:
__ div_s(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
i.InputDoubleRegister(1));
break;
case kMipsModS: {
// 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(isolate()),
0, 2);
// Move the result in the double result register.
__ MovFromFloatResult(i.OutputSingleRegister());
break;
}
case kMipsAbsS:
__ abs_s(i.OutputSingleRegister(), i.InputSingleRegister(0));
break;
case kMipsSqrtS: {
__ sqrt_s(i.OutputDoubleRegister(), i.InputDoubleRegister(0));
break;
}
case kMipsMaxS:
__ max_s(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
i.InputDoubleRegister(1));
break;
case kMipsMinS:
__ min_s(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
i.InputDoubleRegister(1));
break;
case kMipsCmpD:
// Pseudo-instruction used for FP cmp/branch. No opcode emitted here.
break;
case kMipsAddPair:
__ AddPair(i.OutputRegister(0), i.OutputRegister(1), i.InputRegister(0),
i.InputRegister(1), i.InputRegister(2), i.InputRegister(3));
break;
case kMipsSubPair:
__ SubPair(i.OutputRegister(0), i.OutputRegister(1), i.InputRegister(0),
i.InputRegister(1), i.InputRegister(2), i.InputRegister(3));
break;
case kMipsMulPair: {
__ Mulu(i.OutputRegister(1), i.OutputRegister(0), i.InputRegister(0),
i.InputRegister(2));
__ Mul(kScratchReg, i.InputRegister(0), i.InputRegister(3));
__ Mul(kScratchReg2, i.InputRegister(1), i.InputRegister(2));
__ Addu(i.OutputRegister(1), i.OutputRegister(1), kScratchReg);
__ Addu(i.OutputRegister(1), i.OutputRegister(1), kScratchReg2);
} break;
case kMipsAddD:
// TODO(plind): add special case: combine mult & add.
__ add_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
i.InputDoubleRegister(1));
break;
case kMipsSubD:
__ sub_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
i.InputDoubleRegister(1));
break;
case kMipsMaddS:
__ Madd_s(i.OutputFloatRegister(), i.InputFloatRegister(0),
i.InputFloatRegister(1), i.InputFloatRegister(2),
kScratchDoubleReg);
break;
case kMipsMaddD:
__ Madd_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
i.InputDoubleRegister(1), i.InputDoubleRegister(2),
kScratchDoubleReg);
break;
case kMipsMsubS:
__ Msub_s(i.OutputFloatRegister(), i.InputFloatRegister(0),
i.InputFloatRegister(1), i.InputFloatRegister(2),
kScratchDoubleReg);
break;
case kMipsMsubD:
__ Msub_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
i.InputDoubleRegister(1), i.InputDoubleRegister(2),
kScratchDoubleReg);
break;
case kMipsMulD:
// 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 kMipsDivD:
__ div_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
i.InputDoubleRegister(1));
break;
case kMipsModD: {
// 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(isolate()),
0, 2);
// Move the result in the double result register.
__ MovFromFloatResult(i.OutputDoubleRegister());
break;
}
case kMipsAbsD:
__ abs_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0));
break;
case kMipsNegS:
__ Neg_s(i.OutputSingleRegister(), i.InputSingleRegister(0));
break;
case kMipsNegD:
__ Neg_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0));
break;
case kMipsSqrtD: {
__ sqrt_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0));
break;
}
case kMipsMaxD:
__ max_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
i.InputDoubleRegister(1));
break;
case kMipsMinD:
__ min_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
i.InputDoubleRegister(1));
break;
case kMipsFloat64RoundDown: {
ASSEMBLE_ROUND_DOUBLE_TO_DOUBLE(floor);
break;
}
case kMipsFloat32RoundDown: {
ASSEMBLE_ROUND_FLOAT_TO_FLOAT(floor);
break;
}
case kMipsFloat64RoundTruncate: {
ASSEMBLE_ROUND_DOUBLE_TO_DOUBLE(trunc);
break;
}
case kMipsFloat32RoundTruncate: {
ASSEMBLE_ROUND_FLOAT_TO_FLOAT(trunc);
break;
}
case kMipsFloat64RoundUp: {
ASSEMBLE_ROUND_DOUBLE_TO_DOUBLE(ceil);
break;
}
case kMipsFloat32RoundUp: {
ASSEMBLE_ROUND_FLOAT_TO_FLOAT(ceil);
break;
}
case kMipsFloat64RoundTiesEven: {
ASSEMBLE_ROUND_DOUBLE_TO_DOUBLE(round);
break;
}
case kMipsFloat32RoundTiesEven: {
ASSEMBLE_ROUND_FLOAT_TO_FLOAT(round);
break;
}
case kMipsFloat32Max: {
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 kMipsFloat64Max: {
DoubleRegister dst = i.OutputDoubleRegister();
DoubleRegister src1 = i.InputDoubleRegister(0);
DoubleRegister src2 = i.InputDoubleRegister(1);
auto ool = new (zone()) OutOfLineFloat64Max(this, dst, src1, src2);
__ Float64Max(dst, src1, src2, ool->entry());
__ bind(ool->exit());
break;
}
case kMipsFloat32Min: {
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 kMipsFloat64Min: {
DoubleRegister dst = i.OutputDoubleRegister();
DoubleRegister src1 = i.InputDoubleRegister(0);
DoubleRegister src2 = i.InputDoubleRegister(1);
auto ool = new (zone()) OutOfLineFloat64Min(this, dst, src1, src2);
__ Float64Min(dst, src1, src2, ool->entry());
__ bind(ool->exit());
break;
}
case kMipsCvtSD: {
__ cvt_s_d(i.OutputSingleRegister(), i.InputDoubleRegister(0));
break;
}
case kMipsCvtDS: {
__ cvt_d_s(i.OutputDoubleRegister(), i.InputSingleRegister(0));
break;
}
case kMipsCvtDW: {
FPURegister scratch = kScratchDoubleReg;
__ mtc1(i.InputRegister(0), scratch);
__ cvt_d_w(i.OutputDoubleRegister(), scratch);
break;
}
case kMipsCvtSW: {
FPURegister scratch = kScratchDoubleReg;
__ mtc1(i.InputRegister(0), scratch);
__ cvt_s_w(i.OutputDoubleRegister(), scratch);
break;
}
case kMipsCvtSUw: {
FPURegister scratch = kScratchDoubleReg;
__ Cvt_d_uw(i.OutputDoubleRegister(), i.InputRegister(0), scratch);
__ cvt_s_d(i.OutputDoubleRegister(), i.OutputDoubleRegister());
break;
}
case kMipsCvtDUw: {
FPURegister scratch = kScratchDoubleReg;
__ Cvt_d_uw(i.OutputDoubleRegister(), i.InputRegister(0), scratch);
break;
}
case kMipsFloorWD: {
FPURegister scratch = kScratchDoubleReg;
__ Floor_w_d(scratch, i.InputDoubleRegister(0));
__ mfc1(i.OutputRegister(), scratch);
break;
}
case kMipsCeilWD: {
FPURegister scratch = kScratchDoubleReg;
__ Ceil_w_d(scratch, i.InputDoubleRegister(0));
__ mfc1(i.OutputRegister(), scratch);
break;
}
case kMipsRoundWD: {
FPURegister scratch = kScratchDoubleReg;
__ Round_w_d(scratch, i.InputDoubleRegister(0));
__ mfc1(i.OutputRegister(), scratch);
break;
}
case kMipsTruncWD: {
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 kMipsFloorWS: {
FPURegister scratch = kScratchDoubleReg;
__ floor_w_s(scratch, i.InputDoubleRegister(0));
__ mfc1(i.OutputRegister(), scratch);
break;
}
case kMipsCeilWS: {
FPURegister scratch = kScratchDoubleReg;
__ ceil_w_s(scratch, i.InputDoubleRegister(0));
__ mfc1(i.OutputRegister(), scratch);
break;
}
case kMipsRoundWS: {
FPURegister scratch = kScratchDoubleReg;
__ round_w_s(scratch, i.InputDoubleRegister(0));
__ mfc1(i.OutputRegister(), scratch);
break;
}
case kMipsTruncWS: {
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.
__ Addu(kScratchReg, i.OutputRegister(), 1);
__ Slt(kScratchReg2, kScratchReg, i.OutputRegister());
__ Movn(i.OutputRegister(), kScratchReg, kScratchReg2);
break;
}
case kMipsTruncUwD: {
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 kMipsTruncUwS: {
FPURegister scratch = kScratchDoubleReg;
// TODO(plind): Fix wrong param order of Trunc_uw_s() 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.
__ Addu(kScratchReg, i.OutputRegister(), 1);
__ Movz(i.OutputRegister(), zero_reg, kScratchReg);
break;
}
case kMipsFloat64ExtractLowWord32:
__ FmoveLow(i.OutputRegister(), i.InputDoubleRegister(0));
break;
case kMipsFloat64ExtractHighWord32:
__ FmoveHigh(i.OutputRegister(), i.InputDoubleRegister(0));
break;
case kMipsFloat64InsertLowWord32:
__ FmoveLow(i.OutputDoubleRegister(), i.InputRegister(1));
break;
case kMipsFloat64InsertHighWord32:
__ FmoveHigh(i.OutputDoubleRegister(), i.InputRegister(1));
break;
case kMipsFloat64SilenceNaN:
__ FPUCanonicalizeNaN(i.OutputDoubleRegister(), i.InputDoubleRegister(0));
break;
// ... more basic instructions ...
case kMipsSeb:
__ Seb(i.OutputRegister(), i.InputRegister(0));
break;
case kMipsSeh:
__ Seh(i.OutputRegister(), i.InputRegister(0));
break;
case kMipsLbu:
__ lbu(i.OutputRegister(), i.MemoryOperand());
break;
case kMipsLb:
__ lb(i.OutputRegister(), i.MemoryOperand());
break;
case kMipsSb:
__ sb(i.InputOrZeroRegister(2), i.MemoryOperand());
break;
case kMipsLhu:
__ lhu(i.OutputRegister(), i.MemoryOperand());
break;
case kMipsUlhu:
__ Ulhu(i.OutputRegister(), i.MemoryOperand());
break;
case kMipsLh:
__ lh(i.OutputRegister(), i.MemoryOperand());
break;
case kMipsUlh:
__ Ulh(i.OutputRegister(), i.MemoryOperand());
break;
case kMipsSh:
__ sh(i.InputOrZeroRegister(2), i.MemoryOperand());
break;
case kMipsUsh:
__ Ush(i.InputOrZeroRegister(2), i.MemoryOperand(), kScratchReg);
break;
case kMipsLw:
__ lw(i.OutputRegister(), i.MemoryOperand());
break;
case kMipsUlw:
__ Ulw(i.OutputRegister(), i.MemoryOperand());
break;
case kMipsSw:
__ sw(i.InputOrZeroRegister(2), i.MemoryOperand());
break;
case kMipsUsw:
__ Usw(i.InputOrZeroRegister(2), i.MemoryOperand());
break;
case kMipsLwc1: {
__ lwc1(i.OutputSingleRegister(), i.MemoryOperand());
break;
}
case kMipsUlwc1: {
__ Ulwc1(i.OutputSingleRegister(), i.MemoryOperand(), kScratchReg);
break;
}
case kMipsSwc1: {
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 kMipsUswc1: {
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 kMipsLdc1:
__ Ldc1(i.OutputDoubleRegister(), i.MemoryOperand());
break;
case kMipsUldc1:
__ Uldc1(i.OutputDoubleRegister(), i.MemoryOperand(), kScratchReg);
break;
case kMipsSdc1: {
FPURegister ft = i.InputOrZeroDoubleRegister(2);
if (ft == kDoubleRegZero && !__ IsDoubleZeroRegSet()) {
__ Move(kDoubleRegZero, 0.0);
}
__ Sdc1(ft, i.MemoryOperand());
break;
}
case kMipsUsdc1: {
FPURegister ft = i.InputOrZeroDoubleRegister(2);
if (ft == kDoubleRegZero && !__ IsDoubleZeroRegSet()) {
__ Move(kDoubleRegZero, 0.0);
}
__ Usdc1(ft, i.MemoryOperand(), kScratchReg);
break;
}
case kMipsPush:
if (instr->InputAt(0)->IsFPRegister()) {
LocationOperand* op = LocationOperand::cast(instr->InputAt(0));
switch (op->representation()) {
case MachineRepresentation::kFloat32:
__ swc1(i.InputFloatRegister(0), MemOperand(sp, -kFloatSize));
__ Subu(sp, sp, Operand(kFloatSize));
frame_access_state()->IncreaseSPDelta(kFloatSize / kPointerSize);
break;
case MachineRepresentation::kFloat64:
__ Sdc1(i.InputDoubleRegister(0), MemOperand(sp, -kDoubleSize));
__ Subu(sp, sp, Operand(kDoubleSize));
frame_access_state()->IncreaseSPDelta(kDoubleSize / kPointerSize);
break;
default: {
UNREACHABLE();
break;
}
}
} else {
__ Push(i.InputRegister(0));
frame_access_state()->IncreaseSPDelta(1);
}
break;
case kMipsPeek: {
int reverse_slot = MiscField::decode(instr->opcode());
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 {
__ lw(i.OutputRegister(0), MemOperand(fp, offset));
}
break;
}
case kMipsStackClaim: {
__ Subu(sp, sp, Operand(i.InputInt32(0)));
frame_access_state()->IncreaseSPDelta(i.InputInt32(0) / kPointerSize);
break;
}
case kMipsStoreToStackSlot: {
if (instr->InputAt(0)->IsFPRegister()) {
LocationOperand* op = LocationOperand::cast(instr->InputAt(0));
if (op->representation() == MachineRepresentation::kFloat64) {
__ Sdc1(i.InputDoubleRegister(0), MemOperand(sp, i.InputInt32(1)));
} else {
DCHECK_EQ(MachineRepresentation::kFloat32, op->representation());
__ swc1(i.InputSingleRegister(0), MemOperand(sp, i.InputInt32(1)));
}
} else {
__ sw(i.InputRegister(0), MemOperand(sp, i.InputInt32(1)));
}
break;
}
case kMipsByteSwap32: {
__ ByteSwapSigned(i.OutputRegister(0), i.InputRegister(0), 4);
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:
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 kMipsS128Zero: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ xor_v(i.OutputSimd128Register(), i.OutputSimd128Register(),
i.OutputSimd128Register());
break;
}
case kMipsI32x4Splat: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ fill_w(i.OutputSimd128Register(), i.InputRegister(0));
break;
}
case kMipsI32x4ExtractLane: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ copy_s_w(i.OutputRegister(), i.InputSimd128Register(0),
i.InputInt8(1));
break;
}
case kMipsI32x4ReplaceLane: {
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 kMipsI32x4Add: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ addv_w(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMipsI32x4Sub: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ subv_w(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMipsF32x4Splat: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ FmoveLow(kScratchReg, i.InputSingleRegister(0));
__ fill_w(i.OutputSimd128Register(), kScratchReg);
break;
}
case kMipsF32x4ExtractLane: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ copy_u_w(kScratchReg, i.InputSimd128Register(0), i.InputInt8(1));
__ FmoveLow(i.OutputSingleRegister(), kScratchReg);
break;
}
case kMipsF32x4ReplaceLane: {
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 kMipsF32x4SConvertI32x4: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ ffint_s_w(i.OutputSimd128Register(), i.InputSimd128Register(0));
break;
}
case kMipsF32x4UConvertI32x4: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ ffint_u_w(i.OutputSimd128Register(), i.InputSimd128Register(0));
break;
}
case kMipsI32x4Mul: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ mulv_w(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMipsI32x4MaxS: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ max_s_w(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMipsI32x4MinS: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ min_s_w(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMipsI32x4Eq: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ ceq_w(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMipsI32x4Ne: {
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 kMipsI32x4Shl: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ slli_w(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputInt5(1));
break;
}
case kMipsI32x4ShrS: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ srai_w(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputInt5(1));
break;
}
case kMipsI32x4ShrU: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ srli_w(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputInt5(1));
break;
}
case kMipsI32x4MaxU: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ max_u_w(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMipsI32x4MinU: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ min_u_w(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMipsS128Select: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
DCHECK(i.OutputSimd128Register() == i.InputSimd128Register(0));
__ bsel_v(i.OutputSimd128Register(), i.InputSimd128Register(2),
i.InputSimd128Register(1));
break;
}
case kMipsF32x4Abs: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ bclri_w(i.OutputSimd128Register(), i.InputSimd128Register(0), 31);
break;
}
case kMipsF32x4Neg: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ bnegi_w(i.OutputSimd128Register(), i.InputSimd128Register(0), 31);
break;
}
case kMipsF32x4RecipApprox: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ frcp_w(i.OutputSimd128Register(), i.InputSimd128Register(0));
break;
}
case kMipsF32x4RecipSqrtApprox: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ frsqrt_w(i.OutputSimd128Register(), i.InputSimd128Register(0));
break;
}
case kMipsF32x4Add: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ fadd_w(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMipsF32x4Sub: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ fsub_w(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMipsF32x4Mul: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ fmul_w(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMipsF32x4Max: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ fmax_w(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMipsF32x4Min: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ fmin_w(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMipsF32x4Eq: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ fceq_w(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMipsF32x4Ne: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ fcne_w(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMipsF32x4Lt: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ fclt_w(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMipsF32x4Le: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ fcle_w(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMipsI32x4SConvertF32x4: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ ftrunc_s_w(i.OutputSimd128Register(), i.InputSimd128Register(0));
break;
}
case kMipsI32x4UConvertF32x4: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ ftrunc_u_w(i.OutputSimd128Register(), i.InputSimd128Register(0));
break;
}
case kMipsI32x4Neg: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ xor_v(kSimd128RegZero, kSimd128RegZero, kSimd128RegZero);
__ subv_w(i.OutputSimd128Register(), kSimd128RegZero,
i.InputSimd128Register(0));
break;
}
case kMipsI32x4GtS: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ clt_s_w(i.OutputSimd128Register(), i.InputSimd128Register(1),
i.InputSimd128Register(0));
break;
}
case kMipsI32x4GeS: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ cle_s_w(i.OutputSimd128Register(), i.InputSimd128Register(1),
i.InputSimd128Register(0));
break;
}
case kMipsI32x4GtU: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ clt_u_w(i.OutputSimd128Register(), i.InputSimd128Register(1),
i.InputSimd128Register(0));
break;
}
case kMipsI32x4GeU: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ cle_u_w(i.OutputSimd128Register(), i.InputSimd128Register(1),
i.InputSimd128Register(0));
break;
}
case kMipsI16x8Splat: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ fill_h(i.OutputSimd128Register(), i.InputRegister(0));
break;
}
case kMipsI16x8ExtractLane: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ copy_s_h(i.OutputRegister(), i.InputSimd128Register(0),
i.InputInt8(1));
break;
}
case kMipsI16x8ReplaceLane: {
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 kMipsI16x8Neg: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ xor_v(kSimd128RegZero, kSimd128RegZero, kSimd128RegZero);
__ subv_h(i.OutputSimd128Register(), kSimd128RegZero,
i.InputSimd128Register(0));
break;
}
case kMipsI16x8Shl: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ slli_h(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputInt4(1));
break;
}
case kMipsI16x8ShrS: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ srai_h(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputInt4(1));
break;
}
case kMipsI16x8ShrU: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ srli_h(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputInt4(1));
break;
}
case kMipsI16x8Add: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ addv_h(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMipsI16x8AddSaturateS: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ adds_s_h(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMipsI16x8Sub: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ subv_h(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMipsI16x8SubSaturateS: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ subs_s_h(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMipsI16x8Mul: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ mulv_h(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMipsI16x8MaxS: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ max_s_h(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMipsI16x8MinS: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ min_s_h(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMipsI16x8Eq: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ ceq_h(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMipsI16x8Ne: {
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 kMipsI16x8GtS: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ clt_s_h(i.OutputSimd128Register(), i.InputSimd128Register(1),
i.InputSimd128Register(0));
break;
}
case kMipsI16x8GeS: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ cle_s_h(i.OutputSimd128Register(), i.InputSimd128Register(1),
i.InputSimd128Register(0));
break;
}
case kMipsI16x8AddSaturateU: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ adds_u_h(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMipsI16x8SubSaturateU: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ subs_u_h(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMipsI16x8MaxU: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ max_u_h(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMipsI16x8MinU: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ min_u_h(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMipsI16x8GtU: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ clt_u_h(i.OutputSimd128Register(), i.InputSimd128Register(1),
i.InputSimd128Register(0));
break;
}
case kMipsI16x8GeU: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ cle_u_h(i.OutputSimd128Register(), i.InputSimd128Register(1),
i.InputSimd128Register(0));
break;
}
case kMipsI8x16Splat: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ fill_b(i.OutputSimd128Register(), i.InputRegister(0));
break;
}
case kMipsI8x16ExtractLane: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ copy_s_b(i.OutputRegister(), i.InputSimd128Register(0),
i.InputInt8(1));
break;
}
case kMipsI8x16ReplaceLane: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Simd128Register src = i.InputSimd128Register(0);
Simd128Register dst = i.OutputSimd128Register();
if (src != dst) {
__ move_v(dst, src);
}
__ insert_b(dst, i.InputInt8(1), i.InputRegister(2));
break;
}
case kMipsI8x16Neg: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ xor_v(kSimd128RegZero, kSimd128RegZero, kSimd128RegZero);
__ subv_b(i.OutputSimd128Register(), kSimd128RegZero,
i.InputSimd128Register(0));
break;
}
case kMipsI8x16Shl: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ slli_b(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputInt3(1));
break;
}
case kMipsI8x16ShrS: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ srai_b(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputInt3(1));
break;
}
case kMipsI8x16Add: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ addv_b(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMipsI8x16AddSaturateS: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ adds_s_b(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMipsI8x16Sub: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ subv_b(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMipsI8x16SubSaturateS: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ subs_s_b(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMipsI8x16Mul: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ mulv_b(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMipsI8x16MaxS: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ max_s_b(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMipsI8x16MinS: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ min_s_b(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMipsI8x16Eq: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ ceq_b(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMipsI8x16Ne: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Simd128Register dst = i.OutputSimd128Register();
__ ceq_b(dst, i.InputSimd128Register(0), i.InputSimd128Register(1));
__ nor_v(dst, dst, dst);
break;
}
case kMipsI8x16GtS: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ clt_s_b(i.OutputSimd128Register(), i.InputSimd128Register(1),
i.InputSimd128Register(0));
break;
}
case kMipsI8x16GeS: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ cle_s_b(i.OutputSimd128Register(), i.InputSimd128Register(1),
i.InputSimd128Register(0));
break;
}
case kMipsI8x16ShrU: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ srli_b(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputInt3(1));
break;
}
case kMipsI8x16AddSaturateU: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ adds_u_b(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMipsI8x16SubSaturateU: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ subs_u_b(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMipsI8x16MaxU: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ max_u_b(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMipsI8x16MinU: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ min_u_b(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMipsI8x16GtU: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ clt_u_b(i.OutputSimd128Register(), i.InputSimd128Register(1),
i.InputSimd128Register(0));
break;
}
case kMipsI8x16GeU: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ cle_u_b(i.OutputSimd128Register(), i.InputSimd128Register(1),
i.InputSimd128Register(0));
break;
}
case kMipsS128And: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ and_v(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMipsS128Or: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ or_v(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMipsS128Xor: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ xor_v(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMipsS128Not: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ nor_v(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(0));
break;
}
case kMipsS1x4AnyTrue:
case kMipsS1x8AnyTrue:
case kMipsS1x16AnyTrue: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Register dst = i.OutputRegister();
Label all_false;
__ BranchMSA(&all_false, MSA_BRANCH_V, all_zero,
i.InputSimd128Register(0), USE_DELAY_SLOT);
__ li(dst, 0); // branch delay slot
__ li(dst, -1);
__ bind(&all_false);
break;
}
case kMipsS1x4AllTrue: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Register dst = i.OutputRegister();
Label all_true;
__ BranchMSA(&all_true, MSA_BRANCH_W, all_not_zero,
i.InputSimd128Register(0), USE_DELAY_SLOT);
__ li(dst, -1); // branch delay slot
__ li(dst, 0);
__ bind(&all_true);
break;
}
case kMipsS1x8AllTrue: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Register dst = i.OutputRegister();
Label all_true;
__ BranchMSA(&all_true, MSA_BRANCH_H, all_not_zero,
i.InputSimd128Register(0), USE_DELAY_SLOT);
__ li(dst, -1); // branch delay slot
__ li(dst, 0);
__ bind(&all_true);
break;
}
case kMipsS1x16AllTrue: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Register dst = i.OutputRegister();
Label all_true;
__ BranchMSA(&all_true, MSA_BRANCH_B, all_not_zero,
i.InputSimd128Register(0), USE_DELAY_SLOT);
__ li(dst, -1); // branch delay slot
__ li(dst, 0);
__ bind(&all_true);
break;
}
case kMipsMsaLd: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ ld_b(i.OutputSimd128Register(), i.MemoryOperand());
break;
}
case kMipsMsaSt: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ st_b(i.InputSimd128Register(2), i.MemoryOperand());
break;
}
case kMipsS32x4InterleaveRight: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Simd128Register dst = i.OutputSimd128Register(),
src0 = i.InputSimd128Register(0),
src1 = i.InputSimd128Register(1);
// src1 = [7, 6, 5, 4], src0 = [3, 2, 1, 0]
// dst = [5, 1, 4, 0]
__ ilvr_w(dst, src1, src0);
break;
}
case kMipsS32x4InterleaveLeft: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Simd128Register dst = i.OutputSimd128Register(),
src0 = i.InputSimd128Register(0),
src1 = i.InputSimd128Register(1);
// src1 = [7, 6, 5, 4], src0 = [3, 2, 1, 0]
// dst = [7, 3, 6, 2]
__ ilvl_w(dst, src1, src0);
break;
}
case kMipsS32x4PackEven: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Simd128Register dst = i.OutputSimd128Register(),
src0 = i.InputSimd128Register(0),
src1 = i.InputSimd128Register(1);
// src1 = [7, 6, 5, 4], src0 = [3, 2, 1, 0]
// dst = [6, 4, 2, 0]
__ pckev_w(dst, src1, src0);
break;
}
case kMipsS32x4PackOdd: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Simd128Register dst = i.OutputSimd128Register(),
src0 = i.InputSimd128Register(0),
src1 = i.InputSimd128Register(1);
// src1 = [7, 6, 5, 4], src0 = [3, 2, 1, 0]
// dst = [7, 5, 3, 1]
__ pckod_w(dst, src1, src0);
break;
}
case kMipsS32x4InterleaveEven: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Simd128Register dst = i.OutputSimd128Register(),
src0 = i.InputSimd128Register(0),
src1 = i.InputSimd128Register(1);
// src1 = [7, 6, 5, 4], src0 = [3, 2, 1, 0]
// dst = [6, 2, 4, 0]
__ ilvev_w(dst, src1, src0);
break;
}
case kMipsS32x4InterleaveOdd: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Simd128Register<