blob: 887b7e574033c8cc59938d6e3ab50a271dbdef60 [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/codegen/assembler-inl.h"
#include "src/codegen/callable.h"
#include "src/codegen/macro-assembler.h"
#include "src/codegen/mips64/constants-mips64.h"
#include "src/codegen/optimized-compilation-info.h"
#include "src/compiler/backend/code-generator-impl.h"
#include "src/compiler/backend/code-generator.h"
#include "src/compiler/backend/gap-resolver.h"
#include "src/compiler/node-matchers.h"
#include "src/compiler/osr.h"
#include "src/heap/memory-chunk.h"
#include "src/wasm/wasm-code-manager.h"
namespace v8 {
namespace internal {
namespace compiler {
#define __ tasm()->
// 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::kCompressedHeapObject:
case Constant::kHeapObject:
// TODO(plind): Maybe we should handle ExtRef & HeapObj here?
// maybe not done on arm due to const pool ??
break;
case Constant::kDelayedStringConstant:
return Operand::EmbeddedStringConstant(
constant.ToDelayedStringConstant());
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 OutOfLineRecordWrite final : public OutOfLineCode {
public:
OutOfLineRecordWrite(CodeGenerator* gen, Register object, Register index,
Register value, Register scratch0, Register scratch1,
RecordWriteMode mode, StubCallMode stub_mode)
: OutOfLineCode(gen),
object_(object),
index_(index),
value_(value),
scratch0_(scratch0),
scratch1_(scratch1),
mode_(mode),
stub_mode_(stub_mode),
must_save_lr_(!gen->frame_access_state()->has_frame()),
zone_(gen->zone()) {}
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);
}
if (mode_ == RecordWriteMode::kValueIsEphemeronKey) {
__ CallEphemeronKeyBarrier(object_, scratch1_, save_fp_mode);
} else if (stub_mode_ == StubCallMode::kCallWasmRuntimeStub) {
// A direct call to a wasm runtime stub defined in this module.
// Just encode the stub index. This will be patched when the code
// is added to the native module and copied into wasm code space.
__ CallRecordWriteStub(object_, scratch1_, remembered_set_action,
save_fp_mode, wasm::WasmCode::kRecordWrite);
} else {
__ 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_;
StubCallMode const stub_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 OLT;
case kUnsignedLessThanOrEqual:
*predicate = true;
return OLE;
case kUnsignedGreaterThan:
*predicate = false;
return OLE;
case kUnorderedEqual:
case kUnorderedNotEqual:
*predicate = true;
break;
default:
*predicate = true;
break;
}
UNREACHABLE();
}
void EmitWordLoadPoisoningIfNeeded(CodeGenerator* codegen,
InstructionCode opcode, Instruction* instr,
MipsOperandConverter const& i) {
const MemoryAccessMode access_mode =
static_cast<MemoryAccessMode>(MiscField::decode(opcode));
if (access_mode == kMemoryAccessPoisoned) {
Register value = i.OutputRegister();
codegen->tasm()->And(value, value, kSpeculationPoisonRegister);
}
}
} // namespace
#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(load_linked, store_conditional, bin_instr) \
do { \
Label binop; \
__ Daddu(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1)); \
__ sync(); \
__ bind(&binop); \
__ load_linked(i.OutputRegister(0), MemOperand(i.TempRegister(0), 0)); \
__ bin_instr(i.TempRegister(1), i.OutputRegister(0), \
Operand(i.InputRegister(2))); \
__ store_conditional(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(load_linked, store_conditional, sign_extend, \
size, bin_instr, representation) \
do { \
Label binop; \
__ daddu(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1)); \
if (representation == 32) { \
__ andi(i.TempRegister(3), i.TempRegister(0), 0x3); \
} else { \
DCHECK_EQ(representation, 64); \
__ andi(i.TempRegister(3), i.TempRegister(0), 0x7); \
} \
__ Dsubu(i.TempRegister(0), i.TempRegister(0), \
Operand(i.TempRegister(3))); \
__ sll(i.TempRegister(3), i.TempRegister(3), 3); \
__ sync(); \
__ bind(&binop); \
__ load_linked(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); \
__ store_conditional(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(load_linked, store_conditional) \
do { \
Label exchange; \
__ sync(); \
__ bind(&exchange); \
__ daddu(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1)); \
__ load_linked(i.OutputRegister(0), MemOperand(i.TempRegister(0), 0)); \
__ mov(i.TempRegister(1), i.InputRegister(2)); \
__ store_conditional(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( \
load_linked, store_conditional, sign_extend, size, representation) \
do { \
Label exchange; \
__ daddu(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1)); \
if (representation == 32) { \
__ andi(i.TempRegister(1), i.TempRegister(0), 0x3); \
} else { \
DCHECK_EQ(representation, 64); \
__ andi(i.TempRegister(1), i.TempRegister(0), 0x7); \
} \
__ Dsubu(i.TempRegister(0), i.TempRegister(0), \
Operand(i.TempRegister(1))); \
__ sll(i.TempRegister(1), i.TempRegister(1), 3); \
__ sync(); \
__ bind(&exchange); \
__ load_linked(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); \
__ store_conditional(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(load_linked, \
store_conditional) \
do { \
Label compareExchange; \
Label exit; \
__ daddu(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1)); \
__ sync(); \
__ bind(&compareExchange); \
__ load_linked(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)); \
__ store_conditional(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( \
load_linked, store_conditional, sign_extend, size, representation) \
do { \
Label compareExchange; \
Label exit; \
__ daddu(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1)); \
if (representation == 32) { \
__ andi(i.TempRegister(1), i.TempRegister(0), 0x3); \
} else { \
DCHECK_EQ(representation, 64); \
__ andi(i.TempRegister(1), i.TempRegister(0), 0x7); \
} \
__ Dsubu(i.TempRegister(0), i.TempRegister(0), \
Operand(i.TempRegister(1))); \
__ sll(i.TempRegister(1), i.TempRegister(1), 3); \
__ sync(); \
__ bind(&compareExchange); \
__ load_linked(i.TempRegister(2), MemOperand(i.TempRegister(0), 0)); \
__ ExtractBits(i.OutputRegister(0), i.TempRegister(2), i.TempRegister(1), \
size, sign_extend); \
__ ExtractBits(i.InputRegister(2), i.InputRegister(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); \
__ store_conditional(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(), 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(), 0, 1); \
/* Move the result in the double result register. */ \
__ MovFromFloatResult(i.OutputDoubleRegister()); \
} while (0)
#define ASSEMBLE_F64X2_ARITHMETIC_BINOP(op) \
do { \
__ op(i.OutputSimd128Register(), i.InputSimd128Register(0), \
i.InputSimd128Register(1)); \
} 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);
__ PrepareForTailCall(args_reg, 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 * kSystemPointerSize);
state->IncreaseSPDelta(stack_slot_delta);
} else if (allow_shrinkage && stack_slot_delta < 0) {
tasm->Daddu(sp, sp, -stack_slot_delta * kSystemPointerSize);
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 that {kJavaScriptCallCodeStartRegister} is correct.
void CodeGenerator::AssembleCodeStartRegisterCheck() {
__ ComputeCodeStartAddress(kScratchReg);
__ Assert(eq, AbortReason::kWrongFunctionCodeStart,
kJavaScriptCallCodeStartRegister, Operand(kScratchReg));
}
// 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. read from memory the word that contains that bit, which can be found in
// the flags in the referenced {CodeDataContainer} object;
// 2. test kMarkedForDeoptimizationBit in those flags; and
// 3. if it is not zero then it jumps to the builtin.
void CodeGenerator::BailoutIfDeoptimized() {
int offset = Code::kCodeDataContainerOffset - Code::kHeaderSize;
__ Ld(kScratchReg, MemOperand(kJavaScriptCallCodeStartRegister, offset));
__ Lw(kScratchReg,
FieldMemOperand(kScratchReg,
CodeDataContainer::kKindSpecificFlagsOffset));
__ And(kScratchReg, kScratchReg,
Operand(1 << Code::kMarkedForDeoptimizationBit));
__ Jump(BUILTIN_CODE(isolate(), CompileLazyDeoptimizedCode),
RelocInfo::CODE_TARGET, ne, kScratchReg, Operand(zero_reg));
}
void CodeGenerator::GenerateSpeculationPoisonFromCodeStartRegister() {
// Calculate a mask which has all bits set in the normal case, but has all
// bits cleared if we are speculatively executing the wrong PC.
// difference = (current - expected) | (expected - current)
// poison = ~(difference >> (kBitsPerSystemPointer - 1))
__ ComputeCodeStartAddress(kScratchReg);
__ Move(kSpeculationPoisonRegister, kScratchReg);
__ subu(kSpeculationPoisonRegister, kSpeculationPoisonRegister,
kJavaScriptCallCodeStartRegister);
__ subu(kJavaScriptCallCodeStartRegister, kJavaScriptCallCodeStartRegister,
kScratchReg);
__ or_(kSpeculationPoisonRegister, kSpeculationPoisonRegister,
kJavaScriptCallCodeStartRegister);
__ sra(kSpeculationPoisonRegister, kSpeculationPoisonRegister,
kBitsPerSystemPointer - 1);
__ nor(kSpeculationPoisonRegister, kSpeculationPoisonRegister,
kSpeculationPoisonRegister);
}
void CodeGenerator::AssembleRegisterArgumentPoisoning() {
__ And(kJSFunctionRegister, kJSFunctionRegister, kSpeculationPoisonRegister);
__ And(kContextRegister, kContextRegister, kSpeculationPoisonRegister);
__ And(sp, sp, kSpeculationPoisonRegister);
}
// 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 {
Register reg = i.InputRegister(0);
DCHECK_IMPLIES(
instr->HasCallDescriptorFlag(CallDescriptor::kFixedTargetRegister),
reg == kJavaScriptCallCodeStartRegister);
__ daddiu(reg, reg, Code::kHeaderSize - kHeapObjectTag);
__ Call(reg);
}
RecordCallPosition(instr);
frame_access_state()->ClearSPDelta();
break;
}
case kArchCallBuiltinPointer: {
DCHECK(!instr->InputAt(0)->IsImmediate());
Register builtin_index = i.InputRegister(0);
__ CallBuiltinByIndex(builtin_index);
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()) {
Constant constant = i.ToConstant(instr->InputAt(0));
Address wasm_code = static_cast<Address>(constant.ToInt64());
__ Call(wasm_code, constant.rmode());
} else {
__ daddiu(kScratchReg, i.InputRegister(0), 0);
__ Call(kScratchReg);
}
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 {
Register reg = i.InputRegister(0);
DCHECK_IMPLIES(
instr->HasCallDescriptorFlag(CallDescriptor::kFixedTargetRegister),
reg == kJavaScriptCallCodeStartRegister);
__ daddiu(reg, reg, Code::kHeaderSize - kHeapObjectTag);
__ Jump(reg);
}
frame_access_state()->ClearSPDelta();
frame_access_state()->SetFrameAccessToDefault();
break;
}
case kArchTailCallWasm: {
if (instr->InputAt(0)->IsImmediate()) {
Constant constant = i.ToConstant(instr->InputAt(0));
Address wasm_code = static_cast<Address>(constant.ToInt64());
__ Jump(wasm_code, constant.rmode());
} else {
__ daddiu(kScratchReg, i.InputRegister(0), 0);
__ Jump(kScratchReg);
}
frame_access_state()->ClearSPDelta();
frame_access_state()->SetFrameAccessToDefault();
break;
}
case kArchTailCallAddress: {
CHECK(!instr->InputAt(0)->IsImmediate());
Register reg = i.InputRegister(0);
DCHECK_IMPLIES(
instr->HasCallDescriptorFlag(CallDescriptor::kFixedTargetRegister),
reg == kJavaScriptCallCodeStartRegister);
__ Jump(reg);
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));
}
static_assert(kJavaScriptCallCodeStartRegister == a2, "ABI mismatch");
__ Ld(a2, FieldMemOperand(func, JSFunction::kCodeOffset));
__ Daddu(a2, a2, Operand(Code::kHeaderSize - kHeapObjectTag));
__ Call(a2);
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(IsAligned(bytes, kSystemPointerSize));
DCHECK_EQ(0, frame_access_state()->sp_delta());
frame_access_state()->IncreaseSPDelta(bytes / kSystemPointerSize);
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 / kSystemPointerSize));
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());
Label start_call;
bool isWasmCapiFunction =
linkage()->GetIncomingDescriptor()->IsWasmCapiFunction();
// from start_call to return address.
int offset = __ root_array_available() ? 76 : 88;
#if V8_HOST_ARCH_MIPS64
if (__ emit_debug_code()) {
offset += 16;
}
#endif
if (isWasmCapiFunction) {
// Put the return address in a stack slot.
__ mov(kScratchReg, ra);
__ bind(&start_call);
__ nal();
__ nop();
__ Daddu(ra, ra, offset - 8); // 8 = nop + nal
__ sd(ra, MemOperand(fp, WasmExitFrameConstants::kCallingPCOffset));
__ mov(ra, kScratchReg);
}
if (instr->InputAt(0)->IsImmediate()) {
ExternalReference ref = i.InputExternalReference(0);
__ CallCFunction(ref, num_parameters);
} else {
Register func = i.InputRegister(0);
__ CallCFunction(func, num_parameters);
}
if (isWasmCapiFunction) {
CHECK_EQ(offset, __ SizeOfCodeGeneratedSince(&start_call));
RecordSafepoint(instr->reference_map(), Safepoint::kNoLazyDeopt);
}
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 / kSystemPointerSize);
}
break;
}
case kArchJmp:
AssembleArchJump(i.InputRpo(0));
break;
case kArchBinarySearchSwitch:
AssembleArchBinarySearchSwitch(instr);
break;
break;
case kArchTableSwitch:
AssembleArchTableSwitch(instr);
break;
case kArchAbortCSAAssert:
DCHECK(i.InputRegister(0) == a0);
{
// 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::kAbortCSAAssert),
RelocInfo::CODE_TARGET);
}
__ stop();
break;
case kArchDebugBreak:
__ DebugBreak();
break;
case kArchComment:
__ RecordComment(reinterpret_cast<const char*>(i.InputInt64(0)));
break;
case kArchNop:
case kArchThrowTerminator:
// don't emit code for nops.
break;
case kArchDeoptimize: {
DeoptimizationExit* exit =
BuildTranslation(instr, -1, 0, OutputFrameStateCombine::Ignore());
__ Branch(exit->label());
break;
}
case kArchRet:
AssembleReturn(instr->InputAt(0));
break;
case kArchStackPointerGreaterThan:
// Pseudo-instruction used for cmp/branch. No opcode emitted here.
break;
case kArchStackCheckOffset:
__ Move(i.OutputRegister(), Smi::FromInt(GetStackCheckOffset()));
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:
__ TruncateDoubleToI(isolate(), zone(), i.OutputRegister(),
i.InputDoubleRegister(0), DetermineStubCallMode());
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 = zone()->New<OutOfLineRecordWrite>(this, object, index, value,
scratch0, scratch1, mode,
DetermineStubCallMode());
__ Daddu(kScratchReg, object, index);
__ Sd(value, MemOperand(kScratchReg));
__ 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(kSystemPointerSize - 1));
__ Assert(eq, AbortReason::kAllocationIsNotDoubleAligned, kScratchReg,
Operand(zero_reg));
}
if (alignment == 2 * kSystemPointerSize) {
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(), kSystemPointerSize);
__ bind(&done);
} else if (alignment > 2 * kSystemPointerSize) {
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 kArchWordPoisonOnSpeculation:
__ And(i.OutputRegister(), i.InputRegister(0),
kSpeculationPoisonRegister);
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:
ASSEMBLE_IEEE754_BINOP(pow);
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:
__ DaddOverflow(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1),
kScratchReg);
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:
__ DsubOverflow(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1),
kScratchReg);
break;
case kMips64Mul:
__ Mul(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
break;
case kMips64MulOvf:
__ MulOverflow(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1),
kScratchReg);
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:
__ And(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
__ sll(i.OutputRegister(), i.OutputRegister(), 0x0);
break;
case kMips64Or:
__ Or(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
break;
case kMips64Or32:
__ Or(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
__ sll(i.OutputRegister(), i.OutputRegister(), 0x0);
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()) {
__ Nor(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
__ sll(i.OutputRegister(), i.OutputRegister(), 0x0);
} else {
DCHECK_EQ(0, i.InputOperand(1).immediate());
__ Nor(i.OutputRegister(), i.InputRegister(0), zero_reg);
__ sll(i.OutputRegister(), i.OutputRegister(), 0x0);
}
break;
case kMips64Xor:
__ Xor(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
break;
case kMips64Xor32:
__ Xor(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
__ sll(i.OutputRegister(), i.OutputRegister(), 0x0);
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();
__ Ctz(dst, src);
} break;
case kMips64Dctz: {
Register src = i.InputRegister(0);
Register dst = i.OutputRegister();
__ Dctz(dst, src);
} break;
case kMips64Popcnt: {
Register src = i.InputRegister(0);
Register dst = i.OutputRegister();
__ Popcnt(dst, src);
} break;
case kMips64Dpopcnt: {
Register src = i.InputRegister(0);
Register dst = i.OutputRegister();
__ Dpopcnt(dst, src);
} 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.OutputRegister(), i.InputRegister(0), 0x0);
__ srl(i.OutputRegister(), i.OutputRegister(),
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.OutputRegister(), i.InputRegister(0), 0x0);
__ sra(i.OutputRegister(), i.OutputRegister(),
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:
__ And(kScratchReg, i.InputRegister(0), i.InputOperand(1));
// 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: {
FPURegister left = i.InputOrZeroSingleRegister(0);
FPURegister right = i.InputOrZeroSingleRegister(1);
bool predicate;
FPUCondition cc =
FlagsConditionToConditionCmpFPU(&predicate, instr->flags_condition());
if ((left == kDoubleRegZero || right == kDoubleRegZero) &&
!__ IsDoubleZeroRegSet()) {
__ Move(kDoubleRegZero, 0.0);
}
__ CompareF32(cc, left, right);
} 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(), 0, 2);
// Move the result in the double result register.
__ MovFromFloatResult(i.OutputSingleRegister());
break;
}
case kMips64AbsS:
if (kArchVariant == kMips64r6) {
__ abs_s(i.OutputSingleRegister(), i.InputSingleRegister(0));
} else {
__ mfc1(kScratchReg, i.InputSingleRegister(0));
__ Dins(kScratchReg, zero_reg, 31, 1);
__ mtc1(kScratchReg, i.OutputSingleRegister());
}
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: {
FPURegister left = i.InputOrZeroDoubleRegister(0);
FPURegister right = i.InputOrZeroDoubleRegister(1);
bool predicate;
FPUCondition cc =
FlagsConditionToConditionCmpFPU(&predicate, instr->flags_condition());
if ((left == kDoubleRegZero || right == kDoubleRegZero) &&
!__ IsDoubleZeroRegSet()) {
__ Move(kDoubleRegZero, 0.0);
}
__ CompareF64(cc, left, right);
} 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(), 0, 2);
// Move the result in the double result register.
__ MovFromFloatResult(i.OutputDoubleRegister());
break;
}
case kMips64AbsD:
if (kArchVariant == kMips64r6) {
__ abs_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0));
} else {
__ dmfc1(kScratchReg, i.InputDoubleRegister(0));
__ Dins(kScratchReg, zero_reg, 63, 1);
__ dmtc1(kScratchReg, i.OutputDoubleRegister());
}
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: {
__ Floor_d_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0));
break;
}
case kMips64Float32RoundDown: {
__ Floor_s_s(i.OutputSingleRegister(), i.InputSingleRegister(0));
break;
}
case kMips64Float64RoundTruncate: {
__ Trunc_d_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0));
break;
}
case kMips64Float32RoundTruncate: {
__ Trunc_s_s(i.OutputSingleRegister(), i.InputSingleRegister(0));
break;
}
case kMips64Float64RoundUp: {
__ Ceil_d_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0));
break;
}
case kMips64Float32RoundUp: {
__ Ceil_s_s(i.OutputSingleRegister(), i.InputSingleRegister(0));
break;
}
case kMips64Float64RoundTiesEven: {
__ Round_d_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0));
break;
}
case kMips64Float32RoundTiesEven: {
__ Round_s_s(i.OutputSingleRegister(), i.InputSingleRegister(0));
break;
}
case kMips64Float32Max: {
FPURegister dst = i.OutputSingleRegister();
FPURegister src1 = i.InputSingleRegister(0);
FPURegister src2 = i.InputSingleRegister(1);
auto ool = zone()->New<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 = zone()->New<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 = zone()->New<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 = zone()->New<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;
__ Trunc_uw_d(i.OutputRegister(), i.InputDoubleRegister(0), scratch);
break;
}
case kMips64TruncUwS: {
FPURegister scratch = kScratchDoubleReg;
__ Trunc_uw_s(i.OutputRegister(), i.InputDoubleRegister(0), 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;
__ Trunc_ul_s(i.OutputRegister(), i.InputDoubleRegister(0), scratch,
result);
break;
}
case kMips64TruncUlD: {
FPURegister scratch = kScratchDoubleReg;
Register result = instr->OutputCount() > 1 ? i.OutputRegister(1) : no_reg;
__ Trunc_ul_d(i.OutputRegister(0), i.InputDoubleRegister(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());
EmitWordLoadPoisoningIfNeeded(this, opcode, instr, i);
break;
case kMips64Lb:
__ Lb(i.OutputRegister(), i.MemoryOperand());
EmitWordLoadPoisoningIfNeeded(this, opcode, instr, i);
break;
case kMips64Sb:
__ Sb(i.InputOrZeroRegister(2), i.MemoryOperand());
break;
case kMips64Lhu:
__ Lhu(i.OutputRegister(), i.MemoryOperand());
EmitWordLoadPoisoningIfNeeded(this, opcode, instr, i);
break;
case kMips64Ulhu:
__ Ulhu(i.OutputRegister(), i.MemoryOperand());
EmitWordLoadPoisoningIfNeeded(this, opcode, instr, i);
break;
case kMips64Lh:
__ Lh(i.OutputRegister(), i.MemoryOperand());
EmitWordLoadPoisoningIfNeeded(this, opcode, instr, i);
break;
case kMips64Ulh:
__ Ulh(i.OutputRegister(), i.MemoryOperand());
EmitWordLoadPoisoningIfNeeded(this, opcode, instr, i);
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());
EmitWordLoadPoisoningIfNeeded(this, opcode, instr, i);
break;
case kMips64Ulw:
__ Ulw(i.OutputRegister(), i.MemoryOperand());
EmitWordLoadPoisoningIfNeeded(this, opcode, instr, i);
break;
case kMips64Lwu:
__ Lwu(i.OutputRegister(), i.MemoryOperand());
EmitWordLoadPoisoningIfNeeded(this, opcode, instr, i);
break;
case kMips64Ulwu:
__ Ulwu(i.OutputRegister(), i.MemoryOperand());
EmitWordLoadPoisoningIfNeeded(this, opcode, instr, i);
break;
case kMips64Ld:
__ Ld(i.OutputRegister(), i.MemoryOperand());
EmitWordLoadPoisoningIfNeeded(this, opcode, instr, i);
break;
case kMips64Uld:
__ Uld(i.OutputRegister(), i.MemoryOperand());
EmitWordLoadPoisoningIfNeeded(this, opcode, instr, i);
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 kMips64Sync: {
__ sync();
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 / kSystemPointerSize);
} else {
__ Push(i.InputRegister(0));
frame_access_state()->IncreaseSPDelta(1);
}
break;
case kMips64Peek: {
int reverse_slot = i.InputInt32(0);
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 if (op->representation() == MachineRepresentation::kFloat32) {
__ Lwc1(
i.OutputSingleRegister(0),
MemOperand(fp, offset + kLessSignificantWordInDoublewordOffset));
} else {
DCHECK_EQ(MachineRepresentation::kSimd128, op->representation());
__ ld_b(i.OutputSimd128Register(), 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) /
kSystemPointerSize);
break;
}
case kMips64StoreToStackSlot: {
if (instr->InputAt(0)->IsFPRegister()) {
if (instr->InputAt(0)->IsSimd128Register()) {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ st_b(i.InputSimd128Register(0), MemOperand(sp, i.InputInt32(1)));
} else {
__ 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: {
__ ByteSwapSigned(i.OutputRegister(0), i.InputRegister(0), 4);
break;
}
case kMips64S128Load8Splat: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ Lb(kScratchReg, i.MemoryOperand());
__ fill_b(i.OutputSimd128Register(), kScratchReg);
break;
}
case kMips64S128Load16Splat: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ Lh(kScratchReg, i.MemoryOperand());
__ fill_h(i.OutputSimd128Register(), kScratchReg);
break;
}
case kMips64S128Load32Splat: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ Lw(kScratchReg, i.MemoryOperand());
__ fill_w(i.OutputSimd128Register(), kScratchReg);
break;
}
case kMips64S128Load64Splat: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ Ld(kScratchReg, i.MemoryOperand());
__ fill_d(i.OutputSimd128Register(), kScratchReg);
break;
}
case kMips64S128Load8x8S: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Simd128Register dst = i.OutputSimd128Register();
Simd128Register scratch = kSimd128ScratchReg;
__ Ld(kScratchReg, i.MemoryOperand());
__ fill_d(dst, kScratchReg);
__ clti_s_b(scratch, dst, 0);
__ ilvr_b(dst, scratch, dst);
break;
}
case kMips64S128Load8x8U: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Simd128Register dst = i.OutputSimd128Register();
__ xor_v(kSimd128RegZero, kSimd128RegZero, kSimd128RegZero);
__ Ld(kScratchReg, i.MemoryOperand());
__ fill_d(dst, kScratchReg);
__ ilvr_b(dst, kSimd128RegZero, dst);
break;
}
case kMips64S128Load16x4S: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Simd128Register dst = i.OutputSimd128Register();
Simd128Register scratch = kSimd128ScratchReg;
__ Ld(kScratchReg, i.MemoryOperand());
__ fill_d(dst, kScratchReg);
__ clti_s_h(scratch, dst, 0);
__ ilvr_h(dst, scratch, dst);
break;
}
case kMips64S128Load16x4U: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Simd128Register dst = i.OutputSimd128Register();
__ xor_v(kSimd128RegZero, kSimd128RegZero, kSimd128RegZero);
__ Ld(kScratchReg, i.MemoryOperand());
__ fill_d(dst, kScratchReg);
__ ilvr_h(dst, kSimd128RegZero, dst);
break;
}
case kMips64S128Load32x2S: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Simd128Register dst = i.OutputSimd128Register();
Simd128Register scratch = kSimd128ScratchReg;
__ Ld(kScratchReg, i.MemoryOperand());
__ fill_d(dst, kScratchReg);
__ clti_s_w(scratch, dst, 0);
__ ilvr_w(dst, scratch, dst);
break;
}
case kMips64S128Load32x2U: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Simd128Register dst = i.OutputSimd128Register();
__ xor_v(kSimd128RegZero, kSimd128RegZero, kSimd128RegZero);
__ Ld(kScratchReg, i.MemoryOperand());
__ fill_d(dst, kScratchReg);
__ ilvr_w(dst, kSimd128RegZero, dst);
break;
}
case kMips64S128Load32Zero: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Simd128Register dst = i.OutputSimd128Register();
__ xor_v(dst, dst, dst);
__ Lwu(kScratchReg, i.MemoryOperand());
__ insert_w(dst, 0, kScratchReg);
break;
}
case kMips64S128Load64Zero: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Simd128Register dst = i.OutputSimd128Register();
__ xor_v(dst, dst, dst);
__ Ld(kScratchReg, i.MemoryOperand());
__ insert_d(dst, 0, kScratchReg);
break;
}
case kWord32AtomicLoadInt8:
ASSEMBLE_ATOMIC_LOAD_INTEGER(Lb);
break;
case kWord32AtomicLoadUint8:
ASSEMBLE_ATOMIC_LOAD_INTEGER(Lbu);
break;
case kWord32AtomicLoadInt16:
ASSEMBLE_ATOMIC_LOAD_INTEGER(Lh);
break;
case kWord32AtomicLoadUint16:
ASSEMBLE_ATOMIC_LOAD_INTEGER(Lhu);
break;
case kWord32AtomicLoadWord32:
ASSEMBLE_ATOMIC_LOAD_INTEGER(Lw);
break;
case kMips64Word64AtomicLoadUint8:
ASSEMBLE_ATOMIC_LOAD_INTEGER(Lbu);
break;
case kMips64Word64AtomicLoadUint16:
ASSEMBLE_ATOMIC_LOAD_INTEGER(Lhu);
break;
case kMips64Word64AtomicLoadUint32:
ASSEMBLE_ATOMIC_LOAD_INTEGER(Lwu);
break;
case kMips64Word64AtomicLoadUint64:
ASSEMBLE_ATOMIC_LOAD_INTEGER(Ld);
break;
case kWord32AtomicStoreWord8:
ASSEMBLE_ATOMIC_STORE_INTEGER(Sb);
break;
case kWord32AtomicStoreWord16:
ASSEMBLE_ATOMIC_STORE_INTEGER(Sh);
break;
case kWord32AtomicStoreWord32:
ASSEMBLE_ATOMIC_STORE_INTEGER(Sw);
break;
case kMips64Word64AtomicStoreWord8:
ASSEMBLE_ATOMIC_STORE_INTEGER(Sb);
break;
case kMips64Word64AtomicStoreWord16:
ASSEMBLE_ATOMIC_STORE_INTEGER(Sh);
break;
case kMips64Word64AtomicStoreWord32:
ASSEMBLE_ATOMIC_STORE_INTEGER(Sw);
break;
case kMips64Word64AtomicStoreWord64:
ASSEMBLE_ATOMIC_STORE_INTEGER(Sd);
break;
case kWord32AtomicExchangeInt8:
ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT(Ll, Sc, true, 8, 32);
break;
case kWord32AtomicExchangeUint8:
ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT(Ll, Sc, false, 8, 32);
break;
case kWord32AtomicExchangeInt16:
ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT(Ll, Sc, true, 16, 32);
break;
case kWord32AtomicExchangeUint16:
ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT(Ll, Sc, false, 16, 32);
break;
case kWord32AtomicExchangeWord32:
ASSEMBLE_ATOMIC_EXCHANGE_INTEGER(Ll, Sc);
break;
case kMips64Word64AtomicExchangeUint8:
ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT(Lld, Scd, false, 8, 64);
break;
case kMips64Word64AtomicExchangeUint16:
ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT(Lld, Scd, false, 16, 64);
break;
case kMips64Word64AtomicExchangeUint32:
ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT(Lld, Scd, false, 32, 64);
break;
case kMips64Word64AtomicExchangeUint64:
ASSEMBLE_ATOMIC_EXCHANGE_INTEGER(Lld, Scd);
break;
case kWord32AtomicCompareExchangeInt8:
ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER_EXT(Ll, Sc, true, 8, 32);
break;
case kWord32AtomicCompareExchangeUint8:
ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER_EXT(Ll, Sc, false, 8, 32);
break;
case kWord32AtomicCompareExchangeInt16:
ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER_EXT(Ll, Sc, true, 16, 32);
break;
case kWord32AtomicCompareExchangeUint16:
ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER_EXT(Ll, Sc, false, 16, 32);
break;
case kWord32AtomicCompareExchangeWord32:
__ sll(i.InputRegister(2), i.InputRegister(2), 0);
ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER(Ll, Sc);
break;
case kMips64Word64AtomicCompareExchangeUint8:
ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER_EXT(Lld, Scd, false, 8, 64);
break;
case kMips64Word64AtomicCompareExchangeUint16:
ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER_EXT(Lld, Scd, false, 16, 64);
break;
case kMips64Word64AtomicCompareExchangeUint32:
ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER_EXT(Lld, Scd, false, 32, 64);
break;
case kMips64Word64AtomicCompareExchangeUint64:
ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER(Lld, Scd);
break;
#define ATOMIC_BINOP_CASE(op, inst) \
case kWord32Atomic##op##Int8: \
ASSEMBLE_ATOMIC_BINOP_EXT(Ll, Sc, true, 8, inst, 32); \
break; \
case kWord32Atomic##op##Uint8: \
ASSEMBLE_ATOMIC_BINOP_EXT(Ll, Sc, false, 8, inst, 32); \
break; \
case kWord32Atomic##op##Int16: \
ASSEMBLE_ATOMIC_BINOP_EXT(Ll, Sc, true, 16, inst, 32); \
break; \
case kWord32Atomic##op##Uint16: \
ASSEMBLE_ATOMIC_BINOP_EXT(Ll, Sc, false, 16, inst, 32); \
break; \
case kWord32Atomic##op##Word32: \
ASSEMBLE_ATOMIC_BINOP(Ll, Sc, 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
#define ATOMIC_BINOP_CASE(op, inst) \
case kMips64Word64Atomic##op##Uint8: \
ASSEMBLE_ATOMIC_BINOP_EXT(Lld, Scd, false, 8, inst, 64); \
break; \
case kMips64Word64Atomic##op##Uint16: \
ASSEMBLE_ATOMIC_BINOP_EXT(Lld, Scd, false, 16, inst, 64); \
break; \
case kMips64Word64Atomic##op##Uint32: \
ASSEMBLE_ATOMIC_BINOP_EXT(Lld, Scd, false, 32, inst, 64); \
break; \
case kMips64Word64Atomic##op##Uint64: \
ASSEMBLE_ATOMIC_BINOP(Lld, Scd, inst); \
break;
ATOMIC_BINOP_CASE(Add, Daddu)
ATOMIC_BINOP_CASE(Sub, Dsubu)
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 kMips64S128Const: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Simd128Register dst = i.OutputSimd128Register();
uint64_t imm1 = make_uint64(i.InputUint32(1), i.InputUint32(0));
uint64_t imm2 = make_uint64(i.InputUint32(3), i.InputUint32(2));
__ li(kScratchReg, imm1);
__ insert_d(dst, 0, kScratchReg);
__ li(kScratchReg, imm2);
__ insert_d(dst, 1, kScratchReg);
break;
}
case kMips64S128Zero: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Simd128Register dst = i.OutputSimd128Register();
__ xor_v(dst, dst, dst);
break;
}
case kMips64S128AllOnes: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Simd128Register dst = i.OutputSimd128Register();
__ ceq_d(dst, dst, dst);
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 kMips64F64x2Abs: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ bclri_d(i.OutputSimd128Register(), i.InputSimd128Register(0), 63);
break;
}
case kMips64F64x2Neg: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ bnegi_d(i.OutputSimd128Register(), i.InputSimd128Register(0), 63);
break;
}
case kMips64F64x2Sqrt: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ fsqrt_d(i.OutputSimd128Register(), i.InputSimd128Register(0));
break;
}
case kMips64F64x2Add: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
ASSEMBLE_F64X2_ARITHMETIC_BINOP(fadd_d);
break;
}
case kMips64F64x2Sub: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
ASSEMBLE_F64X2_ARITHMETIC_BINOP(fsub_d);
break;
}
case kMips64F64x2Mul: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
ASSEMBLE_F64X2_ARITHMETIC_BINOP(fmul_d);
break;
}
case kMips64F64x2Div: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
ASSEMBLE_F64X2_ARITHMETIC_BINOP(fdiv_d);
break;
}
case kMips64F64x2Min: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Simd128Register dst = i.OutputSimd128Register();
Simd128Register src0 = i.InputSimd128Register(0);
Simd128Register src1 = i.InputSimd128Register(1);
Simd128Register scratch0 = kSimd128RegZero;
Simd128Register scratch1 = kSimd128ScratchReg;
// If inputs are -0.0. and +0.0, then write -0.0 to scratch1.
// scratch1 = (src0 == src1) ? (src0 | src1) : (src1 | src1).
__ fseq_d(scratch0, src0, src1);
__ bsel_v(scratch0, src1, src0);
__ or_v(scratch1, scratch0, src1);
// scratch0 = isNaN(src0) ? src0 : scratch1.
__ fseq_d(scratch0, src0, src0);
__ bsel_v(scratch0, src0, scratch1);
// scratch1 = (src0 < scratch0) ? src0 : scratch0.
__ fslt_d(scratch1, src0, scratch0);
__ bsel_v(scratch1, scratch0, src0);
// Canonicalize the result.
__ fmin_d(dst, scratch1, scratch1);
break;
}
case kMips64F64x2Max: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Simd128Register dst = i.OutputSimd128Register();
Simd128Register src0 = i.InputSimd128Register(0);
Simd128Register src1 = i.InputSimd128Register(1);
Simd128Register scratch0 = kSimd128RegZero;
Simd128Register scratch1 = kSimd128ScratchReg;
// If inputs are -0.0. and +0.0, then write +0.0 to scratch1.
// scratch1 = (src0 == src1) ? (src0 & src1) : (src1 & src1).
__ fseq_d(scratch0, src0, src1);
__ bsel_v(scratch0, src1, src0);
__ and_v(scratch1, scratch0, src1);
// scratch0 = isNaN(src0) ? src0 : scratch1.
__ fseq_d(scratch0, src0, src0);
__ bsel_v(scratch0, src0, scratch1);
// scratch1 = (scratch0 < src0) ? src0 : scratch0.
__ fslt_d(scratch1, scratch0, src0);
__ bsel_v(scratch1, scratch0, src0);
// Canonicalize the result.
__ fmax_d(dst, scratch1, scratch1);
break;
}
case kMips64F64x2Eq: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ fceq_d(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMips64F64x2Ne: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ fcune_d(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMips64F64x2Lt: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ fclt_d(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMips64F64x2Le: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ fcle_d(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMips64F64x2Splat: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ Move(kScratchReg, i.InputDoubleRegister(0));
__ fill_d(i.OutputSimd128Register(), kScratchReg);
break;
}
case kMips64F64x2ExtractLane: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ copy_s_d(kScratchReg, i.InputSimd128Register(0), i.InputInt8(1));
__ Move(i.OutputDoubleRegister(), kScratchReg);
break;
}
case kMips64F64x2ReplaceLane: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Simd128Register src = i.InputSimd128Register(0);
Simd128Register dst = i.OutputSimd128Register();
__ Move(kScratchReg, i.InputDoubleRegister(2));
if (dst != src) {
__ move_v(dst, src);
}
__ insert_d(dst, i.InputInt8(1), kScratchReg);
break;
}
case kMips64I64x2Splat: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ fill_d(i.OutputSimd128Register(), i.InputRegister(0));
break;
}
case kMips64I64x2ExtractLane: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ copy_s_d(i.OutputRegister(), i.InputSimd128Register(0),
i.InputInt8(1));
break;
}
case kMips64F64x2Pmin: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Simd128Register dst = i.OutputSimd128Register();
Simd128Register lhs = i.InputSimd128Register(0);
Simd128Register rhs = i.InputSimd128Register(1);
// dst = rhs < lhs ? rhs : lhs
__ fclt_d(dst, rhs, lhs);
__ bsel_v(dst, lhs, rhs);
break;
}
case kMips64F64x2Pmax: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Simd128Register dst = i.OutputSimd128Register();
Simd128Register lhs = i.InputSimd128Register(0);
Simd128Register rhs = i.InputSimd128Register(1);
// dst = lhs < rhs ? rhs : lhs
__ fclt_d(dst, lhs, rhs);
__ bsel_v(dst, lhs, rhs);
break;
}
case kMips64F64x2Ceil: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ MSARoundD(i.OutputSimd128Register(), i.InputSimd128Register(0),
kRoundToPlusInf);
break;
}
case kMips64F64x2Floor: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ MSARoundD(i.OutputSimd128Register(), i.InputSimd128Register(0),
kRoundToMinusInf);
break;
}
case kMips64F64x2Trunc: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ MSARoundD(i.OutputSimd128Register(), i.InputSimd128Register(0),
kRoundToZero);
break;
}
case kMips64F64x2NearestInt: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ MSARoundD(i.OutputSimd128Register(), i.InputSimd128Register(0),
kRoundToNearest);
break;
}
case kMips64I64x2ReplaceLane: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Simd128Register src = i.InputSimd128Register(0);
Simd128Register dst = i.OutputSimd128Register();
if (src != dst) {
__ move_v(dst, src);
}
__ insert_d(dst, i.InputInt8(1), i.InputRegister(2));
break;
}
case kMips64I64x2Add: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ addv_d(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMips64I64x2Sub: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ subv_d(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMips64I64x2Mul: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ mulv_d(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMips64I64x2Neg: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ xor_v(kSimd128RegZero, kSimd128RegZero, kSimd128RegZero);
__ subv_d(i.OutputSimd128Register(), kSimd128RegZero,
i.InputSimd128Register(0));
break;
}
case kMips64I64x2Shl: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
if (instr->InputAt(1)->IsRegister()) {
__ fill_d(kSimd128ScratchReg, i.InputRegister(1));
__ sll_d(i.OutputSimd128Register(), i.InputSimd128Register(0),
kSimd128ScratchReg);
} else {
__ slli_d(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputInt6(1));
}
break;
}
case kMips64I64x2ShrS: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
if (instr->InputAt(1)->IsRegister()) {
__ fill_d(kSimd128ScratchReg, i.InputRegister(1));
__ sra_d(i.OutputSimd128Register(), i.InputSimd128Register(0),
kSimd128ScratchReg);
} else {
__ srai_d(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputInt6(1));
}
break;
}
case kMips64I64x2ShrU: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
if (instr->InputAt(1)->IsRegister()) {
__ fill_d(kSimd128ScratchReg, i.InputRegister(1));
__ srl_d(i.OutputSimd128Register(), i.InputSimd128Register(0),
kSimd128ScratchReg);
} else {
__ srli_d(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputInt6(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();
__ FmoveLow(kScratchReg, i.InputSingleRegister(2));
if (dst != src) {
__ move_v(dst, src);
}
__ 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);
if (instr->InputAt(1)->IsRegister()) {
__ fill_w(kSimd128ScratchReg, i.InputRegister(1));
__ sll_w(i.OutputSimd128Register(), i.InputSimd128Register(0),
kSimd128ScratchReg);
} else {
__ slli_w(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputInt5(1));
}
break;
}
case kMips64I32x4ShrS: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
if (instr->InputAt(1)->IsRegister()) {
__ fill_w(kSimd128ScratchReg, i.InputRegister(1));
__ sra_w(i.OutputSimd128Register(), i.InputSimd128Register(0),
kSimd128ScratchReg);
} else {
__ srai_w(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputInt5(1));
}
break;
}
case kMips64I32x4ShrU: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
if (instr->InputAt(1)->IsRegister()) {
__ fill_w(kSimd128ScratchReg, i.InputRegister(1));
__ srl_w(i.OutputSimd128Register(), i.InputSimd128Register(0),
kSimd128ScratchReg);
} else {
__ 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 kMips64S128AndNot: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Simd128Register scratch = kSimd128ScratchReg,
dst = i.OutputSimd128Register(),
src0 = i.InputSimd128Register(0),
src1 = i.InputSimd128Register(1);
__ nor_v(scratch, src1, src1);
__ and_v(dst, scratch, src0);
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 kMips64F32x4Div: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ fdiv_w(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMips64F32x4Max: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Simd128Register dst = i.OutputSimd128Register();
Simd128Register src0 = i.InputSimd128Register(0);
Simd128Register src1 = i.InputSimd128Register(1);
Simd128Register scratch0 = kSimd128RegZero;
Simd128Register scratch1 = kSimd128ScratchReg;
// If inputs are -0.0. and +0.0, then write +0.0 to scratch1.
// scratch1 = (src0 == src1) ? (src0 & src1) : (src1 & src1).
__ fseq_w(scratch0, src0, src1);
__ bsel_v(scratch0, src1, src0);
__ and_v(scratch1, scratch0, src1);
// scratch0 = isNaN(src0) ? src0 : scratch1.
__ fseq_w(scratch0, src0, src0);
__ bsel_v(scratch0, src0, scratch1);
// scratch1 = (scratch0 < src0) ? src0 : scratch0.
__ fslt_w(scratch1, scratch0, src0);
__ bsel_v(scratch1, scratch0, src0);
// Canonicalize the result.
__ fmax_w(dst, scratch1, scratch1);
break;
}
case kMips64F32x4Min: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Simd128Register dst = i.OutputSimd128Register();
Simd128Register src0 = i.InputSimd128Register(0);
Simd128Register src1 = i.InputSimd128Register(1);
Simd128Register scratch0 = kSimd128RegZero;
Simd128Register scratch1 = kSimd128ScratchReg;
// If inputs are -0.0. and +0.0, then write -0.0 to scratch1.
// scratch1 = (src0 == src1) ? (src0 | src1) : (src1 | src1).
__ fseq_w(scratch0, src0, src1);
__ bsel_v(scratch0, src1, src0);
__ or_v(scratch1, scratch0, src1);
// scratch0 = isNaN(src0) ? src0 : scratch1.
__ fseq_w(scratch0, src0, src0);
__ bsel_v(scratch0, src0, scratch1);
// scratch1 = (src0 < scratch0) ? src0 : scratch0.
__ fslt_w(scratch1, src0, scratch0);
__ bsel_v(scratch1, scratch0, src0);
// Canonicalize the result.
__ fmin_w(dst, scratch1, scratch1);
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);
__ fcune_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 kMips64F32x4Pmin: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Simd128Register dst = i.OutputSimd128Register();
Simd128Register lhs = i.InputSimd128Register(0);
Simd128Register rhs = i.InputSimd128Register(1);
// dst = rhs < lhs ? rhs : lhs
__ fclt_w(dst, rhs, lhs);
__ bsel_v(dst, lhs, rhs);
break;
}
case kMips64F32x4Pmax: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Simd128Register dst = i.OutputSimd128Register();
Simd128Register lhs = i.InputSimd128Register(0);
Simd128Register rhs = i.InputSimd128Register(1);
// dst = lhs < rhs ? rhs : lhs
__ fclt_w(dst, lhs, rhs);
__ bsel_v(dst, lhs, rhs);
break;
}
case kMips64F32x4Ceil: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ MSARoundW(i.OutputSimd128Register(), i.InputSimd128Register(0),
kRoundToPlusInf);
break;
}
case kMips64F32x4Floor: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ MSARoundW(i.OutputSimd128Register(), i.InputSimd128Register(0),
kRoundToMinusInf);
break;
}
case kMips64F32x4Trunc: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ MSARoundW(i.OutputSimd128Register(), i.InputSimd128Register(0),
kRoundToZero);
break;
}
case kMips64F32x4NearestInt: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ MSARoundW(i.OutputSimd128Register(), i.InputSimd128Register(0),
kRoundToNearest);
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 kMips64F32x4Sqrt: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ fsqrt_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 kMips64I32x4Abs: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ xor_v(kSimd128RegZero, kSimd128RegZero, kSimd128RegZero);
__ asub_s_w(i.OutputSimd128Register(), i.InputSimd128Register(0),
kSimd128RegZero);
break;
}
case kMips64I32x4BitMask: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Register dst = i.OutputRegister();
Simd128Register src = i.InputSimd128Register(0);
Simd128Register scratch0 = kSimd128RegZero;
Simd128Register scratch1 = kSimd128ScratchReg;
__ srli_w(scratch0, src, 31);
__ srli_d(scratch1, scratch0, 31);
__ or_v(scratch0, scratch0, scratch1);
__ shf_w(scratch1, scratch0, 0x0E);
__ slli_d(scratch1, scratch1, 2);
__ or_v(scratch0, scratch0, scratch1);
__ copy_u_b(dst, scratch0, 0);
break;
}
case kMips64I32x4DotI16x8S: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ dotp_s_w(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMips64I16x8Splat: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ fill_h(i.OutputSimd128Register(), i.InputRegister(0));
break;
}
case kMips64I16x8ExtractLaneU: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ copy_u_h(i.OutputRegister(), i.InputSimd128Register(0),
i.InputInt8(1));
break;
}
case kMips64I16x8ExtractLaneS: {
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);
if (instr->InputAt(1)->IsRegister()) {
__ fill_h(kSimd128ScratchReg, i.InputRegister(1));
__ sll_h(i.OutputSimd128Register(), i.InputSimd128Register(0),
kSimd128ScratchReg);
} else {
__ slli_h(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputInt4(1));
}
break;
}
case kMips64I16x8ShrS: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
if (instr->InputAt(1)->IsRegister()) {
__ fill_h(kSimd128ScratchReg, i.InputRegister(1));
__ sra_h(i.OutputSimd128Register(), i.InputSimd128Register(0),
kSimd128ScratchReg);
} else {
__ srai_h(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputInt4(1));
}
break;
}
case kMips64I16x8ShrU: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
if (instr->InputAt(1)->IsRegister()) {
__ fill_h(kSimd128ScratchReg, i.InputRegister(1));
__ srl_h(i.OutputSimd128Register(), i.InputSimd128Register(0),
kSimd128ScratchReg);
} else {
__ 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 kMips64I16x8AddSatS: {
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 kMips64I16x8SubSatS: {
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 kMips64I16x8AddSatU: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ adds_u_h(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMips64I16x8SubSatU: {
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 kMips64I16x8RoundingAverageU: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ aver_u_h(i.OutputSimd128Register(), i.InputSimd128Register(1),
i.InputSimd128Register(0));
break;
}
case kMips64I16x8Abs: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ xor_v(kSimd128RegZero, kSimd128RegZero, kSimd128RegZero);
__ asub_s_h(i.OutputSimd128Register(), i.InputSimd128Register(0),
kSimd128RegZero);
break;
}
case kMips64I16x8BitMask: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Register dst = i.OutputRegister();
Simd128Register src = i.InputSimd128Register(0);
Simd128Register scratch0 = kSimd128RegZero;
Simd128Register scratch1 = kSimd128ScratchReg;
__ srli_h(scratch0, src, 15);
__ srli_w(scratch1, scratch0, 15);
__ or_v(scratch0, scratch0, scratch1);
__ srli_d(scratch1, scratch0, 30);
__ or_v(scratch0, scratch0, scratch1);
__ shf_w(scratch1, scratch0, 0x0E);
__ slli_d(scratch1, scratch1, 4);
__ or_v(scratch0, scratch0, scratch1);
__ copy_u_b(dst, scratch0, 0);
break;
}
case kMips64I8x16Splat: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ fill_b(i.OutputSimd128Register(), i.InputRegister(0));
break;
}
case kMips64I8x16ExtractLaneU: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ copy_u_b(i.OutputRegister(), i.InputSimd128Register(0),
i.InputInt8(1));
break;
}
case kMips64I8x16ExtractLaneS: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ copy_s_b(i.OutputRegister(), i.InputSimd128Register(0),
i.InputInt8(1));
break;
}
case kMips64I8x16ReplaceLane: {
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 kMips64I8x16Neg: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ xor_v(kSimd128RegZero, kSimd128RegZero, kSimd128RegZero);
__ subv_b(i.OutputSimd128Register(), kSimd128RegZero,
i.InputSimd128Register(0));
break;
}
case kMips64I8x16Shl: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
if (instr->InputAt(1)->IsRegister()) {
__ fill_b(kSimd128ScratchReg, i.InputRegister(1));
__ sll_b(i.OutputSimd128Register(), i.InputSimd128Register(0),
kSimd128ScratchReg);
} else {
__ slli_b(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputInt3(1));
}
break;
}
case kMips64I8x16ShrS: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
if (instr->InputAt(1)->IsRegister()) {
__ fill_b(kSimd128ScratchReg, i.InputRegister(1));
__ sra_b(i.OutputSimd128Register(), i.InputSimd128Register(0),
kSimd128ScratchReg);
} else {
__ srai_b(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputInt3(1));
}
break;
}
case kMips64I8x16Add: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ addv_b(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMips64I8x16AddSatS: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ adds_s_b(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMips64I8x16Sub: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ subv_b(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMips64I8x16SubSatS: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ subs_s_b(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMips64I8x16Mul: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ mulv_b(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMips64I8x16MaxS: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ max_s_b(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMips64I8x16MinS: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ min_s_b(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMips64I8x16Eq: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ ceq_b(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMips64I8x16Ne: {
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 kMips64I8x16GtS: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ clt_s_b(i.OutputSimd128Register(), i.InputSimd128Register(1),
i.InputSimd128Register(0));
break;
}
case kMips64I8x16GeS: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ cle_s_b(i.OutputSimd128Register(), i.InputSimd128Register(1),
i.InputSimd128Register(0));
break;
}
case kMips64I8x16ShrU: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
if (instr->InputAt(1)->IsRegister()) {
__ fill_b(kSimd128ScratchReg, i.InputRegister(1));
__ srl_b(i.OutputSimd128Register(), i.InputSimd128Register(0),
kSimd128ScratchReg);
} else {
__ srli_b(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputInt3(1));
}
break;
}
case kMips64I8x16AddSatU: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ adds_u_b(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMips64I8x16SubSatU: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ subs_u_b(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMips64I8x16MaxU: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ max_u_b(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMips64I8x16MinU: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ min_u_b(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMips64I8x16GtU: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ clt_u_b(i.OutputSimd128Register(), i.InputSimd128Register(1),
i.InputSimd128Register(0));
break;
}
case kMips64I8x16GeU: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ cle_u_b(i.OutputSimd128Register(), i.InputSimd128Register(1),
i.InputSimd128Register(0));
break;
}
case kMips64I8x16RoundingAverageU: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ aver_u_b(i.OutputSimd128Register(), i.InputSimd128Register(1),
i.InputSimd128Register(0));
break;
}
case kMips64I8x16Abs: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ xor_v(kSimd128RegZero, kSimd128RegZero, kSimd128RegZero);
__ asub_s_b(i.OutputSimd128Register(), i.InputSimd128Register(0),
kSimd128RegZero);
break;
}
case kMips64I8x16BitMask: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Register dst = i.OutputRegister();
Simd128Register src = i.InputSimd128Register(0);
Simd128Register scratch0 = kSimd128RegZero;
Simd128Register scratch1 = kSimd128ScratchReg;
__ srli_b(scratch0, src, 7);
__ srli_h(scratch1, scratch0, 7);
__ or_v(scratch0, scratch0, scratch1);
__ srli_w(scratch1, scratch0, 14);
__ or_v(scratch0, scratch0, scratch1);
__ srli_d(scratch1, scratch0, 28);
__ or_v(scratch0, scratch0, scratch1);
__ shf_w(scratch1, scratch0, 0x0E);
__ ilvev_b(scratch0, scratch1, scratch0);
__ copy_u_h(dst, scratch0, 0);
break;
}
case kMips64S128And: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ and_v(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMips64S128Or: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ or_v(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMips64S128Xor: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ xor_v(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1));
break;
}
case kMips64S128Not: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ nor_v(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(0));
break;
}
case kMips64V32x4AnyTrue:
case kMips64V16x8AnyTrue:
case kMips64V8x16AnyTrue: {
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, 0l); // branch delay slot
__ li(dst, 1);
__ bind(&all_false);
break;
}
case kMips64V32x4AllTrue: {
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, 0l);
__ bind(&all_true);
break;
}
case kMips64V16x8AllTrue: {
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, 0l);
__ bind(&all_true);
break;
}
case kMips64V8x16AllTrue: {
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, 0l);
__ bind(&all_true);
break;
}
case kMips64MsaLd: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ ld_b(i.OutputSimd128Register(), i.MemoryOperand());
break;
}
case kMips64MsaSt: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ st_b(i.InputSimd128Register(2), i.MemoryOperand());
break;
}
case kMips64S32x4InterleaveRight: {
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 kMips64S32x4InterleaveLeft: {
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 kMips64S32x4PackEven: {
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 kMips64S32x4PackOdd: {
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 kMips64S32x4InterleaveEven: {
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 kMips64S32x4InterleaveOdd: {
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, 5, 1]
__ ilvod_w(dst, src1, src0);
break;
}
case kMips64S32x4Shuffle: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Simd128Register dst = i.OutputSimd128Register(),
src0 = i.InputSimd128Register(0),
src1 = i.InputSimd128Register(1);
int32_t shuffle = i.InputInt32(2);
if (src0 == src1) {
// Unary S32x4 shuffles are handled with shf.w instruction
unsigned lane = shuffle & 0xFF;
if (FLAG_debug_code) {
// range of all four lanes, for unary instruction,
// should belong to the same range, which can be one of these:
// [0, 3] or [4, 7]
if (lane >= 4) {
int32_t shuffle_helper = shuffle;
for (int i = 0; i < 4; ++i) {
lane = shuffle_helper & 0xFF;
CHECK_GE(lane, 4);
shuffle_helper >>= 8;
}
}
}
uint32_t i8 = 0;
for (int i = 0; i < 4; i++) {
lane = shuffle & 0xFF;
if (lane >= 4) {
lane -= 4;
}
DCHECK_GT(4, lane);
i8 |= lane << (2 * i);
shuffle >>= 8;
}
__ shf_w(dst, src0, i8);
} else {
// For binary shuffles use vshf.w instruction
if (dst == src0) {
__ move_v(kSimd128ScratchReg, src0);
src0 = kSimd128ScratchReg;
} else if (dst == src1) {
__ move_v(kSimd128ScratchReg, src1);
src1 = kSimd128ScratchReg;
}
__ li(kScratchReg, i.InputInt32(2));
__ insert_w(dst, 0, kScratchReg);
__ xor_v(kSimd128RegZero, kSimd128RegZero, kSimd128RegZero);
__ ilvr_b(dst, kSimd128RegZero, dst);
__ ilvr_h(dst, kSimd128RegZero, dst);
__ vshf_w(dst, src1, src0);
}
break;
}
case kMips64S16x8InterleaveRight: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Simd128Register dst = i.OutputSimd128Register(),
src0 = i.InputSimd128Register(0),
src1 = i.InputSimd128Register(1);
// src1 = [15, ... 11, 10, 9, 8], src0 = [7, ... 3, 2, 1, 0]
// dst = [11, 3, 10, 2, 9, 1, 8, 0]
__ ilvr_h(dst, src1, src0);
break;
}
case kMips64S16x8InterleaveLeft: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Simd128Register dst = i.OutputSimd128Register(),
src0 = i.InputSimd128Register(0),
src1 = i.InputSimd128Register(1);
// src1 = [15, ... 11, 10, 9, 8], src0 = [7, ... 3, 2, 1, 0]
// dst = [15, 7, 14, 6, 13, 5, 12, 4]
__ ilvl_h(dst, src1, src0);
break;
}
case kMips64S16x8PackEven: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Simd128Register dst = i.OutputSimd128Register(),
src0 = i.InputSimd128Register(0),
src1 = i.InputSimd128Register(1);
// src1 = [15, ... 11, 10, 9, 8], src0 = [7, ... 3, 2, 1, 0]
// dst = [14, 12, 10, 8, 6, 4, 2, 0]
__ pckev_h(dst, src1, src0);
break;
}
case kMips64S16x8PackOdd: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Simd128Register dst = i.OutputSimd128Register(),
src0 = i.InputSimd128Register(0),
src1 = i.InputSimd128Register(1);
// src1 = [15, ... 11, 10, 9, 8], src0 = [7, ... 3, 2, 1, 0]
// dst = [15, 13, 11, 9, 7, 5, 3, 1]
__ pckod_h(dst, src1, src0);
break;
}
case kMips64S16x8InterleaveEven: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Simd128Register dst = i.OutputSimd128Register(),
src0 = i.InputSimd128Register(0),
src1 = i.InputSimd128Register(1);
// src1 = [15, ... 11, 10, 9, 8], src0 = [7, ... 3, 2, 1, 0]
// dst = [14, 6, 12, 4, 10, 2, 8, 0]
__ ilvev_h(dst, src1, src0);
break;
}
case kMips64S16x8InterleaveOdd: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Simd128Register dst = i.OutputSimd128Register(),
src0 = i.InputSimd128Register(0),
src1 = i.InputSimd128Register(1);
// src1 = [15, ... 11, 10, 9, 8], src0 = [7, ... 3, 2, 1, 0]
// dst = [15, 7, ... 11, 3, 9, 1]
__ ilvod_h(dst, src1, src0);
break;
}
case kMips64S16x4Reverse: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
// src = [7, 6, 5, 4, 3, 2, 1, 0], dst = [4, 5, 6, 7, 0, 1, 2, 3]
// shf.df imm field: 0 1 2 3 = 00011011 = 0x1B
__ shf_h(i.OutputSimd128Register(), i.InputSimd128Register(0), 0x1B);
break;
}
case kMips64S16x2Reverse: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
// src = [7, 6, 5, 4, 3, 2, 1, 0], dst = [6, 7, 4, 5, 3, 2, 0, 1]
// shf.df imm field: 2 3 0 1 = 10110001 = 0xB1
__ shf_h(i.OutputSimd128Register(), i.InputSimd128Register(0), 0xB1);
break;
}
case kMips64S8x16InterleaveRight: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Simd128Register dst = i.OutputSimd128Register(),
src0 = i.InputSimd128Register(0),
src1 = i.InputSimd128Register(1);
// src1 = [31, ... 19, 18, 17, 16], src0 = [15, ... 3, 2, 1, 0]
// dst = [23, 7, ... 17, 1, 16, 0]
__ ilvr_b(dst, src1, src0);
break;
}
case kMips64S8x16InterleaveLeft: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Simd128Register dst = i.OutputSimd128Register(),
src0 = i.InputSimd128Register(0),
src1 = i.InputSimd128Register(1);
// src1 = [31, ... 19, 18, 17, 16], src0 = [15, ... 3, 2, 1, 0]
// dst = [31, 15, ... 25, 9, 24, 8]
__ ilvl_b(dst, src1, src0);
break;
}
case kMips64S8x16PackEven: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Simd128Register dst = i.OutputSimd128Register(),
src0 = i.InputSimd128Register(0),
src1 = i.InputSimd128Register(1);
// src1 = [31, ... 19, 18, 17, 16], src0 = [15, ... 3, 2, 1, 0]
// dst = [30, 28, ... 6, 4, 2, 0]
__ pckev_b(dst, src1, src0);
break;
}
case kMips64S8x16PackOdd: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Simd128Register dst = i.OutputSimd128Register(),
src0 = i.InputSimd128Register(0),
src1 = i.InputSimd128Register(1);
// src1 = [31, ... 19, 18, 17, 16], src0 = [15, ... 3, 2, 1, 0]
// dst = [31, 29, ... 7, 5, 3, 1]
__ pckod_b(dst, src1, src0);
break;
}
case kMips64S8x16InterleaveEven: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Simd128Register dst = i.OutputSimd128Register(),
src0 = i.InputSimd128Register(0),
src1 = i.InputSimd128Register(1);
// src1 = [31, ... 19, 18, 17, 16], src0 = [15, ... 3, 2, 1, 0]
// dst = [30, 14, ... 18, 2, 16, 0]
__ ilvev_b(dst, src1, src0);
break;
}
case kMips64S8x16InterleaveOdd: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Simd128Register dst = i.OutputSimd128Register(),
src0 = i.InputSimd128Register(0),
src1 = i.InputSimd128Register(1);
// src1 = [31, ... 19, 18, 17, 16], src0 = [15, ... 3, 2, 1, 0]
// dst = [31, 15, ... 19, 3, 17, 1]
__ ilvod_b(dst, src1, src0);
break;
}
case kMips64S8x16Concat: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Simd128Register dst = i.OutputSimd128Register();
DCHECK(dst == i.InputSimd128Register(0));
__ sldi_b(dst, i.InputSimd128Register(1), i.InputInt4(2));
break;
}
case kMips64I8x16Shuffle: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Simd128Register dst = i.OutputSimd128Register(),
src0 = i.InputSimd128Register(0),
src1 = i.InputSimd128Register(1);
if (dst == src0) {
__ move_v(kSimd128ScratchReg, src0);
src0 = kSimd128ScratchReg;
} else if (dst == src1) {
__ move_v(kSimd128ScratchReg, src1);
src1 = kSimd128ScratchReg;
}
int64_t control_low =
static_cast<int64_t>(i.InputInt32(3)) << 32 | i.InputInt32(2);
int64_t control_hi =
static_cast<int64_t>(i.InputInt32(5)) << 32 | i.InputInt32(4);
__ li(kScratchReg, control_low);
__ insert_d(dst, 0, kScratchReg);
__ li(kScratchReg, control_hi);
__ insert_d(dst, 1, kScratchReg);
__ vshf_b(dst, src1, src0);
break;
}
case kMips64I8x16Swizzle: {
Simd128Register dst = i.OutputSimd128Register(),
tbl = i.InputSimd128Register(0),
ctl = i.InputSimd128Register(1);
DCHECK(dst != ctl && dst != tbl);
Simd128Register zeroReg = i.TempSimd128Register(0);
__ xor_v(zeroReg, zeroReg, zeroReg);
__ move_v(dst, ctl);
__ vshf_b(dst, zeroReg, tbl);
break;
}
case kMips64S8x8Reverse: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
// src = [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
// dst = [8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7]
// [A B C D] => [B A D C]: shf.w imm: 2 3 0 1 = 10110001 = 0xB1
// C: [7, 6, 5, 4] => A': [4, 5, 6, 7]: shf.b imm: 00011011 = 0x1B
__ shf_w(kSimd128ScratchReg, i.InputSimd128Register(0), 0xB1);
__ shf_b(i.OutputSimd128Register(), kSimd128ScratchReg, 0x1B);
break;
}
case kMips64S8x4Reverse: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
// src = [15, 14, ... 3, 2, 1, 0], dst = [12, 13, 14, 15, ... 0, 1, 2, 3]
// shf.df imm field: 0 1 2 3 = 00011011 = 0x1B
__ shf_b(i.OutputSimd128Register(), i.InputSimd128Register(0), 0x1B);
break;
}
case kMips64S8x2Reverse: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
// src = [15, 14, ... 3, 2, 1, 0], dst = [14, 15, 12, 13, ... 2, 3, 0, 1]
// shf.df imm field: 2 3 0 1 = 10110001 = 0xB1
__ shf_b(i.OutputSimd128Register(), i.InputSimd128Register(0), 0xB1);
break;
}
case kMips64I32x4SConvertI16x8Low: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Simd128Register dst = i.OutputSimd128Register();
Simd128Register src = i.InputSimd128Register(0);
__ ilvr_h(kSimd128ScratchReg, src, src);
__ slli_w(dst, kSimd128ScratchReg, 16);
__ srai_w(dst, dst, 16);
break;
}
case kMips64I32x4SConvertI16x8High: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Simd128Register dst = i.OutputSimd128Register();
Simd128Register src = i.InputSimd128Register(0);
__ ilvl_h(kSimd128ScratchReg, src, src);
__ slli_w(dst, kSimd128ScratchReg, 16);
__ srai_w(dst, dst, 16);
break;
}
case kMips64I32x4UConvertI16x8Low: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ xor_v(kSimd128RegZero, kSimd128RegZero, kSimd128RegZero);
__ ilvr_h(i.OutputSimd128Register(), kSimd128RegZero,
i.InputSimd128Register(0));
break;
}
case kMips64I32x4UConvertI16x8High: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ xor_v(kSimd128RegZero, kSimd128RegZero, kSimd128RegZero);
__ ilvl_h(i.OutputSimd128Register(), kSimd128RegZero,
i.InputSimd128Register(0));
break;
}
case kMips64I16x8SConvertI8x16Low: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Simd128Register dst = i.OutputSimd128Register();
Simd128Register src = i.InputSimd128Register(0);
__ ilvr_b(kSimd128ScratchReg, src, src);
__ slli_h(dst, kSimd128ScratchReg, 8);
__ srai_h(dst, dst, 8);
break;
}
case kMips64I16x8SConvertI8x16High: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Simd128Register dst = i.OutputSimd128Register();
Simd128Register src = i.InputSimd128Register(0);
__ ilvl_b(kSimd128ScratchReg, src, src);
__ slli_h(dst, kSimd128ScratchReg, 8);
__ srai_h(dst, dst, 8);
break;
}
case kMips64I16x8SConvertI32x4: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Simd128Register dst = i.OutputSimd128Register();
Simd128Register src0 = i.InputSimd128Register(0);
Simd128Register src1 = i.InputSimd128Register(1);
__ sat_s_w(kSimd128ScratchReg, src0, 15);
__ sat_s_w(kSimd128RegZero, src1, 15); // kSimd128RegZero as scratch
__ pckev_h(dst, kSimd128RegZero, kSimd128ScratchReg);
break;
}
case kMips64I16x8UConvertI32x4: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Simd128Register dst = i.OutputSimd128Register();
Simd128Register src0 = i.InputSimd128Register(0);
Simd128Register src1 = i.InputSimd128Register(1);
__ xor_v(kSimd128RegZero, kSimd128RegZero, kSimd128RegZero);
__ max_s_w(kSimd128ScratchReg, kSimd128RegZero, src0);
__ sat_u_w(kSimd128ScratchReg, kSimd128ScratchReg, 15);
__ max_s_w(dst, kSimd128RegZero, src1);
__ sat_u_w(dst, dst, 15);
__ pckev_h(dst, dst, kSimd128ScratchReg);
break;
}
case kMips64I16x8UConvertI8x16Low: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ xor_v(kSimd128RegZero, kSimd128RegZero, kSimd128RegZero);
__ ilvr_b(i.OutputSimd128Register(), kSimd128RegZero,
i.InputSimd128Register(0));
break;
}
case kMips64I16x8UConvertI8x16High: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
__ xor_v(kSimd128RegZero, kSimd128RegZero, kSimd128RegZero);
__ ilvl_b(i.OutputSimd128Register(), kSimd128RegZero,
i.InputSimd128Register(0));
break;
}
case kMips64I8x16SConvertI16x8: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Simd128Register dst = i.OutputSimd128Register();
Simd128Register src0 = i.InputSimd128Register(0);
Simd128Register src1 = i.InputSimd128Register(1);
__ sat_s_h(kSimd128ScratchReg, src0, 7);
__ sat_s_h(kSimd128RegZero, src1, 7); // kSimd128RegZero as scratch
__ pckev_b(dst, kSimd128RegZero, kSimd128ScratchReg);
break;
}
case kMips64I8x16UConvertI16x8: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Simd128Register dst = i.OutputSimd128Register();
Simd128Register src0 = i.InputSimd128Register(0);
Simd128Register src1 = i.InputSimd128Register(1);
__ xor_v(kSimd128RegZero, kSimd128RegZero, kSimd128RegZero);
__ max_s_h(kSimd128ScratchReg, kSimd128RegZero, src0);
__ sat_u_h(kSimd128ScratchReg, kSimd128ScratchReg, 7);
__ max_s_h(dst, kSimd128RegZero, src1);
__ sat_u_h(dst, dst, 7);
__ pckev_b(dst, dst, kSimd128ScratchReg);
break;
}
case kMips64F32x4AddHoriz: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Simd128Register src0 = i.InputSimd128Register(0);
Simd128Register src1 = i.InputSimd128Register(1);
Simd128Register dst = i.OutputSimd128Register();
__ shf_w(kSimd128ScratchReg, src0, 0xB1); // 2 3 0 1 : 10110001 : 0xB1
__ shf_w(kSimd128RegZero, src1, 0xB1); // kSimd128RegZero as scratch
__ fadd_w(kSimd128ScratchReg, kSimd128ScratchReg, src0);
__ fadd_w(kSimd128RegZero, kSimd128RegZero, src1);
__ pckev_w(dst, kSimd128RegZero, kSimd128ScratchReg);
break;
}
case kMips64I32x4AddHoriz: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Simd128Register src0 = i.InputSimd128Register(0);
Simd128Register src1 = i.InputSimd128Register(1);
Simd128Register dst = i.OutputSimd128Register();
__ hadd_s_d(kSimd128ScratchReg, src0, src0);
__ hadd_s_d(kSimd128RegZero, src1, src1); // kSimd128RegZero as scratch
__ pckev_w(dst, kSimd128RegZero, kSimd128ScratchReg);
break;
}
case kMips64I16x8AddHoriz: {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
Simd128Register src0 = i.InputSimd128Register(0);
Simd128Register src1 = i.InputSimd128Register(1);
Simd128Register dst = i.OutputSimd128Register();
__ hadd_s_w(kSimd128ScratchReg, src0, src0);
__ hadd_s_w(kSimd128RegZero, src1, src1); // kSimd128RegZero as scratch
__ pckev_h(dst, kSimd128RegZero, kSimd128ScratchReg);
break;
}
}
return kSuccess;
} // NOLINT(readability/fn_size)
#define UNSUPPORTED_COND(opcode, condition) \
StdoutStream{} << "Unsupported " << #opcode << " condition: \"" << condition \
<< "\""; \
UNIMPLEMENTED();
void AssembleBranchToLabels(CodeGenerator* gen, TurboAssembler* tasm,
Instruction* instr, FlagsCondition condition,
Label* tlabel, Label* flabel, bool fallthru) {
#undef __
#define __ tasm->
MipsOperandConverter i(gen, instr);
Condition cc = kNoCondition;
// MIPS does not have condition code flags, so compare and branch are
// implemented differently than on the other arch's. The compare operations
// emit mips pseudo-instructions, which are handled here by branch
// instructions that do the actual comparison. Essential that the input
// registers to compare pseudo-op are not modified before this branch op, as
// they are tested here.
if (instr->arch_opcode() == kMips64Tst) {
cc = FlagsConditionToConditionTst(condition);
__ Branch(tlabel, cc, kScratchReg, Operand(zero_reg));
} else if (instr->arch_opcode() == kMips64Dadd ||
instr->arch_opcode() == kMips64Dsub) {
cc = FlagsConditionToConditionOvf(condition);
__ dsra32(kScratchReg, i.OutputRegister(), 0);
__ sra(kScratchReg2, i.OutputRegister(), 31);
__ Branch(tlabel, cc, kScratchReg2, Operand(kScratchReg));
} else if (instr->arch_opcode() == kMips64DaddOvf ||
instr->arch_opcode() == kMips64DsubOvf) {
switch (condition) {
// Overflow occurs if overflow register is negative
case kOverflow:
__ Branch(tlabel, lt, kScratchReg, Operand(zero_reg));
break;
case kNotOverflow:
__ Branch(tlabel, ge, kScratchReg, Operand(zero_reg));
break;
default:
UNSUPPORTED_COND(instr->arch_opcode(), condition);
break;
}
} else if (instr->arch_opcode() == kMips64MulOvf) {
// Overflow occurs if overflow register is not zero
switch (condition) {
case kOverflow:
__ Branch(tlabel, ne, kScratchReg, Operand(zero_reg));
break;
case kNotOverflow:
__ Branch(tlabel, eq, kScratchReg, Operand(zero_reg));
break;
default:
UNSUPPORTED_COND(kMipsMulOvf, condition);
break;
}
} else if (instr->arch_opcode() == kMips64Cmp) {
cc = FlagsConditionToConditionCmp(condition);
__ Branch(tlabel, cc, i.InputRegister(0), i.InputOperand(1));
} else if (instr->arch_opcode() == kArchStackPointerGreaterThan) {
cc = FlagsConditionToConditionCmp(condition);
Register lhs_register = sp;
uint32_t offset;
if (gen->ShouldApplyOffsetToStackCheck(instr, &offset)) {
lhs_register = i.TempRegister(0);
__ Dsubu(lhs_register, sp, offset);
}
__ Branch(tlabel, cc, lhs_register, Operand(i.InputRegister(0)));
} else if (instr->arch_opcode() == kMips64CmpS ||
instr->arch_opcode() == kMips64CmpD) {
bool predicate;
FlagsConditionToConditionCmpFPU(&predicate, condition);
if (predicate) {
__ BranchTrueF(tlabel);
} else {
__ BranchFalseF(tlabel);
}
} else {
PrintF("AssembleArchBranch Unimplemented arch_opcode: %d\n",
instr->arch_opcode());
UNIMPLEMENTED();
}
if (!fallthru) __ Branch(flabel); // no fallthru to flabel.
#undef __
#define __ tasm()->
}
// Assembles branches after an instruction.
void CodeGenerator::AssembleArchBranch(Instruction* instr, BranchInfo* branch) {
Label* tlabel = branch->true_label;
Label* flabel = branch->false_label;
AssembleBranchToLabels(this, tasm(), instr, branch->condition, tlabel, flabel,
branch->fallthru);
}
void CodeGenerator::AssembleBranchPoisoning(FlagsCondition condition,
Instruction* instr) {
// TODO(jarin) Handle float comparisons (kUnordered[Not]Equal).
if (condition == kUnorderedEqual || condition == kUnorderedNotEqual) {
return;
}
MipsOperandConverter i(this, instr);
condition = NegateFlagsCondition(condition);
switch (instr->arch_opcode()) {
case kMips64Cmp: {
__ LoadZeroOnCondition(kSpeculationPoisonRegister, i.InputRegister(0),
i.InputOperand(1),
FlagsConditionToConditionCmp(condition));
}
return;
case kMips64Tst: {
switch (condition) {
case kEqual:
__ LoadZeroIfConditionZero(kSpeculationPoisonRegister, kScratchReg);
break;
case kNotEqual:
__ LoadZeroIfConditionNotZero(kSpeculationPoisonRegister,
kScratchReg);
break;
default:
UNREACHABLE();
}
}
return;
case kMips64Dadd:
case kMips64Dsub: {
// Check for overflow creates 1 or 0 for result.
__ dsrl32(kScratchReg, i.OutputRegister(), 31);
__ srl(kScratchReg2, i.OutputRegister(), 31);
__ xor_(kScratchReg2, kScratchReg, kScratchReg2);
switch (condition) {
case kOverflow:
__ LoadZeroIfConditionNotZero(kSpeculationPoisonRegister,
kScratchReg2);
break;
case kNotOverflow:
__ LoadZeroIfConditionZero(kSpeculationPoisonRegister, kScratchReg2);
break;
default:
UNSUPPORTED_COND(instr->arch_opcode(), condition);
}
}
return;
case kMips64DaddOvf:
case kMips64DsubOvf: {
// Overflow occurs if overflow register is negative
__ Slt(kScratchReg2, kScratchReg, zero_reg);
switch (condition) {
case kOverflow:
__ LoadZeroIfConditionNotZero(kSpeculationPoisonRegister,
kScratchReg2);
break;
case kNotOverflow:
__ LoadZeroIfConditionZero(kSpeculationPoisonRegister, kScratchReg2);
break;
default:
UNSUPPORTED_COND(instr->arch_opcode(), condition);
}
}
return;
case kMips64MulOvf: {
// Overflow occurs if overflow register is not zero
switch (condition) {
case kOverflow:
__ LoadZeroIfConditionNotZero(kSpeculationPoisonRegister,
kScratchReg);
break;
case kNotOverflow:
__ LoadZeroIfConditionZero(kSpeculationPoisonRegister, kScratchReg);
break;
default:
UNSUPPORTED_COND(instr->arch_opcode(), condition);
}
}
return;
case kMips64CmpS:
case kMips64CmpD: {
bool predicate;
FlagsConditionToConditionCmpFPU(&predicate, condition);
if (predicate) {
__ LoadZeroIfFPUCondition(kSpeculationPoisonRegister);
} else {
__ LoadZeroIfNotFPUCondition(kSpeculationPoisonRegister);
}
}
return;
default:
UNREACHABLE();
}
}
#undef UNSUPPORTED_COND
void CodeGenerator::AssembleArchDeoptBranch(Instruction* instr,
BranchInfo* branch) {
AssembleArchBranch(instr, branch);
}
void CodeGenerator::AssembleArchJump(RpoNumber target) {
if (!IsNextInAssemblyOrder(target)) __ Branch(GetLabel(target));
}
void CodeGenerator::AssembleArchTrap(Instruction* instr,
FlagsCondition condition) {
class OutOfLineTrap final : public OutOfLineCode {
public:
OutOfLineTrap(CodeGenerator* gen, Instruction* instr)
: OutOfLineCode(gen), instr_(instr), gen_(gen) {}
void Generate() final {
MipsOperandConverter i(gen_, instr_);
TrapId trap_id =
static_cast<TrapId>(i.InputInt32(instr_->InputCount() - 1));
GenerateCallToTrap(trap_id);
}
private:
void GenerateCallToTrap(TrapId trap_id) {
if (trap_id == TrapId::kInvalid) {
// We cannot test calls to the runtime in cctest/test-run-wasm.
// Therefore we emit a call to C here instead of a call to the runtime.
// We use the context register as the scratch register, because we do
// not have a context here.
__ PrepareCallCFunction(0, 0, cp);
__ CallCFunction(
ExternalReference::wasm_call_trap_callback_for_testing(), 0);
__ LeaveFrame(StackFrame::WASM);
auto call_descriptor = gen_->linkage()->GetIncomingDescriptor();
int pop_count =
static_cast<int>(call_descriptor->StackParameterCount());
pop_count += (pop_count & 1); // align
__ Drop(pop_count);
__ Ret();
} else {
gen_->AssembleSourcePosition(instr_);
// A direct call to a wasm runtime stub defined in this module.
// Just encode the stub index. This will be patched when the code
// is added to the native module and copied into wasm code space.
__ Call(static_cast<Address>(trap_id), RelocInfo::WASM_STUB_CALL);
ReferenceMap* reference_map =
gen_->zone()->New<ReferenceMap>(gen_->zone());
gen_->RecordSafepoint(reference_map, Safepoint::kNoLazyDeopt);
if (FLAG_debug_code) {
__ stop();
}
}
}
Instruction* instr_;
CodeGenerator* gen_;
};
auto ool = zone()->New<OutOfLineTrap>(this, instr);
Label* tlabel = ool->entry();
AssembleBranchToLabels(this, tasm(), instr, condition, tlabel, nullptr, true);
}
// Assembles boolean materializations after an instruction.
void CodeGenerator::AssembleArchBoolean(Instruction* instr,
FlagsCondition condition) {
MipsOperandConverter i(this, instr);
// Materialize a full 32-bit 1 or 0 value. The result register is always the
// last output of the instruction.
DCHECK_NE(0u, instr->OutputCount());
Register result = i.OutputRegister(instr->OutputCount() - 1);
Condition cc = kNoCondition;
// MIPS does not have condition code flags, so compare and branch are
// implemented differently than on the other arch's. The compare operations
// emit mips pseudo-instructions, which are checked and handled here.
if (instr->arch_opcode() == kMips64Tst) {
cc = FlagsConditionToConditionTst(condition);
if (cc == eq) {
__ Sltu(result, kScratchReg, 1);
} else {
__ Sltu(result, zero_reg, kScratchReg);
}
return;
} else if (instr->arch_opcode() == kMips64Dadd ||
instr->arch_opcode() == kMips64Dsub) {
cc = FlagsConditionToConditionOvf(condition);
// Check for overflow creates 1 or 0 for result.
__ dsrl32(kScratchReg, i.OutputRegister(), 31);
__ srl(kScratchReg2, i.OutputRegister(), 31);
__ xor_(result, kScratchReg, kScratchReg2);
if (cc == eq) // Toggle result for not overflow.
__ xori(result, result, 1);
return;
} else if (instr->arch_opcode() == kMips64DaddOvf ||
instr->arch_opcode() == kMips64DsubOvf) {
// Overflow occurs if overflow register is negative
__ slt(result, kScratchReg, zero_reg);
} else if (instr->arch_opcode() == kMips64MulOvf) {
// Overflow occurs if overflow register is not zero
__ Sgtu(result, kScratchReg, zero_reg);
} else if (instr->arch_opcode() == kMips64Cmp) {
cc = FlagsConditionToConditionCmp(condition);
switch (cc) {
case eq:
case ne: {
Register left = i.InputRegister(0);
Operand right = i.InputOperand(1);
if (instr->InputAt(1)->IsImmediate()) {
if (is_int16(-right.immediate())) {
if (right.immediate() == 0) {
if (cc == eq) {
__ Sltu(result, left, 1);
} else {
__ Sltu(result, zero_reg, left);
}
} else {
__ Daddu(result, left, Operand(-right.immediate()));
if (cc == eq) {
__ Sltu(result, result, 1);
} else {
__ Sltu(result, zero_reg, result);
}
}
} else {
if (is_uint16(right.immediate())) {
__ Xor(result, left, right);
} else {
__ li(kScratchReg, right);
__ Xor(result, left, kScratchReg);
}
if (cc == eq) {
__ Sltu(result, result, 1);
} else {
__ Sltu(result, zero_reg, result);
}
}
} else {
__ Xor(result, left, right);
if (cc == eq) {
__ Sltu(result, result, 1);
} else {
__ Sltu(result, zero_reg, result);
}
}
} break;
case lt:
case ge: {
Register left = i.InputRegister(0);
Operand right = i.InputOperand(1);
__ Slt(result, left, right);
if (cc == ge) {
__ xori(result, result, 1);
}
} break;
case gt:
case le: {
Register left = i.InputRegister(1);
Operand right = i.InputOperand(0);
__ Slt(result, left, right);
if (cc == le) {
__ xori(result, result, 1);
}
} break;
case lo:
case hs: {
Register left = i.InputRegister(0);
Operand right = i.InputOperand(1);
__ Sltu(result, left, right);
if (cc == hs) {
__ xori(result, result, 1);
}
} break;
case hi:
case ls: {
Register left = i.InputRegister(1);
Operand right = i.InputOperand(0);
__ Sltu(result, left, right);
if (cc == ls) {
__ xori(result, result, 1);
}
} break;
default:
UNREACHABLE();
}
return;
} else if (instr->arch_opcode() == kMips64CmpD ||
instr->arch_opcode() == kMips64CmpS) {
FPURegister left = i.InputOrZeroDoubleRegister(0);
FPURegister right = i.InputOrZeroDoubleRegister(1);
if ((left == kDoubleRegZero || right == kDoubleRegZero) &&
!__ IsDoubleZeroRegSet()) {
__ Move(kDoubleRegZero, 0.0);
}
bool predicate;
FlagsConditionToConditionCmpFPU(&predicate, condition);
if (kArchVariant != kMips64r6) {
__ li(result, Operand(1));
if (predicate) {
__ Movf(result, zero_reg);
} else {
__ Movt(result, zero_reg);
}
} else {
if (instr->arch_opcode() == kMips64CmpD) {
__ dmfc1(result, kDoubleCompareReg);
} else {
DCHECK_EQ(kMips64CmpS, instr->arch_opcode());
__ mfc1(result, kDoubleCompareReg);
}
if (predicate) {
__ And(result, result, 1); // cmp returns all 1's/0's, use only LSB.
} else {
__ Addu(result, result, 1); // Toggle result for not equal.
}
}
return;
} else {
PrintF("AssembleArchBranch Unimplemented arch_opcode is : %d\n",
instr->arch_opcode());
TRACE_UNIMPL();
UNIMPLEMENTED();
}
}
void CodeGenerator::AssembleArchBinarySearchSwitch(Instruction* instr) {
MipsOperandConverter i(this, instr);
Register input = i.InputRegister(0);
std::vector<std::pair<int32_t, Label*>> cases;
for (size_t index = 2; index < instr->InputCount(); index += 2) {
cases.push_back({i.InputInt32(index + 0), GetLabel(i.InputRpo(index + 1))});
}
AssembleArchBinarySearchSwitchRange(input, i.InputRpo(1), cases.data(),
cases.data() + cases.size());
}
void CodeGenerator::AssembleArchTableSwitch(Instruction* instr) {
MipsOperandConverter i(this, instr);
Register input = i.InputRegister(0);
size_t const case_count = instr->InputCount() - 2;
__ Branch(GetLabel(i.InputRpo(1)), hs, input, Operand(case_count));
__ GenerateSwitchTable(input, case_count, [&i, this](size_t index) {
return GetLabel(i.InputRpo(index + 2));
});
}
void CodeGenerator::FinishFrame(Frame* frame) {
auto call_descriptor = linkage()->GetIncomingDescriptor();
const RegList saves_fpu = call_descriptor->CalleeSavedFPRegisters();
if (saves_fpu != 0) {
int count = base::bits::CountPopulation(saves_fpu);
DCHECK_EQ(kNumCalleeSavedFPU, count);
frame->AllocateSavedCalleeRegisterSlots(count *
(kDoubleSize / kSystemPointerSize));
}
const RegList saves = call_descriptor->CalleeSavedRegisters();
if (saves != 0) {
int count = base::bits::CountPopulation(saves);
DCHECK_EQ(kNumCalleeSaved, count + 1);
frame->AllocateSavedCalleeRegisterSlots(count);
}
}
void CodeGenerator::AssembleConstructFrame() {
auto call_descriptor = linkage()->GetIncomingDescriptor();
if (frame_access_state()->has_frame()) {
if (call_descriptor->IsCFunctionCall()) {
if (info()->GetOutputStackFrameType() == StackFrame::C_WASM_ENTRY) {
__ StubPrologue(StackFrame::C_WASM_ENTRY);
// Reserve stack space for saving the c_entry_fp later.
__ Dsubu(sp, sp, Operand(kSystemPointerSize));
} else {
__ Push(ra, fp);
__ mov(fp, sp);
}
} else if (call_descriptor->IsJSFunctionCall()) {
__ Prologue();
} else {
__ StubPrologue(info()->GetOutputStackFrameType());
if (call_descriptor->IsWasmFunctionCall()) {
__ Push(kWasmInstanceRegister);
} else if (call_descriptor->IsWasmImportWrapper() ||
call_descriptor->IsWasmCapiFunction()) {
// Wasm import wrappers are passed a tuple in the place of the instance.
// Unpack the tuple into the instance and the target callable.
// This must be done here in the codegen because it cannot be expressed
// properly in the graph.
__ ld(kJSFunctionRegister,
FieldMemOperand(kWasmInstanceRegister, Tuple2::kValue2Offset));
__ ld(kWasmInstanceRegister,
FieldMemOperand(kWasmInstanceRegister, Tuple2::kValue1Offset));
__ Push(kWasmInstanceRegister);
if (call_descriptor->IsWasmCapiFunction()) {
// Reserve space for saving the PC later.
__ Dsubu(sp, sp, Operand(kSystemPointerSize));
}
}
}
}
int required_slots =
frame()->GetTotalFrameSlotCount() - frame()->GetFixedSlotCount();
if (info()->is_osr()) {
// TurboFan OSR-compiled functions cannot be entered directly.
__ Abort(AbortReason::kShouldNotDirectlyEnterOsrFunction);
// Unoptimized code jumps directly to this entrypoint while the unoptimized
// frame is still on the stack. Optimized code uses OSR values directly from
// the unoptimized frame. Thus, all that needs to be done is to allocate the
// remaining stack slots.
if (FLAG_code_comments) __ RecordComment("-- OSR entrypoint --");
osr_pc_offset_ = __ pc_offset();
required_slots -= osr_helper()->UnoptimizedFrameSlots();
ResetSpeculationPoison();
}
const RegList saves = call_descriptor->CalleeSavedRegisters();
const RegList saves_fpu = call_descriptor->CalleeSavedFPRegisters();
if (required_slots > 0) {
DCHECK(frame_access_state()->has_frame());
if (info()->IsWasm() && required_slots > 128) {
// For WebAssembly functions with big frames we have to do the stack
// overflow check before we construct the frame. Otherwise we may not
// have enough space on the stack to call the runtime for the stack
// overflow.
Label done;
// If the frame is bigger than the stack, we throw the stack overflow
// exception unconditionally. Thereby we can avoid the integer overflow
// check in the condition code.
if ((required_slots * kSystemPointerSize) < (FLAG_stack_size * 1024)) {
__ Ld(
kScratchReg,
FieldMemOperand(kWasmInstanceRegister,
WasmInstanceObject::kRealStackLimitAddressOffset));
__ Ld(kScratchReg, MemOperand(kScratchReg));
__ Daddu(kScratchReg, kScratchReg,
Operand(required_slots * kSystemPointerSize));
__ Branch(&done, uge, sp, Operand(kScratchReg));
}
__ Call(wasm::WasmCode::kWasmStackOverflow, RelocInfo::WASM_STUB_CALL);
// We come from WebAssembly, there are no references for the GC.
ReferenceMap* reference_map = zone()->New<ReferenceMap>(zone());
RecordSafepoint(reference_map, Safepoint::kNoLazyDeopt);
if (FLAG_debug_code) {
__ stop();
}
__ bind(&done);
}
}
const int returns = frame()->GetReturnSlotCount();
// Skip callee-saved and return slots, which are pushed below.
required_slots -= base::bits::CountPopulation(saves);
required_slots -= base::bits::CountPopulation(saves_fpu);
required_slots -= returns;
if (required_slots > 0) {
__ Dsubu(sp, sp, Operand(required_slots * kSystemPointerSize));
}
if (saves_fpu != 0) {
// Save callee-saved FPU registers.
__ MultiPushFPU(saves_fpu);
DCHECK_EQ(kNumCalleeSavedFPU, base::bits::CountPopulation(saves_fpu));
}
if (saves != 0) {
// Save callee-saved registers.
__ MultiPush(saves);
DCHECK_EQ(kNumCalleeSaved, base::bits::CountPopulation(saves) + 1);
}
if (returns != 0) {
// Create space for returns.
__ Dsubu(sp, sp, Operand(returns * kSystemPointerSize));
}
}
void CodeGenerator::AssembleReturn(InstructionOperand* additional_pop_count) {
auto call_descriptor = linkage()->GetIncomingDescriptor();
const int returns = frame()->GetReturnSlotCount();
if (returns != 0) {
__ Daddu(sp, sp, Operand(returns * kSystemPointerSize));
}
// Restore GP registers.
const RegList saves = call_descriptor->CalleeSavedRegisters();
if (saves != 0) {
__ MultiPop(saves);
}
// Restore FPU registers.
const RegList saves_fpu = call_descriptor->CalleeSavedFPRegisters();
if (saves_fpu != 0) {
__ MultiPopFPU(saves_fpu);
}
MipsOperandConverter g(this, nullptr);
const int parameter_count =
static_cast<int>(call_descriptor->StackParameterCount());
// {aditional_pop_count} is only greater than zero if {parameter_count = 0}.
// Check RawMachineAssembler::PopAndReturn.
if (parameter_count != 0) {
if (additional_pop_count->IsImmediate()) {
DCHECK_EQ(g.ToConstant(additional_pop_count).ToInt32(), 0);
} else if (__ emit_debug_code()) {
__ Assert(eq, AbortReason::kUnexpectedAdditionalPopValue,
g.ToRegister(additional_pop_count),
Operand(static_cast<int64_t>(0)));
}
}
#ifdef V8_NO_ARGUMENTS_ADAPTOR
// Functions with JS linkage have at least one parameter (the receiver).
// If {parameter_count} == 0, it means it is a builtin with
// kDontAdaptArgumentsSentinel, which takes care of JS arguments popping
// itself.
const bool drop_jsargs = frame_access_state()->has_frame() &&
call_descriptor->IsJSFunctionCall() &&
parameter_count != 0;
#else
const bool drop_jsargs = false;
#endif
if (call_descriptor->IsCFunctionCall()) {
AssembleDeconstructFrame();
} else if (frame_access_state()->has_frame()) {
// Canonicalize JSFunction return sites for now unless they have an variable
// number of stack slot pops.
if (additional_pop_count->IsImmediate() &&
g.ToConstant(additional_pop_count).ToInt32() == 0) {
if (return_label_.is_bound()) {
__ Branch(&return_label_);
return;
} else {
__ bind(&return_label_);
}
}
if (drop_jsargs) {
// Get the actual argument count
__ Ld(t0, MemOperand(fp, StandardFrameConstants::kArgCOffset));
}
AssembleDeconstructFrame();
}
if (drop_jsargs) {
// We must pop all arguments from the stack (including the receiver). This
// number of arguments is given by max(1 + argc_reg, parameter_count).
__ Daddu(t0, t0, Operand(1)); // Also pop the receiver.
if (parameter_count > 1) {
__ li(kScratchReg, parameter_count);
__ slt(kScratchReg2, t0, kScratchReg);
__ movn(t0, kScratchReg, kScratchReg2);
}
__ dsll(t0, t0, kSystemPointerSizeLog2);
__ Daddu(sp, sp, t0);
} else if (additional_pop_count->IsImmediate()) {
DCHECK_EQ(Constant::kInt32, g.ToConstant(additional_pop_count).type());
int additional_count = g.ToConstant(additional_pop_count).ToInt32();
__ Drop(parameter_count + additional_count);
} else {
Register pop_reg = g.ToRegister(additional_pop_count);
__ Drop(parameter_count);
__ dsll(pop_reg, pop_reg, kSystemPointerSizeLog2);
__ Daddu(sp, sp, pop_reg);
}
__ Ret();
}
void CodeGenerator::FinishCode() {}
void CodeGenerator::PrepareForDeoptimizationExits(
ZoneDeque<DeoptimizationExit*>* exits) {}
void CodeGenerator::AssembleMove(InstructionOperand* source,
InstructionOperand* destination) {
MipsOperandConverter g(this, nullptr);
// Dispatch on the source and destination operand kinds. Not all
// combinations are possible.
if (source->IsRegister()) {
DCHECK(destination->IsRegister() || destination->IsStackSlot());
Register src = g.ToRegister(source);
if (destination->IsRegister()) {
__ mov(g.ToRegister(destination), src);
} else {
__ Sd(src, g.ToMemOperand(destination));
}
} else if (source->IsStackSlot()) {
DCHECK(destination->IsRegister() || destination->IsStackSlot());
MemOperand src = g.ToMemOperand(source);
if (destination->IsRegister()) {
__ Ld(g.ToRegister(destination), src);
} else {
Register temp = kScratchReg;
__ Ld(temp, src);
__ Sd(temp, g.ToMemOperand(destination));
}
} else if (source->IsConstant()) {
Constant src = g.ToConstant(source);
if (destination->IsRegister() || destination->IsStackSlot()) {
Register dst =
destination->IsRegister() ? g.ToRegister(destination) : kScratchReg;
switch (src.type()) {
case Constant::kInt32:
__ li(dst, Operand(src.ToInt32()));
break;
case Constant::kFloat32:
__ li(dst, Operand::EmbeddedNumber(src.ToFloat32()));
break;
case Constant::kInt64:
if (RelocInfo::IsWasmReference(src.rmode())) {
__ li(dst, Operand(src.ToInt64(), src.rmode()));
} else {
__ li(dst, Operand(src.ToInt64()));
}
break;
case Constant::kFloat64:
__ li(dst, Operand::EmbeddedNumber(src.ToFloat64().value()));
break;
case Constant::kExternalReference:
__ li(dst, src.ToExternalReference());
break;
case Constant::kDelayedStringConstant:
__ li(dst, src.ToDelayedStringConstant());
break;
case Constant::kHeapObject: {
Handle<HeapObject> src_object = src.ToHeapObject();
RootIndex index;
if (IsMaterializableFromRoot(src_object, &index)) {
__ LoadRoot(dst, index);
} else {
__ li(dst, src_object);
}
break;
}
case Constant::kCompressedHeapObject:
UNREACHABLE();
case Constant::kRpoNumber:
UNREACHABLE(); // TODO(titzer): loading RPO numbers on mips64.
break;
}
if (destination->IsStackSlot()) __ Sd(dst, g.ToMemOperand(destination));
} else if (src.type() == Constant::kFloat32) {
if (destination->IsFPStackSlot()) {
MemOperand dst = g.ToMemOperand(destination);
if (bit_cast<int32_t>(src.ToFloat32()) == 0) {
__ Sd(zero_reg, dst);
} else {
__ li(kScratchReg, Operand(bit_cast<int32_t>(src.ToFloat32())));
__ Sd(kScratchReg, dst);
}
} else {
DCHECK(destination->IsFPRegister());
FloatRegister dst = g.ToSingleRegister(destination);
__ Move(dst, src.ToFloat32());
}
} else {
DCHECK_EQ(Constant::kFloat64, src.type());
DoubleRegister dst = destination->IsFPRegister()
? g.ToDoubleRegister(destination)
: kScratchDoubleReg;
__ Move(dst, src.ToFloat64().value());
if (destination->IsFPStackSlot()) {
__ Sdc1(dst, g.ToMemOperand(destination));
}
}
} else if (source->IsFPRegister()) {
MachineRepresentation rep = LocationOperand::cast(source)->representation();
if (rep == MachineRepresentation::kSimd128) {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
MSARegister src = g.ToSimd128Register(source);
if (destination->IsSimd128Register()) {
MSARegister dst = g.ToSimd128Register(destination);
__ move_v(dst, src);
} else {
DCHECK(destination->IsSimd128StackSlot());
__ st_b(src, g.ToMemOperand(destination));
}
} else {
FPURegister src = g.ToDoubleRegister(source);
if (destination->IsFPRegister()) {
FPURegister dst = g.ToDoubleRegister(destination);
__ Move(dst, src);
} else {
DCHECK(destination->IsFPStackSlot());
__ Sdc1(src, g.ToMemOperand(destination));
}
}
} else if (source->IsFPStackSlot()) {
DCHECK(destination->IsFPRegister() || destination->IsFPStackSlot());
MemOperand src = g.ToMemOperand(source);
MachineRepresentation rep = LocationOperand::cast(source)->representation();
if (rep == MachineRepresentation::kSimd128) {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
if (destination->IsSimd128Register()) {
__ ld_b(g.ToSimd128Register(destination), src);
} else {
DCHECK(destination->IsSimd128StackSlot());
MSARegister temp = kSimd128ScratchReg;
__ ld_b(temp, src);
__ st_b(temp, g.ToMemOperand(destination));
}
} else {
if (destination->IsFPRegister()) {
__ Ldc1(g.ToDoubleRegister(destination), src);
} else {
DCHECK(destination->IsFPStackSlot());
FPURegister temp = kScratchDoubleReg;
__ Ldc1(temp, src);
__ Sdc1(temp, g.ToMemOperand(destination));
}
}
} else {
UNREACHABLE();
}
}
void CodeGenerator::AssembleSwap(InstructionOperand* source,
InstructionOperand* destination) {
MipsOperandConverter g(this, nullptr);
// Dispatch on the source and destination operand kinds. Not all
// combinations are possible.
if (source->IsRegister()) {
// Register-register.
Register temp = kScratchReg;
Register src = g.ToRegister(source);
if (destination->IsRegister()) {
Register dst = g.ToRegister(destination);
__ Move(temp, src);
__ Move(src, dst);
__ Move(dst, temp);
} else {
DCHECK(destination->IsStackSlot());
MemOperand dst = g.ToMemOperand(destination);
__ mov(temp, src);
__ Ld(src, dst);
__ Sd(temp, dst);
}
} else if (source->IsStackSlot()) {
DCHECK(destination->IsStackSlot());
Register temp_0 = kScratchReg;
Register temp_1 = kScratchReg2;
MemOperand src = g.ToMemOperand(source);
MemOperand dst = g.ToMemOperand(destination);
__ Ld(temp_0, src);
__ Ld(temp_1, dst);
__ Sd(temp_0, dst);
__ Sd(temp_1, src);
} else if (source->IsFPRegister()) {
MachineRepresentation rep = LocationOperand::cast(source)->representation();
if (rep == MachineRepresentation::kSimd128) {
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
MSARegister temp = kSimd128ScratchReg;
MSARegister src = g.ToSimd128Register(source);
if (destination->IsSimd128Register()) {
MSARegister dst = g.ToSimd128Register(destination);
__ move_v(temp, src);
__ move_v(src, dst);
__ move_v(dst, temp);
} else {
DCHECK(destination->IsSimd128StackSlot());
MemOperand dst = g.ToMemOperand(destination);
__ move_v(temp, src);
__ ld_b(src, dst);
__ st_b(temp, dst);
}
} else {
FPURegister temp = kScratchDoubleReg;
FPURegister src = g.ToDoubleRegister(source);
if (destination->IsFPRegister()) {
FPURegister dst = g.ToDoubleRegister(destination);
__ Move(temp, src);
__ Move(src, dst);
__ Move(dst, temp);
} else {
DCHECK(destination->IsFPStackSlot());
MemOperand dst = g.ToMemOperand(destination);
__ Move(temp, src);
__ Ldc1(src, dst);
__ Sdc1(temp, dst);
}
}
} else if (source->IsFPStackSlot()) {
DCHECK(destination->IsFPStackSlot());
Register temp_0 = kScratchReg;
MemOperand src0 = g.ToMemOperand(source);
MemOperand src1(src0.rm(), src0.offset() + kIntSize);
MemOperand dst0 = g.ToMemOperand(destination);
MemOperand dst1(dst0.rm(), dst0.offset() + kIntSize);
MachineRepresentation rep = LocationOperand::cast(source)->representation();
if (rep == MachineRepresentation::kSimd128) {
MemOperand src2(src0.rm(), src0.offset() + 2 * kIntSize);
MemOperand src3(src0.rm(), src0.offset() + 3 * kIntSize);
MemOperand dst2(dst0.rm(), dst0.offset() + 2 * kIntSize);
MemOperand dst3(dst0.rm(), dst0.offset() + 3 * kIntSize);
CpuFeatureScope msa_scope(tasm(), MIPS_SIMD);
MSARegister temp_1 = kSimd128ScratchReg;
__ ld_b(temp_1, dst0); // Save destination in temp_1.
__ Lw(temp_0, src0); // Then use temp_0 to copy source to destination.
__ Sw(temp_0, dst0);
__ Lw(temp_0, src1);
__ Sw(temp_0, dst1);
__ Lw(temp_0, src2);
__ Sw(temp_0, dst2);
__ Lw(temp_0, src3);
__ Sw(temp_0, dst3);
__ st_b(temp_1, src0);
} else {
FPURegister temp_1 = kScratchDoubleReg;
__ Ldc1(temp_1, dst0); // Save destination in temp_1.
__ Lw(temp_0, src0); // Then use temp_0 to copy source to destination.
__ Sw(temp_0, dst0);
__ Lw(temp_0, src1);
__ Sw(temp_0, dst1);
__ Sdc1(temp_1, src0);
}
} else {
// No other combinations are possible.
UNREACHABLE();
}
}
void CodeGenerator::AssembleJumpTable(Label** targets, size_t target_count) {
// On 64-bit MIPS we emit the jump tables inline.
UNREACHABLE();
}
#undef ASSEMBLE_ATOMIC_LOAD_INTEGER
#undef ASSEMBLE_ATOMIC_STORE_INTEGER
#undef ASSEMBLE_ATOMIC_BINOP
#undef ASSEMBLE_ATOMIC_BINOP_EXT
#undef ASSEMBLE_ATOMIC_EXCHANGE_INTEGER
#undef ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT
#undef ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER
#undef ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER_EXT
#undef ASSEMBLE_IEEE754_BINOP
#undef ASSEMBLE_IEEE754_UNOP
#undef TRACE_MSG
#undef TRACE_UNIMPL
#undef __
} // namespace compiler
} // namespace internal
} // namespace v8