| // Copyright 2014 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "src/assembler-inl.h" |
| #include "src/callable.h" |
| #include "src/compilation-info.h" |
| #include "src/compiler/code-generator-impl.h" |
| #include "src/compiler/code-generator.h" |
| #include "src/compiler/gap-resolver.h" |
| #include "src/compiler/node-matchers.h" |
| #include "src/compiler/osr.h" |
| #include "src/heap/heap-inl.h" |
| #include "src/mips/macro-assembler-mips.h" |
| |
| namespace v8 { |
| namespace internal { |
| namespace compiler { |
| |
| #define __ tasm()-> |
| |
| // TODO(plind): Possibly avoid using these lithium names. |
| #define kScratchReg kLithiumScratchReg |
| #define kCompareReg kLithiumScratchReg2 |
| #define kScratchReg2 kLithiumScratchReg2 |
| #define kScratchDoubleReg kLithiumScratchDouble |
| |
| |
| // TODO(plind): consider renaming these macros. |
| #define TRACE_MSG(msg) \ |
| PrintF("code_gen: \'%s\' in function %s at line %d\n", msg, __FUNCTION__, \ |
| __LINE__) |
| |
| #define TRACE_UNIMPL() \ |
| PrintF("UNIMPLEMENTED code_generator_mips: %s at line %d\n", __FUNCTION__, \ |
| __LINE__) |
| |
| |
| // Adds Mips-specific methods to convert InstructionOperands. |
| class MipsOperandConverter final : public InstructionOperandConverter { |
| public: |
| MipsOperandConverter(CodeGenerator* gen, Instruction* instr) |
| : InstructionOperandConverter(gen, instr) {} |
| |
| FloatRegister OutputSingleRegister(size_t index = 0) { |
| return ToSingleRegister(instr_->OutputAt(index)); |
| } |
| |
| FloatRegister InputSingleRegister(size_t index) { |
| return ToSingleRegister(instr_->InputAt(index)); |
| } |
| |
| FloatRegister ToSingleRegister(InstructionOperand* op) { |
| // Single (Float) and Double register namespace is same on MIPS, |
| // both are typedefs of FPURegister. |
| return ToDoubleRegister(op); |
| } |
| |
| Register InputOrZeroRegister(size_t index) { |
| if (instr_->InputAt(index)->IsImmediate()) { |
| DCHECK_EQ(0, InputInt32(index)); |
| return zero_reg; |
| } |
| return InputRegister(index); |
| } |
| |
| DoubleRegister InputOrZeroDoubleRegister(size_t index) { |
| if (instr_->InputAt(index)->IsImmediate()) return kDoubleRegZero; |
| |
| return InputDoubleRegister(index); |
| } |
| |
| DoubleRegister InputOrZeroSingleRegister(size_t index) { |
| if (instr_->InputAt(index)->IsImmediate()) return kDoubleRegZero; |
| |
| return InputSingleRegister(index); |
| } |
| |
| Operand InputImmediate(size_t index) { |
| Constant constant = ToConstant(instr_->InputAt(index)); |
| switch (constant.type()) { |
| case Constant::kInt32: |
| return Operand(constant.ToInt32()); |
| case Constant::kFloat32: |
| return Operand::EmbeddedNumber(constant.ToFloat32()); |
| case Constant::kFloat64: |
| return Operand::EmbeddedNumber(constant.ToFloat64().value()); |
| case Constant::kInt64: |
| case Constant::kExternalReference: |
| case Constant::kHeapObject: |
| // TODO(plind): Maybe we should handle ExtRef & HeapObj here? |
| // maybe not done on arm due to const pool ?? |
| break; |
| case Constant::kRpoNumber: |
| UNREACHABLE(); // TODO(titzer): RPO immediates on mips? |
| break; |
| } |
| UNREACHABLE(); |
| } |
| |
| Operand InputOperand(size_t index) { |
| InstructionOperand* op = instr_->InputAt(index); |
| if (op->IsRegister()) { |
| return Operand(ToRegister(op)); |
| } |
| return InputImmediate(index); |
| } |
| |
| MemOperand MemoryOperand(size_t* first_index) { |
| const size_t index = *first_index; |
| switch (AddressingModeField::decode(instr_->opcode())) { |
| case kMode_None: |
| break; |
| case kMode_MRI: |
| *first_index += 2; |
| return MemOperand(InputRegister(index + 0), InputInt32(index + 1)); |
| case kMode_MRR: |
| // TODO(plind): r6 address mode, to be implemented ... |
| UNREACHABLE(); |
| } |
| UNREACHABLE(); |
| } |
| |
| MemOperand MemoryOperand(size_t index = 0) { return MemoryOperand(&index); } |
| |
| MemOperand ToMemOperand(InstructionOperand* op) const { |
| DCHECK_NOT_NULL(op); |
| DCHECK(op->IsStackSlot() || op->IsFPStackSlot()); |
| return SlotToMemOperand(AllocatedOperand::cast(op)->index()); |
| } |
| |
| MemOperand SlotToMemOperand(int slot) const { |
| FrameOffset offset = frame_access_state()->GetFrameOffset(slot); |
| return MemOperand(offset.from_stack_pointer() ? sp : fp, offset.offset()); |
| } |
| }; |
| |
| |
| static inline bool HasRegisterInput(Instruction* instr, size_t index) { |
| return instr->InputAt(index)->IsRegister(); |
| } |
| |
| |
| namespace { |
| |
| class OutOfLineRound : public OutOfLineCode { |
| public: |
| OutOfLineRound(CodeGenerator* gen, DoubleRegister result) |
| : OutOfLineCode(gen), result_(result) {} |
| |
| void Generate() final { |
| // Handle rounding to zero case where sign has to be preserved. |
| // High bits of double input already in kScratchReg. |
| __ srl(at, kScratchReg, 31); |
| __ sll(at, at, 31); |
| __ Mthc1(at, result_); |
| } |
| |
| private: |
| DoubleRegister const result_; |
| }; |
| |
| |
| class OutOfLineRound32 : public OutOfLineCode { |
| public: |
| OutOfLineRound32(CodeGenerator* gen, DoubleRegister result) |
| : OutOfLineCode(gen), result_(result) {} |
| |
| void Generate() final { |
| // Handle rounding to zero case where sign has to be preserved. |
| // High bits of float input already in kScratchReg. |
| __ srl(at, kScratchReg, 31); |
| __ sll(at, at, 31); |
| __ mtc1(at, result_); |
| } |
| |
| private: |
| DoubleRegister const result_; |
| }; |
| |
| |
| class OutOfLineRecordWrite final : public OutOfLineCode { |
| public: |
| OutOfLineRecordWrite(CodeGenerator* gen, Register object, Register index, |
| Register value, Register scratch0, Register scratch1, |
| RecordWriteMode mode) |
| : OutOfLineCode(gen), |
| object_(object), |
| index_(index), |
| value_(value), |
| scratch0_(scratch0), |
| scratch1_(scratch1), |
| mode_(mode), |
| must_save_lr_(!gen->frame_access_state()->has_frame()), |
| zone_(gen->zone()) {} |
| |
| void SaveRegisters(RegList registers) { |
| DCHECK_LT(0, NumRegs(registers)); |
| RegList regs = 0; |
| for (int i = 0; i < Register::kNumRegisters; ++i) { |
| if ((registers >> i) & 1u) { |
| regs |= Register::from_code(i).bit(); |
| } |
| } |
| __ MultiPush(regs | ra.bit()); |
| } |
| |
| void RestoreRegisters(RegList registers) { |
| DCHECK_LT(0, NumRegs(registers)); |
| RegList regs = 0; |
| for (int i = 0; i < Register::kNumRegisters; ++i) { |
| if ((registers >> i) & 1u) { |
| regs |= Register::from_code(i).bit(); |
| } |
| } |
| __ MultiPop(regs | ra.bit()); |
| } |
| |
| void Generate() final { |
| if (mode_ > RecordWriteMode::kValueIsPointer) { |
| __ JumpIfSmi(value_, exit()); |
| } |
| __ CheckPageFlag(value_, scratch0_, |
| MemoryChunk::kPointersToHereAreInterestingMask, eq, |
| exit()); |
| __ Addu(scratch1_, object_, index_); |
| RememberedSetAction const remembered_set_action = |
| mode_ > RecordWriteMode::kValueIsMap ? EMIT_REMEMBERED_SET |
| : OMIT_REMEMBERED_SET; |
| SaveFPRegsMode const save_fp_mode = |
| frame()->DidAllocateDoubleRegisters() ? kSaveFPRegs : kDontSaveFPRegs; |
| if (must_save_lr_) { |
| // We need to save and restore ra if the frame was elided. |
| __ Push(ra); |
| } |
| __ CallRecordWriteStub(object_, scratch1_, remembered_set_action, |
| save_fp_mode); |
| if (must_save_lr_) { |
| __ Pop(ra); |
| } |
| } |
| |
| private: |
| Register const object_; |
| Register const index_; |
| Register const value_; |
| Register const scratch0_; |
| Register const scratch1_; |
| RecordWriteMode const mode_; |
| bool must_save_lr_; |
| Zone* zone_; |
| }; |
| |
| #define CREATE_OOL_CLASS(ool_name, tasm_ool_name, T) \ |
| class ool_name final : public OutOfLineCode { \ |
| public: \ |
| ool_name(CodeGenerator* gen, T dst, T src1, T src2) \ |
| : OutOfLineCode(gen), dst_(dst), src1_(src1), src2_(src2) {} \ |
| \ |
| void Generate() final { __ tasm_ool_name(dst_, src1_, src2_); } \ |
| \ |
| private: \ |
| T const dst_; \ |
| T const src1_; \ |
| T const src2_; \ |
| } |
| |
| CREATE_OOL_CLASS(OutOfLineFloat32Max, Float32MaxOutOfLine, FPURegister); |
| CREATE_OOL_CLASS(OutOfLineFloat32Min, Float32MinOutOfLine, FPURegister); |
| CREATE_OOL_CLASS(OutOfLineFloat64Max, Float64MaxOutOfLine, DoubleRegister); |
| CREATE_OOL_CLASS(OutOfLineFloat64Min, Float64MinOutOfLine, DoubleRegister); |
| |
| #undef CREATE_OOL_CLASS |
| |
| Condition FlagsConditionToConditionCmp(FlagsCondition condition) { |
| switch (condition) { |
| case kEqual: |
| return eq; |
| case kNotEqual: |
| return ne; |
| case kSignedLessThan: |
| return lt; |
| case kSignedGreaterThanOrEqual: |
| return ge; |
| case kSignedLessThanOrEqual: |
| return le; |
| case kSignedGreaterThan: |
| return gt; |
| case kUnsignedLessThan: |
| return lo; |
| case kUnsignedGreaterThanOrEqual: |
| return hs; |
| case kUnsignedLessThanOrEqual: |
| return ls; |
| case kUnsignedGreaterThan: |
| return hi; |
| case kUnorderedEqual: |
| case kUnorderedNotEqual: |
| break; |
| default: |
| break; |
| } |
| UNREACHABLE(); |
| } |
| |
| |
| Condition FlagsConditionToConditionTst(FlagsCondition condition) { |
| switch (condition) { |
| case kNotEqual: |
| return ne; |
| case kEqual: |
| return eq; |
| default: |
| break; |
| } |
| UNREACHABLE(); |
| } |
| |
| |
| FPUCondition FlagsConditionToConditionCmpFPU(bool& predicate, |
| FlagsCondition condition) { |
| switch (condition) { |
| case kEqual: |
| predicate = true; |
| return EQ; |
| case kNotEqual: |
| predicate = false; |
| return EQ; |
| case kUnsignedLessThan: |
| predicate = true; |
| return OLT; |
| case kUnsignedGreaterThanOrEqual: |
| predicate = false; |
| return ULT; |
| case kUnsignedLessThanOrEqual: |
| predicate = true; |
| return OLE; |
| case kUnsignedGreaterThan: |
| predicate = false; |
| return ULE; |
| case kUnorderedEqual: |
| case kUnorderedNotEqual: |
| predicate = true; |
| break; |
| default: |
| predicate = true; |
| break; |
| } |
| UNREACHABLE(); |
| } |
| |
| } // namespace |
| |
| #define ASSEMBLE_ROUND_DOUBLE_TO_DOUBLE(mode) \ |
| if (IsMipsArchVariant(kMips32r6)) { \ |
| __ cfc1(kScratchReg, FCSR); \ |
| __ li(at, Operand(mode_##mode)); \ |
| __ ctc1(at, FCSR); \ |
| __ rint_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0)); \ |
| __ ctc1(kScratchReg, FCSR); \ |
| } else { \ |
| auto ool = new (zone()) OutOfLineRound(this, i.OutputDoubleRegister()); \ |
| Label done; \ |
| __ Mfhc1(kScratchReg, i.InputDoubleRegister(0)); \ |
| __ Ext(at, kScratchReg, HeapNumber::kExponentShift, \ |
| HeapNumber::kExponentBits); \ |
| __ Branch(USE_DELAY_SLOT, &done, hs, at, \ |
| Operand(HeapNumber::kExponentBias + HeapNumber::kMantissaBits)); \ |
| __ mov_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0)); \ |
| __ mode##_l_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0)); \ |
| __ Move(at, kScratchReg2, i.OutputDoubleRegister()); \ |
| __ or_(at, at, kScratchReg2); \ |
| __ Branch(USE_DELAY_SLOT, ool->entry(), eq, at, Operand(zero_reg)); \ |
| __ cvt_d_l(i.OutputDoubleRegister(), i.OutputDoubleRegister()); \ |
| __ bind(ool->exit()); \ |
| __ bind(&done); \ |
| } |
| |
| |
| #define ASSEMBLE_ROUND_FLOAT_TO_FLOAT(mode) \ |
| if (IsMipsArchVariant(kMips32r6)) { \ |
| __ cfc1(kScratchReg, FCSR); \ |
| __ li(at, Operand(mode_##mode)); \ |
| __ ctc1(at, FCSR); \ |
| __ rint_s(i.OutputDoubleRegister(), i.InputDoubleRegister(0)); \ |
| __ ctc1(kScratchReg, FCSR); \ |
| } else { \ |
| int32_t kFloat32ExponentBias = 127; \ |
| int32_t kFloat32MantissaBits = 23; \ |
| int32_t kFloat32ExponentBits = 8; \ |
| auto ool = new (zone()) OutOfLineRound32(this, i.OutputDoubleRegister()); \ |
| Label done; \ |
| __ mfc1(kScratchReg, i.InputDoubleRegister(0)); \ |
| __ Ext(at, kScratchReg, kFloat32MantissaBits, kFloat32ExponentBits); \ |
| __ Branch(USE_DELAY_SLOT, &done, hs, at, \ |
| Operand(kFloat32ExponentBias + kFloat32MantissaBits)); \ |
| __ mov_s(i.OutputDoubleRegister(), i.InputDoubleRegister(0)); \ |
| __ mode##_w_s(i.OutputDoubleRegister(), i.InputDoubleRegister(0)); \ |
| __ mfc1(at, i.OutputDoubleRegister()); \ |
| __ Branch(USE_DELAY_SLOT, ool->entry(), eq, at, Operand(zero_reg)); \ |
| __ cvt_s_w(i.OutputDoubleRegister(), i.OutputDoubleRegister()); \ |
| __ bind(ool->exit()); \ |
| __ bind(&done); \ |
| } |
| |
| #define ASSEMBLE_ATOMIC_LOAD_INTEGER(asm_instr) \ |
| do { \ |
| __ asm_instr(i.OutputRegister(), i.MemoryOperand()); \ |
| __ sync(); \ |
| } while (0) |
| |
| #define ASSEMBLE_ATOMIC_STORE_INTEGER(asm_instr) \ |
| do { \ |
| __ sync(); \ |
| __ asm_instr(i.InputOrZeroRegister(2), i.MemoryOperand()); \ |
| __ sync(); \ |
| } while (0) |
| |
| #define ASSEMBLE_ATOMIC_BINOP(bin_instr) \ |
| do { \ |
| Label binop; \ |
| __ Addu(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1)); \ |
| __ sync(); \ |
| __ bind(&binop); \ |
| __ Ll(i.OutputRegister(0), MemOperand(i.TempRegister(0), 0)); \ |
| __ bin_instr(i.TempRegister(1), i.OutputRegister(0), \ |
| Operand(i.InputRegister(2))); \ |
| __ Sc(i.TempRegister(1), MemOperand(i.TempRegister(0), 0)); \ |
| __ BranchShort(&binop, eq, i.TempRegister(1), Operand(zero_reg)); \ |
| __ sync(); \ |
| } while (0) |
| |
| #define ASSEMBLE_ATOMIC_BINOP_EXT(sign_extend, size, bin_instr) \ |
| do { \ |
| Label binop; \ |
| __ Addu(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1)); \ |
| __ andi(i.TempRegister(3), i.TempRegister(0), 0x3); \ |
| __ Subu(i.TempRegister(0), i.TempRegister(0), Operand(i.TempRegister(3))); \ |
| __ sll(i.TempRegister(3), i.TempRegister(3), 3); \ |
| __ sync(); \ |
| __ bind(&binop); \ |
| __ Ll(i.TempRegister(1), MemOperand(i.TempRegister(0), 0)); \ |
| __ ExtractBits(i.OutputRegister(0), i.TempRegister(1), i.TempRegister(3), \ |
| size, sign_extend); \ |
| __ bin_instr(i.TempRegister(2), i.OutputRegister(0), \ |
| Operand(i.InputRegister(2))); \ |
| __ InsertBits(i.TempRegister(1), i.TempRegister(2), i.TempRegister(3), \ |
| size); \ |
| __ Sc(i.TempRegister(1), MemOperand(i.TempRegister(0), 0)); \ |
| __ BranchShort(&binop, eq, i.TempRegister(1), Operand(zero_reg)); \ |
| __ sync(); \ |
| } while (0) |
| |
| #define ASSEMBLE_ATOMIC_EXCHANGE_INTEGER() \ |
| do { \ |
| Label exchange; \ |
| __ sync(); \ |
| __ bind(&exchange); \ |
| __ Addu(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1)); \ |
| __ Ll(i.OutputRegister(0), MemOperand(i.TempRegister(0), 0)); \ |
| __ mov(i.TempRegister(1), i.InputRegister(2)); \ |
| __ Sc(i.TempRegister(1), MemOperand(i.TempRegister(0), 0)); \ |
| __ BranchShort(&exchange, eq, i.TempRegister(1), Operand(zero_reg)); \ |
| __ sync(); \ |
| } while (0) |
| |
| #define ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT(sign_extend, size) \ |
| do { \ |
| Label exchange; \ |
| __ Addu(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1)); \ |
| __ andi(i.TempRegister(1), i.TempRegister(0), 0x3); \ |
| __ Subu(i.TempRegister(0), i.TempRegister(0), Operand(i.TempRegister(1))); \ |
| __ sll(i.TempRegister(1), i.TempRegister(1), 3); \ |
| __ sync(); \ |
| __ bind(&exchange); \ |
| __ Ll(i.TempRegister(2), MemOperand(i.TempRegister(0), 0)); \ |
| __ ExtractBits(i.OutputRegister(0), i.TempRegister(2), i.TempRegister(1), \ |
| size, sign_extend); \ |
| __ InsertBits(i.TempRegister(2), i.InputRegister(2), i.TempRegister(1), \ |
| size); \ |
| __ Sc(i.TempRegister(2), MemOperand(i.TempRegister(0), 0)); \ |
| __ BranchShort(&exchange, eq, i.TempRegister(2), Operand(zero_reg)); \ |
| __ sync(); \ |
| } while (0) |
| |
| #define ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER() \ |
| do { \ |
| Label compareExchange; \ |
| Label exit; \ |
| __ Addu(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1)); \ |
| __ sync(); \ |
| __ bind(&compareExchange); \ |
| __ Ll(i.OutputRegister(0), MemOperand(i.TempRegister(0), 0)); \ |
| __ BranchShort(&exit, ne, i.InputRegister(2), \ |
| Operand(i.OutputRegister(0))); \ |
| __ mov(i.TempRegister(2), i.InputRegister(3)); \ |
| __ Sc(i.TempRegister(2), MemOperand(i.TempRegister(0), 0)); \ |
| __ BranchShort(&compareExchange, eq, i.TempRegister(2), \ |
| Operand(zero_reg)); \ |
| __ bind(&exit); \ |
| __ sync(); \ |
| } while (0) |
| |
| #define ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER_EXT(sign_extend, size) \ |
| do { \ |
| Label compareExchange; \ |
| Label exit; \ |
| __ Addu(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1)); \ |
| __ andi(i.TempRegister(1), i.TempRegister(0), 0x3); \ |
| __ Subu(i.TempRegister(0), i.TempRegister(0), Operand(i.TempRegister(1))); \ |
| __ sll(i.TempRegister(1), i.TempRegister(1), 3); \ |
| __ sync(); \ |
| __ bind(&compareExchange); \ |
| __ Ll(i.TempRegister(2), MemOperand(i.TempRegister(0), 0)); \ |
| __ ExtractBits(i.OutputRegister(0), i.TempRegister(2), i.TempRegister(1), \ |
| size, sign_extend); \ |
| __ BranchShort(&exit, ne, i.InputRegister(2), \ |
| Operand(i.OutputRegister(0))); \ |
| __ InsertBits(i.TempRegister(2), i.InputRegister(3), i.TempRegister(1), \ |
| size); \ |
| __ Sc(i.TempRegister(2), MemOperand(i.TempRegister(0), 0)); \ |
| __ BranchShort(&compareExchange, eq, i.TempRegister(2), \ |
| Operand(zero_reg)); \ |
| __ bind(&exit); \ |
| __ sync(); \ |
| } while (0) |
| |
| #define ASSEMBLE_IEEE754_BINOP(name) \ |
| do { \ |
| FrameScope scope(tasm(), StackFrame::MANUAL); \ |
| __ PrepareCallCFunction(0, 2, kScratchReg); \ |
| __ MovToFloatParameters(i.InputDoubleRegister(0), \ |
| i.InputDoubleRegister(1)); \ |
| __ CallCFunction(ExternalReference::ieee754_##name##_function(isolate()), \ |
| 0, 2); \ |
| /* Move the result in the double result register. */ \ |
| __ MovFromFloatResult(i.OutputDoubleRegister()); \ |
| } while (0) |
| |
| #define ASSEMBLE_IEEE754_UNOP(name) \ |
| do { \ |
| FrameScope scope(tasm(), StackFrame::MANUAL); \ |
| __ PrepareCallCFunction(0, 1, kScratchReg); \ |
| __ MovToFloatParameter(i.InputDoubleRegister(0)); \ |
| __ CallCFunction(ExternalReference::ieee754_##name##_function(isolate()), \ |
| 0, 1); \ |
| /* Move the result in the double result register. */ \ |
| __ MovFromFloatResult(i.OutputDoubleRegister()); \ |
| } while (0) |
| |
| void CodeGenerator::AssembleDeconstructFrame() { |
| __ mov(sp, fp); |
| __ Pop(ra, fp); |
| } |
| |
| void CodeGenerator::AssemblePrepareTailCall() { |
| if (frame_access_state()->has_frame()) { |
| __ lw(ra, MemOperand(fp, StandardFrameConstants::kCallerPCOffset)); |
| __ lw(fp, MemOperand(fp, StandardFrameConstants::kCallerFPOffset)); |
| } |
| frame_access_state()->SetFrameAccessToSP(); |
| } |
| |
| void CodeGenerator::AssemblePopArgumentsAdaptorFrame(Register args_reg, |
| Register scratch1, |
| Register scratch2, |
| Register scratch3) { |
| DCHECK(!AreAliased(args_reg, scratch1, scratch2, scratch3)); |
| Label done; |
| |
| // Check if current frame is an arguments adaptor frame. |
| __ lw(scratch1, MemOperand(fp, StandardFrameConstants::kContextOffset)); |
| __ Branch(&done, ne, scratch1, |
| Operand(StackFrame::TypeToMarker(StackFrame::ARGUMENTS_ADAPTOR))); |
| |
| // Load arguments count from current arguments adaptor frame (note, it |
| // does not include receiver). |
| Register caller_args_count_reg = scratch1; |
| __ lw(caller_args_count_reg, |
| MemOperand(fp, ArgumentsAdaptorFrameConstants::kLengthOffset)); |
| __ SmiUntag(caller_args_count_reg); |
| |
| ParameterCount callee_args_count(args_reg); |
| __ PrepareForTailCall(callee_args_count, caller_args_count_reg, scratch2, |
| scratch3); |
| __ bind(&done); |
| } |
| |
| namespace { |
| |
| void AdjustStackPointerForTailCall(TurboAssembler* tasm, |
| FrameAccessState* state, |
| int new_slot_above_sp, |
| bool allow_shrinkage = true) { |
| int current_sp_offset = state->GetSPToFPSlotCount() + |
| StandardFrameConstants::kFixedSlotCountAboveFp; |
| int stack_slot_delta = new_slot_above_sp - current_sp_offset; |
| if (stack_slot_delta > 0) { |
| tasm->Subu(sp, sp, stack_slot_delta * kPointerSize); |
| state->IncreaseSPDelta(stack_slot_delta); |
| } else if (allow_shrinkage && stack_slot_delta < 0) { |
| tasm->Addu(sp, sp, -stack_slot_delta * kPointerSize); |
| state->IncreaseSPDelta(stack_slot_delta); |
| } |
| } |
| |
| } // namespace |
| |
| void CodeGenerator::AssembleTailCallBeforeGap(Instruction* instr, |
| int first_unused_stack_slot) { |
| AdjustStackPointerForTailCall(tasm(), frame_access_state(), |
| first_unused_stack_slot, false); |
| } |
| |
| void CodeGenerator::AssembleTailCallAfterGap(Instruction* instr, |
| int first_unused_stack_slot) { |
| AdjustStackPointerForTailCall(tasm(), frame_access_state(), |
| first_unused_stack_slot); |
| } |
| |
| // Check if the code object is marked for deoptimization. If it is, then it |
| // jumps to the CompileLazyDeoptimizedCode builtin. In order to do this we need |
| // to: |
| // 1. load the address of the current instruction; |
| // 2. read from memory the word that contains that bit, which can be found in |
| // the flags in the referenced {CodeDataContainer} object; |
| // 3. test kMarkedForDeoptimizationBit in those flags; and |
| // 4. if it is not zero then it jumps to the builtin. |
| void CodeGenerator::BailoutIfDeoptimized() { |
| Label current; |
| // This push on ra and the pop below together ensure that we restore the |
| // register ra, which is needed while computing frames for deoptimization. |
| __ push(ra); |
| // The bal instruction puts the address of the current instruction into |
| // the return address (ra) register, which we can use later on. |
| __ bal(¤t); |
| __ nop(); |
| int pc = __ pc_offset(); |
| __ bind(¤t); |
| int offset = Code::kCodeDataContainerOffset - (Code::kHeaderSize + pc); |
| __ lw(a2, MemOperand(ra, offset)); |
| __ pop(ra); |
| __ lw(a2, FieldMemOperand(a2, CodeDataContainer::kKindSpecificFlagsOffset)); |
| __ And(a2, a2, Operand(1 << Code::kMarkedForDeoptimizationBit)); |
| Handle<Code> code = isolate()->builtins()->builtin_handle( |
| Builtins::kCompileLazyDeoptimizedCode); |
| __ Jump(code, RelocInfo::CODE_TARGET, ne, a2, Operand(zero_reg)); |
| } |
| |
| // Assembles an instruction after register allocation, producing machine code. |
| CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction( |
| Instruction* instr) { |
| MipsOperandConverter i(this, instr); |
| InstructionCode opcode = instr->opcode(); |
| ArchOpcode arch_opcode = ArchOpcodeField::decode(opcode); |
| switch (arch_opcode) { |
| case kArchCallCodeObject: { |
| if (instr->InputAt(0)->IsImmediate()) { |
| __ Call(i.InputCode(0), RelocInfo::CODE_TARGET); |
| } else { |
| __ Call(at, i.InputRegister(0), Code::kHeaderSize - kHeapObjectTag); |
| } |
| RecordCallPosition(instr); |
| frame_access_state()->ClearSPDelta(); |
| break; |
| } |
| case kArchCallWasmFunction: { |
| if (instr->InputAt(0)->IsImmediate()) { |
| Address wasm_code = reinterpret_cast<Address>( |
| i.ToConstant(instr->InputAt(0)).ToInt32()); |
| __ Call(wasm_code, info()->IsWasm() ? RelocInfo::WASM_CALL |
| : RelocInfo::JS_TO_WASM_CALL); |
| } else { |
| __ Call(i.InputRegister(0)); |
| } |
| RecordCallPosition(instr); |
| frame_access_state()->ClearSPDelta(); |
| break; |
| } |
| case kArchTailCallCodeObjectFromJSFunction: |
| case kArchTailCallCodeObject: { |
| if (arch_opcode == kArchTailCallCodeObjectFromJSFunction) { |
| AssemblePopArgumentsAdaptorFrame(kJavaScriptCallArgCountRegister, |
| i.TempRegister(0), i.TempRegister(1), |
| i.TempRegister(2)); |
| } |
| if (instr->InputAt(0)->IsImmediate()) { |
| __ Jump(i.InputCode(0), RelocInfo::CODE_TARGET); |
| } else { |
| __ Jump(at, i.InputRegister(0), Code::kHeaderSize - kHeapObjectTag); |
| } |
| frame_access_state()->ClearSPDelta(); |
| frame_access_state()->SetFrameAccessToDefault(); |
| break; |
| } |
| case kArchTailCallWasm: { |
| if (instr->InputAt(0)->IsImmediate()) { |
| Address wasm_code = reinterpret_cast<Address>( |
| i.ToConstant(instr->InputAt(0)).ToInt32()); |
| __ Jump(wasm_code, info()->IsWasm() ? RelocInfo::WASM_CALL |
| : RelocInfo::JS_TO_WASM_CALL); |
| } else { |
| __ Jump(i.InputRegister(0)); |
| } |
| frame_access_state()->ClearSPDelta(); |
| frame_access_state()->SetFrameAccessToDefault(); |
| break; |
| } |
| case kArchTailCallAddress: { |
| CHECK(!instr->InputAt(0)->IsImmediate()); |
| __ Jump(i.InputRegister(0)); |
| frame_access_state()->ClearSPDelta(); |
| frame_access_state()->SetFrameAccessToDefault(); |
| break; |
| } |
| case kArchCallJSFunction: { |
| Register func = i.InputRegister(0); |
| if (FLAG_debug_code) { |
| // Check the function's context matches the context argument. |
| __ lw(kScratchReg, FieldMemOperand(func, JSFunction::kContextOffset)); |
| __ Assert(eq, AbortReason::kWrongFunctionContext, cp, |
| Operand(kScratchReg)); |
| } |
| __ lw(at, FieldMemOperand(func, JSFunction::kCodeOffset)); |
| __ Call(at, Code::kHeaderSize - kHeapObjectTag); |
| RecordCallPosition(instr); |
| frame_access_state()->ClearSPDelta(); |
| frame_access_state()->SetFrameAccessToDefault(); |
| break; |
| } |
| case kArchPrepareCallCFunction: { |
| int const num_parameters = MiscField::decode(instr->opcode()); |
| __ PrepareCallCFunction(num_parameters, kScratchReg); |
| // Frame alignment requires using FP-relative frame addressing. |
| frame_access_state()->SetFrameAccessToFP(); |
| break; |
| } |
| case kArchSaveCallerRegisters: { |
| fp_mode_ = |
| static_cast<SaveFPRegsMode>(MiscField::decode(instr->opcode())); |
| DCHECK(fp_mode_ == kDontSaveFPRegs || fp_mode_ == kSaveFPRegs); |
| // kReturnRegister0 should have been saved before entering the stub. |
| int bytes = __ PushCallerSaved(fp_mode_, kReturnRegister0); |
| DCHECK_EQ(0, bytes % kPointerSize); |
| DCHECK_EQ(0, frame_access_state()->sp_delta()); |
| frame_access_state()->IncreaseSPDelta(bytes / kPointerSize); |
| DCHECK(!caller_registers_saved_); |
| caller_registers_saved_ = true; |
| break; |
| } |
| case kArchRestoreCallerRegisters: { |
| DCHECK(fp_mode_ == |
| static_cast<SaveFPRegsMode>(MiscField::decode(instr->opcode()))); |
| DCHECK(fp_mode_ == kDontSaveFPRegs || fp_mode_ == kSaveFPRegs); |
| // Don't overwrite the returned value. |
| int bytes = __ PopCallerSaved(fp_mode_, kReturnRegister0); |
| frame_access_state()->IncreaseSPDelta(-(bytes / kPointerSize)); |
| DCHECK_EQ(0, frame_access_state()->sp_delta()); |
| DCHECK(caller_registers_saved_); |
| caller_registers_saved_ = false; |
| break; |
| } |
| case kArchPrepareTailCall: |
| AssemblePrepareTailCall(); |
| break; |
| case kArchCallCFunction: { |
| int const num_parameters = MiscField::decode(instr->opcode()); |
| if (instr->InputAt(0)->IsImmediate()) { |
| ExternalReference ref = i.InputExternalReference(0); |
| __ CallCFunction(ref, num_parameters); |
| } else { |
| Register func = i.InputRegister(0); |
| __ CallCFunction(func, num_parameters); |
| } |
| frame_access_state()->SetFrameAccessToDefault(); |
| // Ideally, we should decrement SP delta to match the change of stack |
| // pointer in CallCFunction. However, for certain architectures (e.g. |
| // ARM), there may be more strict alignment requirement, causing old SP |
| // to be saved on the stack. In those cases, we can not calculate the SP |
| // delta statically. |
| frame_access_state()->ClearSPDelta(); |
| if (caller_registers_saved_) { |
| // Need to re-sync SP delta introduced in kArchSaveCallerRegisters. |
| // Here, we assume the sequence to be: |
| // kArchSaveCallerRegisters; |
| // kArchCallCFunction; |
| // kArchRestoreCallerRegisters; |
| int bytes = |
| __ RequiredStackSizeForCallerSaved(fp_mode_, kReturnRegister0); |
| frame_access_state()->IncreaseSPDelta(bytes / kPointerSize); |
| } |
| break; |
| } |
| case kArchJmp: |
| AssembleArchJump(i.InputRpo(0)); |
| break; |
| case kArchLookupSwitch: |
| AssembleArchLookupSwitch(instr); |
| break; |
| case kArchTableSwitch: |
| AssembleArchTableSwitch(instr); |
| break; |
| case kArchDebugAbort: |
| DCHECK(i.InputRegister(0) == a0); |
| if (!frame_access_state()->has_frame()) { |
| // We don't actually want to generate a pile of code for this, so just |
| // claim there is a stack frame, without generating one. |
| FrameScope scope(tasm(), StackFrame::NONE); |
| __ Call(isolate()->builtins()->builtin_handle(Builtins::kAbortJS), |
| RelocInfo::CODE_TARGET); |
| } else { |
| __ Call(isolate()->builtins()->builtin_handle(Builtins::kAbortJS), |
| RelocInfo::CODE_TARGET); |
| } |
| __ stop("kArchDebugAbort"); |
| break; |
| case kArchDebugBreak: |
| __ stop("kArchDebugBreak"); |
| break; |
| case kArchComment: { |
| Address comment_string = i.InputExternalReference(0).address(); |
| __ RecordComment(reinterpret_cast<const char*>(comment_string)); |
| break; |
| } |
| case kArchNop: |
| case kArchThrowTerminator: |
| // don't emit code for nops. |
| break; |
| case kArchDeoptimize: { |
| int deopt_state_id = |
| BuildTranslation(instr, -1, 0, OutputFrameStateCombine::Ignore()); |
| CodeGenResult result = |
| AssembleDeoptimizerCall(deopt_state_id, current_source_position_); |
| if (result != kSuccess) return result; |
| break; |
| } |
| case kArchRet: |
| AssembleReturn(instr->InputAt(0)); |
| break; |
| case kArchStackPointer: |
| __ mov(i.OutputRegister(), sp); |
| break; |
| case kArchFramePointer: |
| __ mov(i.OutputRegister(), fp); |
| break; |
| case kArchParentFramePointer: |
| if (frame_access_state()->has_frame()) { |
| __ lw(i.OutputRegister(), MemOperand(fp, 0)); |
| } else { |
| __ mov(i.OutputRegister(), fp); |
| } |
| break; |
| case kArchTruncateDoubleToI: |
| __ TruncateDoubleToIDelayed(zone(), i.OutputRegister(), |
| i.InputDoubleRegister(0)); |
| break; |
| case kArchStoreWithWriteBarrier: { |
| RecordWriteMode mode = |
| static_cast<RecordWriteMode>(MiscField::decode(instr->opcode())); |
| Register object = i.InputRegister(0); |
| Register index = i.InputRegister(1); |
| Register value = i.InputRegister(2); |
| Register scratch0 = i.TempRegister(0); |
| Register scratch1 = i.TempRegister(1); |
| auto ool = new (zone()) OutOfLineRecordWrite(this, object, index, value, |
| scratch0, scratch1, mode); |
| __ Addu(at, object, index); |
| __ sw(value, MemOperand(at)); |
| __ CheckPageFlag(object, scratch0, |
| MemoryChunk::kPointersFromHereAreInterestingMask, ne, |
| ool->entry()); |
| __ bind(ool->exit()); |
| break; |
| } |
| case kArchStackSlot: { |
| FrameOffset offset = |
| frame_access_state()->GetFrameOffset(i.InputInt32(0)); |
| Register base_reg = offset.from_stack_pointer() ? sp : fp; |
| __ Addu(i.OutputRegister(), base_reg, Operand(offset.offset())); |
| int alignment = i.InputInt32(1); |
| DCHECK(alignment == 0 || alignment == 4 || alignment == 8 || |
| alignment == 16); |
| if (FLAG_debug_code && alignment > 0) { |
| // Verify that the output_register is properly aligned |
| __ And(kScratchReg, i.OutputRegister(), Operand(kPointerSize - 1)); |
| __ Assert(eq, AbortReason::kAllocationIsNotDoubleAligned, kScratchReg, |
| Operand(zero_reg)); |
| } |
| |
| if (alignment == 2 * kPointerSize) { |
| Label done; |
| __ Addu(kScratchReg, base_reg, Operand(offset.offset())); |
| __ And(kScratchReg, kScratchReg, Operand(alignment - 1)); |
| __ BranchShort(&done, eq, kScratchReg, Operand(zero_reg)); |
| __ Addu(i.OutputRegister(), i.OutputRegister(), kPointerSize); |
| __ bind(&done); |
| } else if (alignment > 2 * kPointerSize) { |
| Label done; |
| __ Addu(kScratchReg, base_reg, Operand(offset.offset())); |
| __ And(kScratchReg, kScratchReg, Operand(alignment - 1)); |
| __ BranchShort(&done, eq, kScratchReg, Operand(zero_reg)); |
| __ li(kScratchReg2, alignment); |
| __ Subu(kScratchReg2, kScratchReg2, Operand(kScratchReg)); |
| __ Addu(i.OutputRegister(), i.OutputRegister(), kScratchReg2); |
| __ bind(&done); |
| } |
| break; |
| } |
| case kIeee754Float64Acos: |
| ASSEMBLE_IEEE754_UNOP(acos); |
| break; |
| case kIeee754Float64Acosh: |
| ASSEMBLE_IEEE754_UNOP(acosh); |
| break; |
| case kIeee754Float64Asin: |
| ASSEMBLE_IEEE754_UNOP(asin); |
| break; |
| case kIeee754Float64Asinh: |
| ASSEMBLE_IEEE754_UNOP(asinh); |
| break; |
| case kIeee754Float64Atan: |
| ASSEMBLE_IEEE754_UNOP(atan); |
| break; |
| case kIeee754Float64Atanh: |
| ASSEMBLE_IEEE754_UNOP(atanh); |
| break; |
| case kIeee754Float64Atan2: |
| ASSEMBLE_IEEE754_BINOP(atan2); |
| break; |
| case kIeee754Float64Cos: |
| ASSEMBLE_IEEE754_UNOP(cos); |
| break; |
| case kIeee754Float64Cosh: |
| ASSEMBLE_IEEE754_UNOP(cosh); |
| break; |
| case kIeee754Float64Cbrt: |
| ASSEMBLE_IEEE754_UNOP(cbrt); |
| break; |
| case kIeee754Float64Exp: |
| ASSEMBLE_IEEE754_UNOP(exp); |
| break; |
| case kIeee754Float64Expm1: |
| ASSEMBLE_IEEE754_UNOP(expm1); |
| break; |
| case kIeee754Float64Log: |
| ASSEMBLE_IEEE754_UNOP(log); |
| break; |
| case kIeee754Float64Log1p: |
| ASSEMBLE_IEEE754_UNOP(log1p); |
| break; |
| case kIeee754Float64Log10: |
| ASSEMBLE_IEEE754_UNOP(log10); |
| break; |
| case kIeee754Float64Log2: |
| ASSEMBLE_IEEE754_UNOP(log2); |
| break; |
| case kIeee754Float64Pow: { |
| __ CallStubDelayed(new (zone()) |
| MathPowStub(nullptr, MathPowStub::DOUBLE)); |
| break; |
| } |
| case kIeee754Float64Sin: |
| ASSEMBLE_IEEE754_UNOP(sin); |
| break; |
| case kIeee754Float64Sinh: |
| ASSEMBLE_IEEE754_UNOP(sinh); |
| break; |
| case kIeee754Float64Tan: |
| ASSEMBLE_IEEE754_UNOP(tan); |
| break; |
| case kIeee754Float64Tanh: |
| ASSEMBLE_IEEE754_UNOP(tanh); |
| break; |
| case kMipsAdd: |
| __ Addu(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); |
| break; |
| case kMipsAddOvf: |
| // Pseudo-instruction used for overflow/branch. No opcode emitted here. |
| break; |
| case kMipsSub: |
| __ Subu(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); |
| break; |
| case kMipsSubOvf: |
| // Pseudo-instruction used for overflow/branch. No opcode emitted here. |
| break; |
| case kMipsMul: |
| __ Mul(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); |
| break; |
| case kMipsMulOvf: |
| // Pseudo-instruction used for overflow/branch. No opcode emitted here. |
| break; |
| case kMipsMulHigh: |
| __ Mulh(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); |
| break; |
| case kMipsMulHighU: |
| __ Mulhu(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); |
| break; |
| case kMipsDiv: |
| __ Div(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); |
| if (IsMipsArchVariant(kMips32r6)) { |
| __ selnez(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1)); |
| } else { |
| __ Movz(i.OutputRegister(), i.InputRegister(1), i.InputRegister(1)); |
| } |
| break; |
| case kMipsDivU: |
| __ Divu(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); |
| if (IsMipsArchVariant(kMips32r6)) { |
| __ selnez(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1)); |
| } else { |
| __ Movz(i.OutputRegister(), i.InputRegister(1), i.InputRegister(1)); |
| } |
| break; |
| case kMipsMod: |
| __ Mod(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); |
| break; |
| case kMipsModU: |
| __ Modu(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); |
| break; |
| case kMipsAnd: |
| __ And(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); |
| break; |
| case kMipsOr: |
| __ Or(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); |
| break; |
| case kMipsNor: |
| if (instr->InputAt(1)->IsRegister()) { |
| __ Nor(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); |
| } else { |
| DCHECK_EQ(0, i.InputOperand(1).immediate()); |
| __ Nor(i.OutputRegister(), i.InputRegister(0), zero_reg); |
| } |
| break; |
| case kMipsXor: |
| __ Xor(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); |
| break; |
| case kMipsClz: |
| __ Clz(i.OutputRegister(), i.InputRegister(0)); |
| break; |
| case kMipsCtz: { |
| Register src = i.InputRegister(0); |
| Register dst = i.OutputRegister(); |
| if (IsMipsArchVariant(kMips32r6)) { |
| // We don't have an instruction to count the number of trailing zeroes. |
| // Start by flipping the bits end-for-end so we can count the number of |
| // leading zeroes instead. |
| __ Ror(dst, src, 16); |
| __ wsbh(dst, dst); |
| __ bitswap(dst, dst); |
| __ Clz(dst, dst); |
| } else { |
| // Convert trailing zeroes to trailing ones, and bits to their left |
| // to zeroes. |
| __ Addu(kScratchReg, src, -1); |
| __ Xor(dst, kScratchReg, src); |
| __ And(dst, dst, kScratchReg); |
| // Count number of leading zeroes. |
| __ Clz(dst, dst); |
| // Subtract number of leading zeroes from 32 to get number of trailing |
| // ones. Remember that the trailing ones were formerly trailing zeroes. |
| __ li(kScratchReg, 32); |
| __ Subu(dst, kScratchReg, dst); |
| } |
| } break; |
| case kMipsPopcnt: { |
| // https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel |
| // |
| // A generalization of the best bit counting method to integers of |
| // bit-widths up to 128 (parameterized by type T) is this: |
| // |
| // v = v - ((v >> 1) & (T)~(T)0/3); // temp |
| // v = (v & (T)~(T)0/15*3) + ((v >> 2) & (T)~(T)0/15*3); // temp |
| // v = (v + (v >> 4)) & (T)~(T)0/255*15; // temp |
| // c = (T)(v * ((T)~(T)0/255)) >> (sizeof(T) - 1) * BITS_PER_BYTE; //count |
| // |
| // For comparison, for 32-bit quantities, this algorithm can be executed |
| // using 20 MIPS instructions (the calls to LoadConst32() generate two |
| // machine instructions each for the values being used in this algorithm). |
| // A(n unrolled) loop-based algorithm requires 25 instructions. |
| // |
| // For 64-bit quantities, this algorithm gets executed twice, (once |
| // for in_lo, and again for in_hi), but saves a few instructions |
| // because the mask values only have to be loaded once. Using this |
| // algorithm the count for a 64-bit operand can be performed in 29 |
| // instructions compared to a loop-based algorithm which requires 47 |
| // instructions. |
| Register src = i.InputRegister(0); |
| Register dst = i.OutputRegister(); |
| uint32_t B0 = 0x55555555; // (T)~(T)0/3 |
| uint32_t B1 = 0x33333333; // (T)~(T)0/15*3 |
| uint32_t B2 = 0x0F0F0F0F; // (T)~(T)0/255*15 |
| uint32_t value = 0x01010101; // (T)~(T)0/255 |
| uint32_t shift = 24; // (sizeof(T) - 1) * BITS_PER_BYTE |
| __ srl(kScratchReg, src, 1); |
| __ li(kScratchReg2, B0); |
| __ And(kScratchReg, kScratchReg, kScratchReg2); |
| __ Subu(kScratchReg, src, kScratchReg); |
| __ li(kScratchReg2, B1); |
| __ And(dst, kScratchReg, kScratchReg2); |
| __ srl(kScratchReg, kScratchReg, 2); |
| __ And(kScratchReg, kScratchReg, kScratchReg2); |
| __ Addu(kScratchReg, dst, kScratchReg); |
| __ srl(dst, kScratchReg, 4); |
| __ Addu(dst, dst, kScratchReg); |
| __ li(kScratchReg2, B2); |
| __ And(dst, dst, kScratchReg2); |
| __ li(kScratchReg, value); |
| __ Mul(dst, dst, kScratchReg); |
| __ srl(dst, dst, shift); |
| } break; |
| case kMipsShl: |
| if (instr->InputAt(1)->IsRegister()) { |
| __ sllv(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1)); |
| } else { |
| int32_t imm = i.InputOperand(1).immediate(); |
| __ sll(i.OutputRegister(), i.InputRegister(0), imm); |
| } |
| break; |
| case kMipsShr: |
| if (instr->InputAt(1)->IsRegister()) { |
| __ srlv(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1)); |
| } else { |
| int32_t imm = i.InputOperand(1).immediate(); |
| __ srl(i.OutputRegister(), i.InputRegister(0), imm); |
| } |
| break; |
| case kMipsSar: |
| if (instr->InputAt(1)->IsRegister()) { |
| __ srav(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1)); |
| } else { |
| int32_t imm = i.InputOperand(1).immediate(); |
| __ sra(i.OutputRegister(), i.InputRegister(0), imm); |
| } |
| break; |
| case kMipsShlPair: { |
| Register second_output = |
| instr->OutputCount() >= 2 ? i.OutputRegister(1) : i.TempRegister(0); |
| if (instr->InputAt(2)->IsRegister()) { |
| __ ShlPair(i.OutputRegister(0), second_output, i.InputRegister(0), |
| i.InputRegister(1), i.InputRegister(2)); |
| } else { |
| uint32_t imm = i.InputOperand(2).immediate(); |
| __ ShlPair(i.OutputRegister(0), second_output, i.InputRegister(0), |
| i.InputRegister(1), imm); |
| } |
| } break; |
| case kMipsShrPair: { |
| Register second_output = |
| instr->OutputCount() >= 2 ? i.OutputRegister(1) : i.TempRegister(0); |
| if (instr->InputAt(2)->IsRegister()) { |
| __ ShrPair(i.OutputRegister(0), second_output, i.InputRegister(0), |
| i.InputRegister(1), i.InputRegister(2)); |
| } else { |
| uint32_t imm = i.InputOperand(2).immediate(); |
| __ ShrPair(i.OutputRegister(0), second_output, i.InputRegister(0), |
| i.InputRegister(1), imm); |
| } |
| } break; |
| case kMipsSarPair: { |
| Register second_output = |
| instr->OutputCount() >= 2 ? i.OutputRegister(1) : i.TempRegister(0); |
| if (instr->InputAt(2)->IsRegister()) { |
| __ SarPair(i.OutputRegister(0), second_output, i.InputRegister(0), |
| i.InputRegister(1), i.InputRegister(2)); |
| } else { |
| uint32_t imm = i.InputOperand(2).immediate(); |
| __ SarPair(i.OutputRegister(0), second_output, i.InputRegister(0), |
| i.InputRegister(1), imm); |
| } |
| } break; |
| case kMipsExt: |
| __ Ext(i.OutputRegister(), i.InputRegister(0), i.InputInt8(1), |
| i.InputInt8(2)); |
| break; |
| case kMipsIns: |
| if (instr->InputAt(1)->IsImmediate() && i.InputInt8(1) == 0) { |
| __ Ins(i.OutputRegister(), zero_reg, i.InputInt8(1), i.InputInt8(2)); |
| } else { |
| __ Ins(i.OutputRegister(), i.InputRegister(0), i.InputInt8(1), |
| i.InputInt8(2)); |
| } |
| break; |
| case kMipsRor: |
| __ Ror(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); |
| break; |
| case kMipsTst: |
| // Pseudo-instruction used for tst/branch. No opcode emitted here. |
| break; |
| case kMipsCmp: |
| // Pseudo-instruction used for cmp/branch. No opcode emitted here. |
| break; |
| case kMipsMov: |
| // TODO(plind): Should we combine mov/li like this, or use separate instr? |
| // - Also see x64 ASSEMBLE_BINOP & RegisterOrOperandType |
| if (HasRegisterInput(instr, 0)) { |
| __ mov(i.OutputRegister(), i.InputRegister(0)); |
| } else { |
| __ li(i.OutputRegister(), i.InputOperand(0)); |
| } |
| break; |
| case kMipsLsa: |
| DCHECK(instr->InputAt(2)->IsImmediate()); |
| __ Lsa(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1), |
| i.InputInt8(2)); |
| break; |
| case kMipsCmpS: |
| // Pseudo-instruction used for FP cmp/branch. No opcode emitted here. |
| break; |
| case kMipsAddS: |
| // TODO(plind): add special case: combine mult & add. |
| __ add_s(i.OutputDoubleRegister(), i.InputDoubleRegister(0), |
| i.InputDoubleRegister(1)); |
| break; |
| case kMipsSubS: |
| __ sub_s(i.OutputDoubleRegister(), i.InputDoubleRegister(0), |
| i.InputDoubleRegister(1)); |
| break; |
| case kMipsMulS: |
| // TODO(plind): add special case: right op is -1.0, see arm port. |
| __ mul_s(i.OutputDoubleRegister(), i.InputDoubleRegister(0), |
| i.InputDoubleRegister(1)); |
| break; |
| case kMipsDivS: |
| __ div_s(i.OutputDoubleRegister(), i.InputDoubleRegister(0), |
| i.InputDoubleRegister(1)); |
| break; |
| case kMipsModS: { |
| // TODO(bmeurer): We should really get rid of this special instruction, |
| // and generate a CallAddress instruction instead. |
| FrameScope scope(tasm(), StackFrame::MANUAL); |
| __ PrepareCallCFunction(0, 2, kScratchReg); |
| __ MovToFloatParameters(i.InputDoubleRegister(0), |
| i.InputDoubleRegister(1)); |
| // TODO(balazs.kilvady): implement mod_two_floats_operation(isolate()) |
| __ CallCFunction(ExternalReference::mod_two_doubles_operation(isolate()), |
| 0, 2); |
| // Move the result in the double result register. |
| __ MovFromFloatResult(i.OutputSingleRegister()); |
| break; |
| } |
| case kMipsAbsS: |
| __ abs_s(i.OutputSingleRegister(), i.InputSingleRegister(0)); |
| break; |
| case kMipsSqrtS: { |
| __ sqrt_s(i.OutputDoubleRegister(), i.InputDoubleRegister(0)); |
| break; |
| } |
| case kMipsMaxS: |
| __ max_s(i.OutputDoubleRegister(), i.InputDoubleRegister(0), |
| i.InputDoubleRegister(1)); |
| break; |
| case kMipsMinS: |
| __ min_s(i.OutputDoubleRegister(), i.InputDoubleRegister(0), |
| i.InputDoubleRegister(1)); |
| break; |
| case kMipsCmpD: |
| // Pseudo-instruction used for FP cmp/branch. No opcode emitted here. |
| break; |
| case kMipsAddPair: |
| __ AddPair(i.OutputRegister(0), i.OutputRegister(1), i.InputRegister(0), |
| i.InputRegister(1), i.InputRegister(2), i.InputRegister(3)); |
| break; |
| case kMipsSubPair: |
| __ SubPair(i.OutputRegister(0), i.OutputRegister(1), i.InputRegister(0), |
| i.InputRegister(1), i.InputRegister(2), i.InputRegister(3)); |
| break; |
| case kMipsMulPair: { |
| __ Mulu(i.OutputRegister(1), i.OutputRegister(0), i.InputRegister(0), |
| i.InputRegister(2)); |
| __ Mul(kScratchReg, i.InputRegister(0), i.InputRegister(3)); |
| __ Mul(kScratchReg2, i.InputRegister(1), i.InputRegister(2)); |
| __ Addu(i.OutputRegister(1), i.OutputRegister(1), kScratchReg); |
| __ Addu(i.OutputRegister(1), i.OutputRegister(1), kScratchReg2); |
| } break; |
| case kMipsAddD: |
| // TODO(plind): add special case: combine mult & add. |
| __ add_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0), |
| i.InputDoubleRegister(1)); |
| break; |
| case kMipsSubD: |
| __ sub_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0), |
| i.InputDoubleRegister(1)); |
| break; |
| case kMipsMaddS: |
| __ Madd_s(i.OutputFloatRegister(), i.InputFloatRegister(0), |
| i.InputFloatRegister(1), i.InputFloatRegister(2), |
| kScratchDoubleReg); |
| break; |
| case kMipsMaddD: |
| __ Madd_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0), |
| i.InputDoubleRegister(1), i.InputDoubleRegister(2), |
| kScratchDoubleReg); |
| break; |
| case kMipsMsubS: |
| __ Msub_s(i.OutputFloatRegister(), i.InputFloatRegister(0), |
| i.InputFloatRegister(1), i.InputFloatRegister(2), |
| kScratchDoubleReg); |
| break; |
| case kMipsMsubD: |
| __ Msub_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0), |
| i.InputDoubleRegister(1), i.InputDoubleRegister(2), |
| kScratchDoubleReg); |
| break; |
| case kMipsMulD: |
| // TODO(plind): add special case: right op is -1.0, see arm port. |
| __ mul_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0), |
| i.InputDoubleRegister(1)); |
| break; |
| case kMipsDivD: |
| __ div_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0), |
| i.InputDoubleRegister(1)); |
| break; |
| case kMipsModD: { |
| // TODO(bmeurer): We should really get rid of this special instruction, |
| // and generate a CallAddress instruction instead. |
| FrameScope scope(tasm(), StackFrame::MANUAL); |
| __ PrepareCallCFunction(0, 2, kScratchReg); |
| __ MovToFloatParameters(i.InputDoubleRegister(0), |
| i.InputDoubleRegister(1)); |
| __ CallCFunction(ExternalReference::mod_two_doubles_operation(isolate()), |
| 0, 2); |
| // Move the result in the double result register. |
| __ MovFromFloatResult(i.OutputDoubleRegister()); |
| break; |
| } |
| case kMipsAbsD: |
| __ abs_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0)); |
| break; |
| case kMipsNegS: |
| __ Neg_s(i.OutputSingleRegister(), i.InputSingleRegister(0)); |
| break; |
| case kMipsNegD: |
| __ Neg_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0)); |
| break; |
| case kMipsSqrtD: { |
| __ sqrt_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0)); |
| break; |
| } |
| case kMipsMaxD: |
| __ max_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0), |
| i.InputDoubleRegister(1)); |
| break; |
| case kMipsMinD: |
| __ min_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0), |
| i.InputDoubleRegister(1)); |
| break; |
| case kMipsFloat64RoundDown: { |
| ASSEMBLE_ROUND_DOUBLE_TO_DOUBLE(floor); |
| break; |
| } |
| case kMipsFloat32RoundDown: { |
| ASSEMBLE_ROUND_FLOAT_TO_FLOAT(floor); |
| break; |
| } |
| case kMipsFloat64RoundTruncate: { |
| ASSEMBLE_ROUND_DOUBLE_TO_DOUBLE(trunc); |
| break; |
| } |
| case kMipsFloat32RoundTruncate: { |
| ASSEMBLE_ROUND_FLOAT_TO_FLOAT(trunc); |
| break; |
| } |
| case kMipsFloat64RoundUp: { |
| ASSEMBLE_ROUND_DOUBLE_TO_DOUBLE(ceil); |
| break; |
| } |
| case kMipsFloat32RoundUp: { |
| ASSEMBLE_ROUND_FLOAT_TO_FLOAT(ceil); |
| break; |
| } |
| case kMipsFloat64RoundTiesEven: { |
| ASSEMBLE_ROUND_DOUBLE_TO_DOUBLE(round); |
| break; |
| } |
| case kMipsFloat32RoundTiesEven: { |
| ASSEMBLE_ROUND_FLOAT_TO_FLOAT(round); |
| break; |
| } |
| case kMipsFloat32Max: { |
| FPURegister dst = i.OutputSingleRegister(); |
| FPURegister src1 = i.InputSingleRegister(0); |
| FPURegister src2 = i.InputSingleRegister(1); |
| auto ool = new (zone()) OutOfLineFloat32Max(this, dst, src1, src2); |
| __ Float32Max(dst, src1, src2, ool->entry()); |
| __ bind(ool->exit()); |
| break; |
| } |
| case kMipsFloat64Max: { |
| DoubleRegister dst = i.OutputDoubleRegister(); |
| DoubleRegister src1 = i.InputDoubleRegister(0); |
| DoubleRegister src2 = i.InputDoubleRegister(1); |
| auto ool = new (zone()) OutOfLineFloat64Max(this, dst, src1, src2); |
| __ Float64Max(dst, src1, src2, ool->entry()); |
| __ bind(ool->exit()); |
| break; |
| } |
| case kMipsFloat32Min: { |
| FPURegister dst = i.OutputSingleRegister(); |
| FPURegister src1 = i.InputSingleRegister(0); |
| FPURegister src2 = i.InputSingleRegister(1); |
| auto ool = new (zone()) OutOfLineFloat32Min(this, dst, src1, src2); |
| __ Float32Min(dst, src1, src2, ool->entry()); |
| __ bind(ool->exit()); |
| break; |
| } |
| case kMipsFloat64Min: { |
| DoubleRegister dst = i.OutputDoubleRegister(); |
| DoubleRegister src1 = i.InputDoubleRegister(0); |
| DoubleRegister src2 = i.InputDoubleRegister(1); |
| auto ool = new (zone()) OutOfLineFloat64Min(this, dst, src1, src2); |
| __ Float64Min(dst, src1, src2, ool->entry()); |
| __ bind(ool->exit()); |
| break; |
| } |
| case kMipsCvtSD: { |
| __ cvt_s_d(i.OutputSingleRegister(), i.InputDoubleRegister(0)); |
| break; |
| } |
| case kMipsCvtDS: { |
| __ cvt_d_s(i.OutputDoubleRegister(), i.InputSingleRegister(0)); |
| break; |
| } |
| case kMipsCvtDW: { |
| FPURegister scratch = kScratchDoubleReg; |
| __ mtc1(i.InputRegister(0), scratch); |
| __ cvt_d_w(i.OutputDoubleRegister(), scratch); |
| break; |
| } |
| case kMipsCvtSW: { |
| FPURegister scratch = kScratchDoubleReg; |
| __ mtc1(i.InputRegister(0), scratch); |
| __ cvt_s_w(i.OutputDoubleRegister(), scratch); |
| break; |
| } |
| case kMipsCvtSUw: { |
| FPURegister scratch = kScratchDoubleReg; |
| __ Cvt_d_uw(i.OutputDoubleRegister(), i.InputRegister(0), scratch); |
| __ cvt_s_d(i.OutputDoubleRegister(), i.OutputDoubleRegister()); |
| break; |
| } |
| case kMipsCvtDUw: { |
| FPURegister scratch = kScratchDoubleReg; |
| __ Cvt_d_uw(i.OutputDoubleRegister(), i.InputRegister(0), scratch); |
| break; |
| } |
| case kMipsFloorWD: { |
| FPURegister scratch = kScratchDoubleReg; |
| __ Floor_w_d(scratch, i.InputDoubleRegister(0)); |
| __ mfc1(i.OutputRegister(), scratch); |
| break; |
| } |
| case kMipsCeilWD: { |
| FPURegister scratch = kScratchDoubleReg; |
| __ Ceil_w_d(scratch, i.InputDoubleRegister(0)); |
| __ mfc1(i.OutputRegister(), scratch); |
| break; |
| } |
| case kMipsRoundWD: { |
| FPURegister scratch = kScratchDoubleReg; |
| __ Round_w_d(scratch, i.InputDoubleRegister(0)); |
| __ mfc1(i.OutputRegister(), scratch); |
| break; |
| } |
| case kMipsTruncWD: { |
| FPURegister scratch = kScratchDoubleReg; |
| // Other arches use round to zero here, so we follow. |
| __ Trunc_w_d(scratch, i.InputDoubleRegister(0)); |
| __ mfc1(i.OutputRegister(), scratch); |
| break; |
| } |
| case kMipsFloorWS: { |
| FPURegister scratch = kScratchDoubleReg; |
| __ floor_w_s(scratch, i.InputDoubleRegister(0)); |
| __ mfc1(i.OutputRegister(), scratch); |
| break; |
| } |
| case kMipsCeilWS: { |
| FPURegister scratch = kScratchDoubleReg; |
| __ ceil_w_s(scratch, i.InputDoubleRegister(0)); |
| __ mfc1(i.OutputRegister(), scratch); |
| break; |
| } |
| case kMipsRoundWS: { |
| FPURegister scratch = kScratchDoubleReg; |
| __ round_w_s(scratch, i.InputDoubleRegister(0)); |
| __ mfc1(i.OutputRegister(), scratch); |
| break; |
| } |
| case kMipsTruncWS: { |
| FPURegister scratch = kScratchDoubleReg; |
| __ trunc_w_s(scratch, i.InputDoubleRegister(0)); |
| __ mfc1(i.OutputRegister(), scratch); |
| // Avoid INT32_MAX as an overflow indicator and use INT32_MIN instead, |
| // because INT32_MIN allows easier out-of-bounds detection. |
| __ Addu(kScratchReg, i.OutputRegister(), 1); |
| __ Slt(kScratchReg2, kScratchReg, i.OutputRegister()); |
| __ Movn(i.OutputRegister(), kScratchReg, kScratchReg2); |
| break; |
| } |
| case kMipsTruncUwD: { |
| FPURegister scratch = kScratchDoubleReg; |
| // TODO(plind): Fix wrong param order of Trunc_uw_d() macro-asm function. |
| __ Trunc_uw_d(i.InputDoubleRegister(0), i.OutputRegister(), scratch); |
| break; |
| } |
| case kMipsTruncUwS: { |
| FPURegister scratch = kScratchDoubleReg; |
| // TODO(plind): Fix wrong param order of Trunc_uw_s() macro-asm function. |
| __ Trunc_uw_s(i.InputDoubleRegister(0), i.OutputRegister(), scratch); |
| // Avoid UINT32_MAX as an overflow indicator and use 0 instead, |
| // because 0 allows easier out-of-bounds detection. |
| __ Addu(kScratchReg, i.OutputRegister(), 1); |
| __ Movz(i.OutputRegister(), zero_reg, kScratchReg); |
| break; |
| } |
| case kMipsFloat64ExtractLowWord32: |
| __ FmoveLow(i.OutputRegister(), i.InputDoubleRegister(0)); |
| break; |
| case kMipsFloat64ExtractHighWord32: |
| __ FmoveHigh(i.OutputRegister(), i.InputDoubleRegister(0)); |
| break; |
| case kMipsFloat64InsertLowWord32: |
| __ FmoveLow(i.OutputDoubleRegister(), i.InputRegister(1)); |
| break; |
| case kMipsFloat64InsertHighWord32: |
| __ FmoveHigh(i.OutputDoubleRegister(), i.InputRegister(1)); |
| break; |
| case kMipsFloat64SilenceNaN: |
| __ FPUCanonicalizeNaN(i.OutputDoubleRegister(), i.InputDoubleRegister(0)); |
| break; |
| |
| // ... more basic instructions ... |
| case kMipsSeb: |
| __ Seb(i.OutputRegister(), i.InputRegister(0)); |
| break; |
| case kMipsSeh: |
| __ Seh(i.OutputRegister(), i.InputRegister(0)); |
| break; |
| case kMipsLbu: |
| __ lbu(i.OutputRegister(), i.MemoryOperand()); |
| break; |
| case kMipsLb: |
| __ lb(i.OutputRegister(), i.MemoryOperand()); |
| break; |
| case kMipsSb: |
| __ sb(i.InputOrZeroRegister(2), i.MemoryOperand()); |
| break; |
| case kMipsLhu: |
| __ lhu(i.OutputRegister(), i.MemoryOperand()); |
| break; |
| case kMipsUlhu: |
| __ Ulhu(i.OutputRegister(), i.MemoryOperand()); |
| break; |
| case kMipsLh: |
| __ lh(i.OutputRegister(), i.MemoryOperand()); |
| break; |
| case kMipsUlh: |
| __ Ulh(i.OutputRegister(), i.MemoryOperand()); |
| break; |
| case kMipsSh: |
| __ sh(i.InputOrZeroRegister(2), i.MemoryOperand()); |
| break; |
| case kMipsUsh: |
| __ Ush(i.InputOrZeroRegister(2), i.MemoryOperand(), kScratchReg); |
| break; |
| case kMipsLw: |
| __ lw(i.OutputRegister(), i.MemoryOperand()); |
| break; |
| case kMipsUlw: |
| __ Ulw(i.OutputRegister(), i.MemoryOperand()); |
| break; |
| case kMipsSw: |
| __ sw(i.InputOrZeroRegister(2), i.MemoryOperand()); |
| break; |
| case kMipsUsw: |
| __ Usw(i.InputOrZeroRegister(2), i.MemoryOperand()); |
| break; |
| case kMipsLwc1: { |
| __ lwc1(i.OutputSingleRegister(), i.MemoryOperand()); |
| break; |
| } |
| case kMipsUlwc1: { |
| __ Ulwc1(i.OutputSingleRegister(), i.MemoryOperand(), kScratchReg); |
| break; |
| } |
| case kMipsSwc1: { |
| size_t index = 0; |
| MemOperand operand = i.MemoryOperand(&index); |
| FPURegister ft = i.InputOrZeroSingleRegister(index); |
| if (ft == kDoubleRegZero && !__ IsDoubleZeroRegSet()) { |
| __ Move(kDoubleRegZero, 0.0); |
| } |
| __ swc1(ft, operand); |
| break; |
| } |
| case kMipsUswc1: { |
| size_t index = 0; |
| MemOperand operand = i.MemoryOperand(&index); |
| FPURegister ft = i.InputOrZeroSingleRegister(index); |
| if (ft == kDoubleRegZero && !__ IsDoubleZeroRegSet()) { |
| __ Move(kDoubleRegZero, 0.0); |
| } |
| __ Uswc1(ft, operand, kScratchReg); |
| break; |
| } |
| case kMipsLdc1: |
| __ Ldc1(i.OutputDoubleRegister(), i.MemoryOperand()); |
| break; |
| case kMipsUldc1: |
| __ Uldc1(i.OutputDoubleRegister(), i.MemoryOperand(), kScratchReg); |
| break; |
| case kMipsSdc1: { |
| FPURegister ft = i.InputOrZeroDoubleRegister(2); |
| if (ft == kDoubleRegZero && !__ IsDoubleZeroRegSet()) { |
| __ Move(kDoubleRegZero, 0.0); |
| } |
| __ Sdc1(ft, i.MemoryOperand()); |
| break; |
| } |
| case kMipsUsdc1: { |
| FPURegister ft = i.InputOrZeroDoubleRegister(2); |
| if (ft == kDoubleRegZero && !__ IsDoubleZeroRegSet()) { |
| __ Move(kDoubleRegZero, 0.0); |
| } |
| __ Usdc1(ft, i.MemoryOperand(), kScratchReg); |
| break; |
| } |
| case kMipsPush: |
| if (instr->InputAt(0)->IsFPRegister()) { |
| LocationOperand* op = LocationOperand::cast(instr->InputAt(0)); |
| switch (op->representation()) { |
| case MachineRepresentation::kFloat32: |
| __ swc1(i.InputFloatRegister(0), MemOperand(sp, -kFloatSize)); |
| __ Subu(sp, sp, Operand(kFloatSize)); |
| frame_access_state()->IncreaseSPDelta(kFloatSize / kPointerSize); |
| break; |
| case MachineRepresentation::kFloat64: |
| __ Sdc1(i.InputDoubleRegister(0), MemOperand(sp, -kDoubleSize)); |
| __ Subu(sp, sp, Operand(kDoubleSize)); |
| frame_access_state()->IncreaseSPDelta(kDoubleSize / kPointerSize); |
| break; |
| default: { |
| UNREACHABLE(); |
| break; |
| } |
| } |
| } else { |
| __ Push(i.InputRegister(0)); |
| frame_access_state()->IncreaseSPDelta(1); |
| } |
| break; |
| case kMipsPeek: { |
| int reverse_slot = MiscField::decode(instr->opcode()); |
| int offset = |
| FrameSlotToFPOffset(frame()->GetTotalFrameSlotCount() - reverse_slot); |
| if (instr->OutputAt(0)->IsFPRegister()) { |
| LocationOperand* op = LocationOperand::cast(instr->OutputAt(0)); |
| if (op->representation() == MachineRepresentation::kFloat64) { |
| __ Ldc1(i.OutputDoubleRegister(), MemOperand(fp, offset)); |
| } else { |
| DCHECK_EQ(op->representation(), MachineRepresentation::kFloat32); |
| __ lwc1(i.OutputSingleRegister(0), MemOperand(fp, offset)); |
| } |
| } else { |
| __ lw(i.OutputRegister(0), MemOperand(fp, offset)); |
| } |
| break; |
| } |
| case kMipsStackClaim: { |
| __ Subu(sp, sp, Operand(i.InputInt32(0))); |
| frame_access_state()->IncreaseSPDelta(i.InputInt32(0) / kPointerSize); |
| break; |
| } |
| case kMipsStoreToStackSlot: { |
| if (instr->InputAt(0)->IsFPRegister()) { |
| LocationOperand* op = LocationOperand::cast(instr->InputAt(0)); |
| if (op->representation() == MachineRepresentation::kFloat64) { |
| __ Sdc1(i.InputDoubleRegister(0), MemOperand(sp, i.InputInt32(1))); |
| } else { |
| DCHECK_EQ(MachineRepresentation::kFloat32, op->representation()); |
| __ swc1(i.InputSingleRegister(0), MemOperand(sp, i.InputInt32(1))); |
| } |
| } else { |
| __ sw(i.InputRegister(0), MemOperand(sp, i.InputInt32(1))); |
| } |
| break; |
| } |
| case kMipsByteSwap32: { |
| __ ByteSwapSigned(i.OutputRegister(0), i.InputRegister(0), 4); |
| break; |
| } |
| case kAtomicLoadInt8: |
| ASSEMBLE_ATOMIC_LOAD_INTEGER(lb); |
| break; |
| case kAtomicLoadUint8: |
| ASSEMBLE_ATOMIC_LOAD_INTEGER(lbu); |
| break; |
| case kAtomicLoadInt16: |
| ASSEMBLE_ATOMIC_LOAD_INTEGER(lh); |
| break; |
| case kAtomicLoadUint16: |
| ASSEMBLE_ATOMIC_LOAD_INTEGER(lhu); |
| break; |
| case kAtomicLoadWord32: |
| ASSEMBLE_ATOMIC_LOAD_INTEGER(lw); |
| break; |
| case kAtomicStoreWord8: |
| ASSEMBLE_ATOMIC_STORE_INTEGER(sb); |
| break; |
| case kAtomicStoreWord16: |
| ASSEMBLE_ATOMIC_STORE_INTEGER(sh); |
| break; |
| case kAtomicStoreWord32: |
| ASSEMBLE_ATOMIC_STORE_INTEGER(sw); |
| break; |
| case kAtomicExchangeInt8: |
| ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT(true, 8); |
| break; |
| case kAtomicExchangeUint8: |
| ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT(false, 8); |
| break; |
| case kAtomicExchangeInt16: |
| ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT(true, 16); |
| break; |
| case kAtomicExchangeUint16: |
| ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT(false, 16); |
| break; |
| case kAtomicExchangeWord32: |
| ASSEMBLE_ATOMIC_EXCHANGE_INTEGER(); |
| break; |
| case kAtomicCompareExchangeInt8: |
| ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER_EXT(true, 8); |
| break; |
| case kAtomicCompareExchangeUint8: |
| ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER_EXT(false, 8); |
| break; |
| case kAtomicCompareExchangeInt16: |
| ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER_EXT(true, 16); |
| break; |
| case kAtomicCompareExchangeUint16: |
| ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER_EXT(false, 16); |
| break; |
| case kAtomicCompareExchangeWord32: |
| ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER(); |
| break; |
| #define ATOMIC_BINOP_CASE(op, inst) \ |
| case kAtomic##op##Int8: \ |
| ASSEMBLE_ATOMIC_BINOP_EXT(true, 8, inst); \ |
| break; \ |
| case kAtomic##op##Uint8: \ |
| ASSEMBLE_ATOMIC_BINOP_EXT(false, 8, inst); \ |
| break; \ |
| case kAtomic##op##Int16: \ |
| ASSEMBLE_ATOMIC_BINOP_EXT(true, 16, inst); \ |
| break; \ |
| case kAtomic##op##Uint16: \ |
| ASSEMBLE_ATOMIC_BINOP_EXT(false, 16, inst); \ |
| break; \ |
| case kAtomic##op##Word32: \ |
| ASSEMBLE_ATOMIC_BINOP(inst); \ |
| break; |
| ATOMIC_BINOP_CASE(Add, Addu) |
| ATOMIC_BINOP_CASE(Sub, Subu) |
| ATOMIC_BINOP_CASE(And, And) |
| ATOMIC_BINOP_CASE(Or, Or) |
| ATOMIC_BINOP_CASE(Xor, Xor) |
| #undef ATOMIC_BINOP_CASE |
| case kMipsS128Zero: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ xor_v(i.OutputSimd128Register(), i.OutputSimd128Register(), |
| i.OutputSimd128Register()); |
| break; |
| } |
| case kMipsI32x4Splat: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ fill_w(i.OutputSimd128Register(), i.InputRegister(0)); |
| break; |
| } |
| case kMipsI32x4ExtractLane: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ copy_s_w(i.OutputRegister(), i.InputSimd128Register(0), |
| i.InputInt8(1)); |
| break; |
| } |
| case kMipsI32x4ReplaceLane: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| Simd128Register src = i.InputSimd128Register(0); |
| Simd128Register dst = i.OutputSimd128Register(); |
| if (src != dst) { |
| __ move_v(dst, src); |
| } |
| __ insert_w(dst, i.InputInt8(1), i.InputRegister(2)); |
| break; |
| } |
| case kMipsI32x4Add: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ addv_w(i.OutputSimd128Register(), i.InputSimd128Register(0), |
| i.InputSimd128Register(1)); |
| break; |
| } |
| case kMipsI32x4Sub: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ subv_w(i.OutputSimd128Register(), i.InputSimd128Register(0), |
| i.InputSimd128Register(1)); |
| break; |
| } |
| case kMipsF32x4Splat: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ FmoveLow(kScratchReg, i.InputSingleRegister(0)); |
| __ fill_w(i.OutputSimd128Register(), kScratchReg); |
| break; |
| } |
| case kMipsF32x4ExtractLane: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ copy_u_w(kScratchReg, i.InputSimd128Register(0), i.InputInt8(1)); |
| __ FmoveLow(i.OutputSingleRegister(), kScratchReg); |
| break; |
| } |
| case kMipsF32x4ReplaceLane: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| Simd128Register src = i.InputSimd128Register(0); |
| Simd128Register dst = i.OutputSimd128Register(); |
| if (src != dst) { |
| __ move_v(dst, src); |
| } |
| __ FmoveLow(kScratchReg, i.InputSingleRegister(2)); |
| __ insert_w(dst, i.InputInt8(1), kScratchReg); |
| break; |
| } |
| case kMipsF32x4SConvertI32x4: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ ffint_s_w(i.OutputSimd128Register(), i.InputSimd128Register(0)); |
| break; |
| } |
| case kMipsF32x4UConvertI32x4: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ ffint_u_w(i.OutputSimd128Register(), i.InputSimd128Register(0)); |
| break; |
| } |
| case kMipsI32x4Mul: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ mulv_w(i.OutputSimd128Register(), i.InputSimd128Register(0), |
| i.InputSimd128Register(1)); |
| break; |
| } |
| case kMipsI32x4MaxS: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ max_s_w(i.OutputSimd128Register(), i.InputSimd128Register(0), |
| i.InputSimd128Register(1)); |
| break; |
| } |
| case kMipsI32x4MinS: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ min_s_w(i.OutputSimd128Register(), i.InputSimd128Register(0), |
| i.InputSimd128Register(1)); |
| break; |
| } |
| case kMipsI32x4Eq: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ ceq_w(i.OutputSimd128Register(), i.InputSimd128Register(0), |
| i.InputSimd128Register(1)); |
| break; |
| } |
| case kMipsI32x4Ne: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| Simd128Register dst = i.OutputSimd128Register(); |
| __ ceq_w(dst, i.InputSimd128Register(0), i.InputSimd128Register(1)); |
| __ nor_v(dst, dst, dst); |
| break; |
| } |
| case kMipsI32x4Shl: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ slli_w(i.OutputSimd128Register(), i.InputSimd128Register(0), |
| i.InputInt5(1)); |
| break; |
| } |
| case kMipsI32x4ShrS: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ srai_w(i.OutputSimd128Register(), i.InputSimd128Register(0), |
| i.InputInt5(1)); |
| break; |
| } |
| case kMipsI32x4ShrU: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ srli_w(i.OutputSimd128Register(), i.InputSimd128Register(0), |
| i.InputInt5(1)); |
| break; |
| } |
| case kMipsI32x4MaxU: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ max_u_w(i.OutputSimd128Register(), i.InputSimd128Register(0), |
| i.InputSimd128Register(1)); |
| break; |
| } |
| case kMipsI32x4MinU: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ min_u_w(i.OutputSimd128Register(), i.InputSimd128Register(0), |
| i.InputSimd128Register(1)); |
| break; |
| } |
| case kMipsS128Select: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| DCHECK(i.OutputSimd128Register() == i.InputSimd128Register(0)); |
| __ bsel_v(i.OutputSimd128Register(), i.InputSimd128Register(2), |
| i.InputSimd128Register(1)); |
| break; |
| } |
| case kMipsF32x4Abs: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ bclri_w(i.OutputSimd128Register(), i.InputSimd128Register(0), 31); |
| break; |
| } |
| case kMipsF32x4Neg: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ bnegi_w(i.OutputSimd128Register(), i.InputSimd128Register(0), 31); |
| break; |
| } |
| case kMipsF32x4RecipApprox: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ frcp_w(i.OutputSimd128Register(), i.InputSimd128Register(0)); |
| break; |
| } |
| case kMipsF32x4RecipSqrtApprox: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ frsqrt_w(i.OutputSimd128Register(), i.InputSimd128Register(0)); |
| break; |
| } |
| case kMipsF32x4Add: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ fadd_w(i.OutputSimd128Register(), i.InputSimd128Register(0), |
| i.InputSimd128Register(1)); |
| break; |
| } |
| case kMipsF32x4Sub: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ fsub_w(i.OutputSimd128Register(), i.InputSimd128Register(0), |
| i.InputSimd128Register(1)); |
| break; |
| } |
| case kMipsF32x4Mul: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ fmul_w(i.OutputSimd128Register(), i.InputSimd128Register(0), |
| i.InputSimd128Register(1)); |
| break; |
| } |
| case kMipsF32x4Max: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ fmax_w(i.OutputSimd128Register(), i.InputSimd128Register(0), |
| i.InputSimd128Register(1)); |
| break; |
| } |
| case kMipsF32x4Min: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ fmin_w(i.OutputSimd128Register(), i.InputSimd128Register(0), |
| i.InputSimd128Register(1)); |
| break; |
| } |
| case kMipsF32x4Eq: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ fceq_w(i.OutputSimd128Register(), i.InputSimd128Register(0), |
| i.InputSimd128Register(1)); |
| break; |
| } |
| case kMipsF32x4Ne: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ fcne_w(i.OutputSimd128Register(), i.InputSimd128Register(0), |
| i.InputSimd128Register(1)); |
| break; |
| } |
| case kMipsF32x4Lt: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ fclt_w(i.OutputSimd128Register(), i.InputSimd128Register(0), |
| i.InputSimd128Register(1)); |
| break; |
| } |
| case kMipsF32x4Le: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ fcle_w(i.OutputSimd128Register(), i.InputSimd128Register(0), |
| i.InputSimd128Register(1)); |
| break; |
| } |
| case kMipsI32x4SConvertF32x4: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ ftrunc_s_w(i.OutputSimd128Register(), i.InputSimd128Register(0)); |
| break; |
| } |
| case kMipsI32x4UConvertF32x4: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ ftrunc_u_w(i.OutputSimd128Register(), i.InputSimd128Register(0)); |
| break; |
| } |
| case kMipsI32x4Neg: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ xor_v(kSimd128RegZero, kSimd128RegZero, kSimd128RegZero); |
| __ subv_w(i.OutputSimd128Register(), kSimd128RegZero, |
| i.InputSimd128Register(0)); |
| break; |
| } |
| case kMipsI32x4GtS: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ clt_s_w(i.OutputSimd128Register(), i.InputSimd128Register(1), |
| i.InputSimd128Register(0)); |
| break; |
| } |
| case kMipsI32x4GeS: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ cle_s_w(i.OutputSimd128Register(), i.InputSimd128Register(1), |
| i.InputSimd128Register(0)); |
| break; |
| } |
| case kMipsI32x4GtU: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ clt_u_w(i.OutputSimd128Register(), i.InputSimd128Register(1), |
| i.InputSimd128Register(0)); |
| break; |
| } |
| case kMipsI32x4GeU: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ cle_u_w(i.OutputSimd128Register(), i.InputSimd128Register(1), |
| i.InputSimd128Register(0)); |
| break; |
| } |
| case kMipsI16x8Splat: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ fill_h(i.OutputSimd128Register(), i.InputRegister(0)); |
| break; |
| } |
| case kMipsI16x8ExtractLane: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ copy_s_h(i.OutputRegister(), i.InputSimd128Register(0), |
| i.InputInt8(1)); |
| break; |
| } |
| case kMipsI16x8ReplaceLane: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| Simd128Register src = i.InputSimd128Register(0); |
| Simd128Register dst = i.OutputSimd128Register(); |
| if (src != dst) { |
| __ move_v(dst, src); |
| } |
| __ insert_h(dst, i.InputInt8(1), i.InputRegister(2)); |
| break; |
| } |
| case kMipsI16x8Neg: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ xor_v(kSimd128RegZero, kSimd128RegZero, kSimd128RegZero); |
| __ subv_h(i.OutputSimd128Register(), kSimd128RegZero, |
| i.InputSimd128Register(0)); |
| break; |
| } |
| case kMipsI16x8Shl: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ slli_h(i.OutputSimd128Register(), i.InputSimd128Register(0), |
| i.InputInt4(1)); |
| break; |
| } |
| case kMipsI16x8ShrS: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ srai_h(i.OutputSimd128Register(), i.InputSimd128Register(0), |
| i.InputInt4(1)); |
| break; |
| } |
| case kMipsI16x8ShrU: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ srli_h(i.OutputSimd128Register(), i.InputSimd128Register(0), |
| i.InputInt4(1)); |
| break; |
| } |
| case kMipsI16x8Add: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ addv_h(i.OutputSimd128Register(), i.InputSimd128Register(0), |
| i.InputSimd128Register(1)); |
| break; |
| } |
| case kMipsI16x8AddSaturateS: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ adds_s_h(i.OutputSimd128Register(), i.InputSimd128Register(0), |
| i.InputSimd128Register(1)); |
| break; |
| } |
| case kMipsI16x8Sub: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ subv_h(i.OutputSimd128Register(), i.InputSimd128Register(0), |
| i.InputSimd128Register(1)); |
| break; |
| } |
| case kMipsI16x8SubSaturateS: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ subs_s_h(i.OutputSimd128Register(), i.InputSimd128Register(0), |
| i.InputSimd128Register(1)); |
| break; |
| } |
| case kMipsI16x8Mul: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ mulv_h(i.OutputSimd128Register(), i.InputSimd128Register(0), |
| i.InputSimd128Register(1)); |
| break; |
| } |
| case kMipsI16x8MaxS: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ max_s_h(i.OutputSimd128Register(), i.InputSimd128Register(0), |
| i.InputSimd128Register(1)); |
| break; |
| } |
| case kMipsI16x8MinS: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ min_s_h(i.OutputSimd128Register(), i.InputSimd128Register(0), |
| i.InputSimd128Register(1)); |
| break; |
| } |
| case kMipsI16x8Eq: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ ceq_h(i.OutputSimd128Register(), i.InputSimd128Register(0), |
| i.InputSimd128Register(1)); |
| break; |
| } |
| case kMipsI16x8Ne: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| Simd128Register dst = i.OutputSimd128Register(); |
| __ ceq_h(dst, i.InputSimd128Register(0), i.InputSimd128Register(1)); |
| __ nor_v(dst, dst, dst); |
| break; |
| } |
| case kMipsI16x8GtS: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ clt_s_h(i.OutputSimd128Register(), i.InputSimd128Register(1), |
| i.InputSimd128Register(0)); |
| break; |
| } |
| case kMipsI16x8GeS: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ cle_s_h(i.OutputSimd128Register(), i.InputSimd128Register(1), |
| i.InputSimd128Register(0)); |
| break; |
| } |
| case kMipsI16x8AddSaturateU: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ adds_u_h(i.OutputSimd128Register(), i.InputSimd128Register(0), |
| i.InputSimd128Register(1)); |
| break; |
| } |
| case kMipsI16x8SubSaturateU: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ subs_u_h(i.OutputSimd128Register(), i.InputSimd128Register(0), |
| i.InputSimd128Register(1)); |
| break; |
| } |
| case kMipsI16x8MaxU: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ max_u_h(i.OutputSimd128Register(), i.InputSimd128Register(0), |
| i.InputSimd128Register(1)); |
| break; |
| } |
| case kMipsI16x8MinU: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ min_u_h(i.OutputSimd128Register(), i.InputSimd128Register(0), |
| i.InputSimd128Register(1)); |
| break; |
| } |
| case kMipsI16x8GtU: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ clt_u_h(i.OutputSimd128Register(), i.InputSimd128Register(1), |
| i.InputSimd128Register(0)); |
| break; |
| } |
| case kMipsI16x8GeU: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ cle_u_h(i.OutputSimd128Register(), i.InputSimd128Register(1), |
| i.InputSimd128Register(0)); |
| break; |
| } |
| case kMipsI8x16Splat: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ fill_b(i.OutputSimd128Register(), i.InputRegister(0)); |
| break; |
| } |
| case kMipsI8x16ExtractLane: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ copy_s_b(i.OutputRegister(), i.InputSimd128Register(0), |
| i.InputInt8(1)); |
| break; |
| } |
| case kMipsI8x16ReplaceLane: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| Simd128Register src = i.InputSimd128Register(0); |
| Simd128Register dst = i.OutputSimd128Register(); |
| if (src != dst) { |
| __ move_v(dst, src); |
| } |
| __ insert_b(dst, i.InputInt8(1), i.InputRegister(2)); |
| break; |
| } |
| case kMipsI8x16Neg: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ xor_v(kSimd128RegZero, kSimd128RegZero, kSimd128RegZero); |
| __ subv_b(i.OutputSimd128Register(), kSimd128RegZero, |
| i.InputSimd128Register(0)); |
| break; |
| } |
| case kMipsI8x16Shl: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ slli_b(i.OutputSimd128Register(), i.InputSimd128Register(0), |
| i.InputInt3(1)); |
| break; |
| } |
| case kMipsI8x16ShrS: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ srai_b(i.OutputSimd128Register(), i.InputSimd128Register(0), |
| i.InputInt3(1)); |
| break; |
| } |
| case kMipsI8x16Add: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ addv_b(i.OutputSimd128Register(), i.InputSimd128Register(0), |
| i.InputSimd128Register(1)); |
| break; |
| } |
| case kMipsI8x16AddSaturateS: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ adds_s_b(i.OutputSimd128Register(), i.InputSimd128Register(0), |
| i.InputSimd128Register(1)); |
| break; |
| } |
| case kMipsI8x16Sub: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ subv_b(i.OutputSimd128Register(), i.InputSimd128Register(0), |
| i.InputSimd128Register(1)); |
| break; |
| } |
| case kMipsI8x16SubSaturateS: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ subs_s_b(i.OutputSimd128Register(), i.InputSimd128Register(0), |
| i.InputSimd128Register(1)); |
| break; |
| } |
| case kMipsI8x16Mul: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ mulv_b(i.OutputSimd128Register(), i.InputSimd128Register(0), |
| i.InputSimd128Register(1)); |
| break; |
| } |
| case kMipsI8x16MaxS: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ max_s_b(i.OutputSimd128Register(), i.InputSimd128Register(0), |
| i.InputSimd128Register(1)); |
| break; |
| } |
| case kMipsI8x16MinS: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ min_s_b(i.OutputSimd128Register(), i.InputSimd128Register(0), |
| i.InputSimd128Register(1)); |
| break; |
| } |
| case kMipsI8x16Eq: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ ceq_b(i.OutputSimd128Register(), i.InputSimd128Register(0), |
| i.InputSimd128Register(1)); |
| break; |
| } |
| case kMipsI8x16Ne: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| Simd128Register dst = i.OutputSimd128Register(); |
| __ ceq_b(dst, i.InputSimd128Register(0), i.InputSimd128Register(1)); |
| __ nor_v(dst, dst, dst); |
| break; |
| } |
| case kMipsI8x16GtS: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ clt_s_b(i.OutputSimd128Register(), i.InputSimd128Register(1), |
| i.InputSimd128Register(0)); |
| break; |
| } |
| case kMipsI8x16GeS: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ cle_s_b(i.OutputSimd128Register(), i.InputSimd128Register(1), |
| i.InputSimd128Register(0)); |
| break; |
| } |
| case kMipsI8x16ShrU: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ srli_b(i.OutputSimd128Register(), i.InputSimd128Register(0), |
| i.InputInt3(1)); |
| break; |
| } |
| case kMipsI8x16AddSaturateU: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ adds_u_b(i.OutputSimd128Register(), i.InputSimd128Register(0), |
| i.InputSimd128Register(1)); |
| break; |
| } |
| case kMipsI8x16SubSaturateU: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ subs_u_b(i.OutputSimd128Register(), i.InputSimd128Register(0), |
| i.InputSimd128Register(1)); |
| break; |
| } |
| case kMipsI8x16MaxU: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ max_u_b(i.OutputSimd128Register(), i.InputSimd128Register(0), |
| i.InputSimd128Register(1)); |
| break; |
| } |
| case kMipsI8x16MinU: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ min_u_b(i.OutputSimd128Register(), i.InputSimd128Register(0), |
| i.InputSimd128Register(1)); |
| break; |
| } |
| case kMipsI8x16GtU: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ clt_u_b(i.OutputSimd128Register(), i.InputSimd128Register(1), |
| i.InputSimd128Register(0)); |
| break; |
| } |
| case kMipsI8x16GeU: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ cle_u_b(i.OutputSimd128Register(), i.InputSimd128Register(1), |
| i.InputSimd128Register(0)); |
| break; |
| } |
| case kMipsS128And: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ and_v(i.OutputSimd128Register(), i.InputSimd128Register(0), |
| i.InputSimd128Register(1)); |
| break; |
| } |
| case kMipsS128Or: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ or_v(i.OutputSimd128Register(), i.InputSimd128Register(0), |
| i.InputSimd128Register(1)); |
| break; |
| } |
| case kMipsS128Xor: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ xor_v(i.OutputSimd128Register(), i.InputSimd128Register(0), |
| i.InputSimd128Register(1)); |
| break; |
| } |
| case kMipsS128Not: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ nor_v(i.OutputSimd128Register(), i.InputSimd128Register(0), |
| i.InputSimd128Register(0)); |
| break; |
| } |
| case kMipsS1x4AnyTrue: |
| case kMipsS1x8AnyTrue: |
| case kMipsS1x16AnyTrue: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| Register dst = i.OutputRegister(); |
| Label all_false; |
| |
| __ BranchMSA(&all_false, MSA_BRANCH_V, all_zero, |
| i.InputSimd128Register(0), USE_DELAY_SLOT); |
| __ li(dst, 0); // branch delay slot |
| __ li(dst, -1); |
| __ bind(&all_false); |
| break; |
| } |
| case kMipsS1x4AllTrue: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| Register dst = i.OutputRegister(); |
| Label all_true; |
| __ BranchMSA(&all_true, MSA_BRANCH_W, all_not_zero, |
| i.InputSimd128Register(0), USE_DELAY_SLOT); |
| __ li(dst, -1); // branch delay slot |
| __ li(dst, 0); |
| __ bind(&all_true); |
| break; |
| } |
| case kMipsS1x8AllTrue: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| Register dst = i.OutputRegister(); |
| Label all_true; |
| __ BranchMSA(&all_true, MSA_BRANCH_H, all_not_zero, |
| i.InputSimd128Register(0), USE_DELAY_SLOT); |
| __ li(dst, -1); // branch delay slot |
| __ li(dst, 0); |
| __ bind(&all_true); |
| break; |
| } |
| case kMipsS1x16AllTrue: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| Register dst = i.OutputRegister(); |
| Label all_true; |
| __ BranchMSA(&all_true, MSA_BRANCH_B, all_not_zero, |
| i.InputSimd128Register(0), USE_DELAY_SLOT); |
| __ li(dst, -1); // branch delay slot |
| __ li(dst, 0); |
| __ bind(&all_true); |
| break; |
| } |
| case kMipsMsaLd: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ ld_b(i.OutputSimd128Register(), i.MemoryOperand()); |
| break; |
| } |
| case kMipsMsaSt: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| __ st_b(i.InputSimd128Register(2), i.MemoryOperand()); |
| break; |
| } |
| case kMipsS32x4InterleaveRight: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| Simd128Register dst = i.OutputSimd128Register(), |
| src0 = i.InputSimd128Register(0), |
| src1 = i.InputSimd128Register(1); |
| // src1 = [7, 6, 5, 4], src0 = [3, 2, 1, 0] |
| // dst = [5, 1, 4, 0] |
| __ ilvr_w(dst, src1, src0); |
| break; |
| } |
| case kMipsS32x4InterleaveLeft: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| Simd128Register dst = i.OutputSimd128Register(), |
| src0 = i.InputSimd128Register(0), |
| src1 = i.InputSimd128Register(1); |
| // src1 = [7, 6, 5, 4], src0 = [3, 2, 1, 0] |
| // dst = [7, 3, 6, 2] |
| __ ilvl_w(dst, src1, src0); |
| break; |
| } |
| case kMipsS32x4PackEven: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| Simd128Register dst = i.OutputSimd128Register(), |
| src0 = i.InputSimd128Register(0), |
| src1 = i.InputSimd128Register(1); |
| // src1 = [7, 6, 5, 4], src0 = [3, 2, 1, 0] |
| // dst = [6, 4, 2, 0] |
| __ pckev_w(dst, src1, src0); |
| break; |
| } |
| case kMipsS32x4PackOdd: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| Simd128Register dst = i.OutputSimd128Register(), |
| src0 = i.InputSimd128Register(0), |
| src1 = i.InputSimd128Register(1); |
| // src1 = [7, 6, 5, 4], src0 = [3, 2, 1, 0] |
| // dst = [7, 5, 3, 1] |
| __ pckod_w(dst, src1, src0); |
| break; |
| } |
| case kMipsS32x4InterleaveEven: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| Simd128Register dst = i.OutputSimd128Register(), |
| src0 = i.InputSimd128Register(0), |
| src1 = i.InputSimd128Register(1); |
| // src1 = [7, 6, 5, 4], src0 = [3, 2, 1, 0] |
| // dst = [6, 2, 4, 0] |
| __ ilvev_w(dst, src1, src0); |
| break; |
| } |
| case kMipsS32x4InterleaveOdd: { |
| CpuFeatureScope msa_scope(tasm(), MIPS_SIMD); |
| Simd128Register 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 kMipsS32x4Shuffle: { |
| 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 kMipsS16x8InterleaveRight: { |
| 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 kMipsS16x8InterleaveLeft: { |
| 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 kMipsS16x8PackEven: { |
| 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 kMipsS16x8PackOdd: { |
| 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 kMipsS16x8InterleaveEven: { |
| 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 kMipsS16x8InterleaveOdd: { |
| 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 kMipsS16x4Reverse: { |
| 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 kMipsS16x2Reverse: { |
| 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 kMipsS8x16InterleaveRight: { |
| 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 kMipsS8x16InterleaveLeft: { |
| 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 kMipsS8x16PackEven: { |
| 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 kMipsS8x16PackOdd: { |
| 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 kMipsS8x16InterleaveEven: { |
| 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 kMipsS8x16InterleaveOdd: { |
| 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 kMipsS8x16Concat: { |
| 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 kMipsS8x16Shuffle: { |
| 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; |
| } |
| |
| __ li(kScratchReg, i.InputInt32(2)); |
| __ insert_w(dst, 0, kScratchReg); |
| __ li(kScratchReg, i.InputInt32(3)); |
| __ insert_w(dst, 1, kScratchReg); |
| __ li(kScratchReg, i.InputInt32(4)); |
| __ insert_w(dst, 2, kScratchReg); |
| __ li(kScratchReg, i.InputInt32(5)); |
| __ insert_w(dst, 3, kScratchReg); |
| __ vshf_b(dst, src1, src0); |
| break; |
| } |
| case kMipsS8x8Reverse: { |
| 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 kMipsS8x4Reverse: { |
| 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 kMipsS8x2Reverse: { |
| 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; |
| } |