summaryrefslogtreecommitdiff
path: root/deps/v8/src/compiler/backend/loong64/code-generator-loong64.cc
diff options
context:
space:
mode:
Diffstat (limited to 'deps/v8/src/compiler/backend/loong64/code-generator-loong64.cc')
-rw-r--r--deps/v8/src/compiler/backend/loong64/code-generator-loong64.cc2636
1 files changed, 2636 insertions, 0 deletions
diff --git a/deps/v8/src/compiler/backend/loong64/code-generator-loong64.cc b/deps/v8/src/compiler/backend/loong64/code-generator-loong64.cc
new file mode 100644
index 0000000000..0397a36145
--- /dev/null
+++ b/deps/v8/src/compiler/backend/loong64/code-generator-loong64.cc
@@ -0,0 +1,2636 @@
+// Copyright 2021 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/loong64/constants-loong64.h"
+#include "src/codegen/macro-assembler.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"
+
+#if V8_ENABLE_WEBASSEMBLY
+#include "src/wasm/wasm-code-manager.h"
+#endif // V8_ENABLE_WEBASSEMBLY
+
+namespace v8 {
+namespace internal {
+namespace compiler {
+
+#define __ tasm()->
+
+// TODO(LOONG_dev): 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_loong64: %s at line %d\n", \
+ __FUNCTION__, __LINE__)
+
+// Adds Loong64-specific methods to convert InstructionOperands.
+class Loong64OperandConverter final : public InstructionOperandConverter {
+ public:
+ Loong64OperandConverter(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 LOONG64,
+ // 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:
+ break;
+ case Constant::kDelayedStringConstant:
+ return Operand::EmbeddedStringConstant(
+ constant.ToDelayedStringConstant());
+ case Constant::kRpoNumber:
+ UNREACHABLE(); // TODO(titzer): RPO immediates on loong64?
+ }
+ 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_Root:
+ *first_index += 1;
+ return MemOperand(kRootRegister, InputInt32(index));
+ case kMode_MRI:
+ *first_index += 2;
+ return MemOperand(InputRegister(index + 0), InputInt32(index + 1));
+ case kMode_MRR:
+ *first_index += 2;
+ return MemOperand(InputRegister(index + 0), InputRegister(index + 1));
+ }
+ 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, Operand offset,
+ Register value, RecordWriteMode mode,
+ StubCallMode stub_mode)
+ : OutOfLineCode(gen),
+ object_(object),
+ offset_(offset),
+ value_(value),
+ mode_(mode),
+#if V8_ENABLE_WEBASSEMBLY
+ stub_mode_(stub_mode),
+#endif // V8_ENABLE_WEBASSEMBLY
+ must_save_lr_(!gen->frame_access_state()->has_frame()),
+ zone_(gen->zone()) {
+ }
+
+ void Generate() final {
+ __ CheckPageFlag(value_, MemoryChunk::kPointersToHereAreInterestingMask, eq,
+ exit());
+ RememberedSetAction const remembered_set_action =
+ mode_ > RecordWriteMode::kValueIsMap ? RememberedSetAction::kEmit
+ : RememberedSetAction::kOmit;
+ SaveFPRegsMode const save_fp_mode = frame()->DidAllocateDoubleRegisters()
+ ? SaveFPRegsMode::kSave
+ : SaveFPRegsMode::kIgnore;
+ if (must_save_lr_) {
+ // We need to save and restore ra if the frame was elided.
+ __ Push(ra);
+ }
+ if (mode_ == RecordWriteMode::kValueIsEphemeronKey) {
+ __ CallEphemeronKeyBarrier(object_, offset_, save_fp_mode);
+#if V8_ENABLE_WEBASSEMBLY
+ } 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.
+ __ CallRecordWriteStubSaveRegisters(object_, offset_,
+ remembered_set_action, save_fp_mode,
+ StubCallMode::kCallWasmRuntimeStub);
+#endif // V8_ENABLE_WEBASSEMBLY
+ } else {
+ __ CallRecordWriteStubSaveRegisters(object_, offset_,
+ remembered_set_action, save_fp_mode);
+ }
+ if (must_save_lr_) {
+ __ Pop(ra);
+ }
+ }
+
+ private:
+ Register const object_;
+ Operand const offset_;
+ Register const value_;
+ RecordWriteMode const mode_;
+#if V8_ENABLE_WEBASSEMBLY
+ StubCallMode const stub_mode_;
+#endif // V8_ENABLE_WEBASSEMBLY
+ 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 CEQ;
+ case kNotEqual:
+ *predicate = false;
+ return CEQ;
+ case kUnsignedLessThan:
+ *predicate = true;
+ return CLT;
+ case kUnsignedGreaterThanOrEqual:
+ *predicate = false;
+ return CLT;
+ case kUnsignedLessThanOrEqual:
+ *predicate = true;
+ return CLE;
+ case kUnsignedGreaterThan:
+ *predicate = false;
+ return CLE;
+ case kUnorderedEqual:
+ case kUnorderedNotEqual:
+ *predicate = true;
+ break;
+ default:
+ *predicate = true;
+ break;
+ }
+ UNREACHABLE();
+}
+
+} // namespace
+
+#define ASSEMBLE_ATOMIC_LOAD_INTEGER(asm_instr) \
+ do { \
+ __ asm_instr(i.OutputRegister(), i.MemoryOperand()); \
+ __ dbar(0); \
+ } while (0)
+
+// TODO(LOONG_dev): remove second dbar?
+#define ASSEMBLE_ATOMIC_STORE_INTEGER(asm_instr) \
+ do { \
+ __ dbar(0); \
+ __ asm_instr(i.InputOrZeroRegister(2), i.MemoryOperand()); \
+ __ dbar(0); \
+ } while (0)
+
+// only use for sub_w and sub_d
+#define ASSEMBLE_ATOMIC_BINOP(load_linked, store_conditional, bin_instr) \
+ do { \
+ Label binop; \
+ __ Add_d(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1)); \
+ __ dbar(0); \
+ __ 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)); \
+ __ dbar(0); \
+ } while (0)
+
+// TODO(LOONG_dev): remove second dbar?
+#define ASSEMBLE_ATOMIC_BINOP_EXT(load_linked, store_conditional, sign_extend, \
+ size, bin_instr, representation) \
+ do { \
+ Label binop; \
+ __ add_d(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); \
+ } \
+ __ Sub_d(i.TempRegister(0), i.TempRegister(0), \
+ Operand(i.TempRegister(3))); \
+ __ slli_w(i.TempRegister(3), i.TempRegister(3), 3); \
+ __ dbar(0); \
+ __ 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)); \
+ __ dbar(0); \
+ } while (0)
+
+// TODO(LOONG_dev): remove second dbar?
+#define ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT( \
+ load_linked, store_conditional, sign_extend, size, representation) \
+ do { \
+ Label exchange; \
+ __ add_d(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); \
+ } \
+ __ Sub_d(i.TempRegister(0), i.TempRegister(0), \
+ Operand(i.TempRegister(1))); \
+ __ slli_w(i.TempRegister(1), i.TempRegister(1), 3); \
+ __ dbar(0); \
+ __ 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)); \
+ __ dbar(0); \
+ } while (0)
+
+// TODO(LOONG_dev): remove second dbar?
+#define ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER(load_linked, \
+ store_conditional) \
+ do { \
+ Label compareExchange; \
+ Label exit; \
+ __ add_d(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1)); \
+ __ dbar(0); \
+ __ 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); \
+ __ dbar(0); \
+ } while (0)
+
+// TODO(LOONG_dev): remove second dbar?
+#define ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER_EXT( \
+ load_linked, store_conditional, sign_extend, size, representation) \
+ do { \
+ Label compareExchange; \
+ Label exit; \
+ __ add_d(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); \
+ } \
+ __ Sub_d(i.TempRegister(0), i.TempRegister(0), \
+ Operand(i.TempRegister(1))); \
+ __ slli_w(i.TempRegister(1), i.TempRegister(1), 3); \
+ __ dbar(0); \
+ __ 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), zero_reg, 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); \
+ __ dbar(0); \
+ } while (0)
+
+#define ASSEMBLE_IEEE754_BINOP(name) \
+ do { \
+ FrameScope scope(tasm(), StackFrame::MANUAL); \
+ UseScratchRegisterScope temps(tasm()); \
+ Register scratch = temps.Acquire(); \
+ __ PrepareCallCFunction(0, 2, scratch); \
+ __ CallCFunction(ExternalReference::ieee754_##name##_function(), 0, 2); \
+ } while (0)
+
+#define ASSEMBLE_IEEE754_UNOP(name) \
+ do { \
+ FrameScope scope(tasm(), StackFrame::MANUAL); \
+ UseScratchRegisterScope temps(tasm()); \
+ Register scratch = temps.Acquire(); \
+ __ PrepareCallCFunction(0, 1, scratch); \
+ __ CallCFunction(ExternalReference::ieee754_##name##_function(), 0, 1); \
+ } 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_d(ra, MemOperand(fp, StandardFrameConstants::kCallerPCOffset));
+ __ Ld_d(fp, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
+ }
+ frame_access_state()->SetFrameAccessToSP();
+}
+
+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->Sub_d(sp, sp, stack_slot_delta * kSystemPointerSize);
+ state->IncreaseSPDelta(stack_slot_delta);
+ } else if (allow_shrinkage && stack_slot_delta < 0) {
+ tasm->Add_d(sp, sp, -stack_slot_delta * kSystemPointerSize);
+ state->IncreaseSPDelta(stack_slot_delta);
+ }
+}
+
+} // namespace
+
+void CodeGenerator::AssembleTailCallBeforeGap(Instruction* instr,
+ int first_unused_slot_offset) {
+ AdjustStackPointerForTailCall(tasm(), frame_access_state(),
+ first_unused_slot_offset, false);
+}
+
+void CodeGenerator::AssembleTailCallAfterGap(Instruction* instr,
+ int first_unused_slot_offset) {
+ AdjustStackPointerForTailCall(tasm(), frame_access_state(),
+ first_unused_slot_offset);
+}
+
+// Check that {kJavaScriptCallCodeStartRegister} is correct.
+void CodeGenerator::AssembleCodeStartRegisterCheck() {
+ UseScratchRegisterScope temps(tasm());
+ Register scratch = temps.Acquire();
+ __ ComputeCodeStartAddress(scratch);
+ __ Assert(eq, AbortReason::kWrongFunctionCodeStart,
+ kJavaScriptCallCodeStartRegister, Operand(scratch));
+}
+
+// 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() {
+ UseScratchRegisterScope temps(tasm());
+ Register scratch = temps.Acquire();
+ int offset = Code::kCodeDataContainerOffset - Code::kHeaderSize;
+ __ Ld_d(scratch, MemOperand(kJavaScriptCallCodeStartRegister, offset));
+ __ Ld_w(scratch, FieldMemOperand(
+ scratch, CodeDataContainer::kKindSpecificFlagsOffset));
+ __ And(scratch, scratch, Operand(1 << Code::kMarkedForDeoptimizationBit));
+ __ Jump(BUILTIN_CODE(isolate(), CompileLazyDeoptimizedCode),
+ RelocInfo::CODE_TARGET, ne, scratch, Operand(zero_reg));
+}
+
+// Assembles an instruction after register allocation, producing machine code.
+CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction(
+ Instruction* instr) {
+ Loong64OperandConverter 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);
+ __ CallCodeObject(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;
+ }
+#if V8_ENABLE_WEBASSEMBLY
+ case kArchCallWasmFunction: {
+ 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 {
+ __ Call(i.InputRegister(0));
+ }
+ RecordCallPosition(instr);
+ frame_access_state()->ClearSPDelta();
+ 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 {
+ __ Jump(i.InputRegister(0));
+ }
+ frame_access_state()->ClearSPDelta();
+ frame_access_state()->SetFrameAccessToDefault();
+ break;
+ }
+#endif // V8_ENABLE_WEBASSEMBLY
+ case kArchTailCallCodeObject: {
+ 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);
+ __ JumpCodeObject(reg);
+ }
+ 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) {
+ UseScratchRegisterScope temps(tasm());
+ Register scratch = temps.Acquire();
+ // Check the function's context matches the context argument.
+ __ Ld_d(scratch, FieldMemOperand(func, JSFunction::kContextOffset));
+ __ Assert(eq, AbortReason::kWrongFunctionContext, cp, Operand(scratch));
+ }
+ static_assert(kJavaScriptCallCodeStartRegister == a2, "ABI mismatch");
+ __ Ld_d(a2, FieldMemOperand(func, JSFunction::kCodeOffset));
+ __ CallCodeObject(a2);
+ RecordCallPosition(instr);
+ frame_access_state()->ClearSPDelta();
+ break;
+ }
+ case kArchPrepareCallCFunction: {
+ UseScratchRegisterScope temps(tasm());
+ Register scratch = temps.Acquire();
+ int const num_parameters = MiscField::decode(instr->opcode());
+ __ PrepareCallCFunction(num_parameters, scratch);
+ // 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_ == SaveFPRegsMode::kIgnore ||
+ fp_mode_ == SaveFPRegsMode::kSave);
+ // 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_ == SaveFPRegsMode::kIgnore ||
+ fp_mode_ == SaveFPRegsMode::kSave);
+ // 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());
+#if V8_ENABLE_WEBASSEMBLY
+ Label start_call;
+ bool isWasmCapiFunction =
+ linkage()->GetIncomingDescriptor()->IsWasmCapiFunction();
+ // from start_call to return address.
+ int offset = __ root_array_available() ? 36 : 80; // 9 or 20 instrs
+#endif // V8_ENABLE_WEBASSEMBLY
+#if V8_HOST_ARCH_LOONG64
+ if (FLAG_debug_code) {
+ offset += 12; // see CallCFunction
+ }
+#endif
+#if V8_ENABLE_WEBASSEMBLY
+ if (isWasmCapiFunction) {
+ __ bind(&start_call);
+ __ pcaddi(t7, -4);
+ __ St_d(t7, MemOperand(fp, WasmExitFrameConstants::kCallingPCOffset));
+ }
+#endif // V8_ENABLE_WEBASSEMBLY
+ 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 V8_ENABLE_WEBASSEMBLY
+ if (isWasmCapiFunction) {
+ CHECK_EQ(offset, __ SizeOfCodeGeneratedSince(&start_call));
+ RecordSafepoint(instr->reference_map());
+ }
+#endif // V8_ENABLE_WEBASSEMBLY
+ 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;
+ 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()->code_handle(Builtin::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, 0, OutputFrameStateCombine::Ignore());
+ __ Branch(exit->label());
+ break;
+ }
+ case kArchRet:
+ AssembleReturn(instr->InputAt(0));
+ break;
+ case kArchStackPointerGreaterThan: {
+ Register lhs_register = sp;
+ uint32_t offset;
+ if (ShouldApplyOffsetToStackCheck(instr, &offset)) {
+ lhs_register = i.TempRegister(1);
+ __ Sub_d(lhs_register, sp, offset);
+ }
+ __ Sltu(i.TempRegister(0), i.InputRegister(0), lhs_register);
+ 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_d(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: // Fall through.
+ case kArchAtomicStoreWithWriteBarrier: {
+ RecordWriteMode mode =
+ static_cast<RecordWriteMode>(MiscField::decode(instr->opcode()));
+ AddressingMode addressing_mode =
+ AddressingModeField::decode(instr->opcode());
+ Register object = i.InputRegister(0);
+ Operand offset(zero_reg);
+ if (addressing_mode == kMode_MRI) {
+ offset = Operand(i.InputInt64(1));
+ } else {
+ DCHECK_EQ(addressing_mode, kMode_MRR);
+ offset = Operand(i.InputRegister(1));
+ }
+ Register value = i.InputRegister(2);
+
+ auto ool = zone()->New<OutOfLineRecordWrite>(
+ this, object, offset, value, mode, DetermineStubCallMode());
+ if (arch_opcode == kArchStoreWithWriteBarrier) {
+ if (addressing_mode == kMode_MRI) {
+ __ St_d(value, MemOperand(object, i.InputInt64(1)));
+ } else {
+ DCHECK_EQ(addressing_mode, kMode_MRR);
+ __ St_d(value, MemOperand(object, i.InputRegister(1)));
+ }
+ } else {
+ DCHECK_EQ(kArchAtomicStoreWithWriteBarrier, arch_opcode);
+ DCHECK_EQ(addressing_mode, kMode_MRI);
+ UseScratchRegisterScope temps(tasm());
+ Register scratch = temps.Acquire();
+ __ Add_d(scratch, object, Operand(i.InputInt64(1)));
+ __ amswap_db_d(zero_reg, value, scratch);
+ }
+ if (mode > RecordWriteMode::kValueIsPointer) {
+ __ JumpIfSmi(value, ool->exit());
+ }
+ __ CheckPageFlag(object, MemoryChunk::kPointersFromHereAreInterestingMask,
+ ne, ool->entry());
+ __ bind(ool->exit());
+ break;
+ }
+ case kArchStackSlot: {
+ UseScratchRegisterScope temps(tasm());
+ Register scratch = temps.Acquire();
+ FrameOffset offset =
+ frame_access_state()->GetFrameOffset(i.InputInt32(0));
+ Register base_reg = offset.from_stack_pointer() ? sp : fp;
+ __ Add_d(i.OutputRegister(), base_reg, Operand(offset.offset()));
+ if (FLAG_debug_code) {
+ // Verify that the output_register is properly aligned
+ __ And(scratch, i.OutputRegister(), Operand(kSystemPointerSize - 1));
+ __ Assert(eq, AbortReason::kAllocationIsNotDoubleAligned, scratch,
+ Operand(zero_reg));
+ }
+ 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 kLoong64Add_w:
+ __ Add_w(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
+ break;
+ case kLoong64Add_d:
+ __ Add_d(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
+ break;
+ case kLoong64AddOvf_d:
+ __ AddOverflow_d(i.OutputRegister(), i.InputRegister(0),
+ i.InputOperand(1), t8);
+ break;
+ case kLoong64Sub_w:
+ __ Sub_w(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
+ break;
+ case kLoong64Sub_d:
+ __ Sub_d(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
+ break;
+ case kLoong64SubOvf_d:
+ __ SubOverflow_d(i.OutputRegister(), i.InputRegister(0),
+ i.InputOperand(1), t8);
+ break;
+ case kLoong64Mul_w:
+ __ Mul_w(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
+ break;
+ case kLoong64MulOvf_w:
+ __ MulOverflow_w(i.OutputRegister(), i.InputRegister(0),
+ i.InputOperand(1), t8);
+ break;
+ case kLoong64Mulh_w:
+ __ Mulh_w(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
+ break;
+ case kLoong64Mulh_wu:
+ __ Mulh_wu(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
+ break;
+ case kLoong64Mulh_d:
+ __ Mulh_d(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
+ break;
+ case kLoong64Div_w:
+ __ Div_w(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
+ __ masknez(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1));
+ break;
+ case kLoong64Div_wu:
+ __ Div_wu(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
+ __ masknez(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1));
+ break;
+ case kLoong64Mod_w:
+ __ Mod_w(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
+ break;
+ case kLoong64Mod_wu:
+ __ Mod_wu(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
+ break;
+ case kLoong64Mul_d:
+ __ Mul_d(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
+ break;
+ case kLoong64Div_d:
+ __ Div_d(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
+ __ masknez(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1));
+ break;
+ case kLoong64Div_du:
+ __ Div_du(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
+ __ masknez(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1));
+ break;
+ case kLoong64Mod_d:
+ __ Mod_d(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
+ break;
+ case kLoong64Mod_du:
+ __ Mod_du(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
+ break;
+ case kLoong64Alsl_d:
+ DCHECK(instr->InputAt(2)->IsImmediate());
+ __ Alsl_d(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1),
+ i.InputInt8(2), t7);
+ break;
+ case kLoong64Alsl_w:
+ DCHECK(instr->InputAt(2)->IsImmediate());
+ __ Alsl_w(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1),
+ i.InputInt8(2), t7);
+ break;
+ case kLoong64And:
+ case kLoong64And32:
+ __ And(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
+ break;
+ case kLoong64Or:
+ case kLoong64Or32:
+ __ Or(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
+ break;
+ case kLoong64Nor:
+ case kLoong64Nor32:
+ 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 kLoong64Xor:
+ case kLoong64Xor32:
+ __ Xor(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
+ break;
+ case kLoong64Clz_w:
+ __ clz_w(i.OutputRegister(), i.InputRegister(0));
+ break;
+ case kLoong64Clz_d:
+ __ clz_d(i.OutputRegister(), i.InputRegister(0));
+ break;
+ case kLoong64Sll_w:
+ if (instr->InputAt(1)->IsRegister()) {
+ __ sll_w(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1));
+ } else {
+ int64_t imm = i.InputOperand(1).immediate();
+ __ slli_w(i.OutputRegister(), i.InputRegister(0),
+ static_cast<uint16_t>(imm));
+ }
+ break;
+ case kLoong64Srl_w:
+ if (instr->InputAt(1)->IsRegister()) {
+ __ srl_w(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1));
+ } else {
+ int64_t imm = i.InputOperand(1).immediate();
+ __ srli_w(i.OutputRegister(), i.InputRegister(0),
+ static_cast<uint16_t>(imm));
+ }
+ break;
+ case kLoong64Sra_w:
+ if (instr->InputAt(1)->IsRegister()) {
+ __ sra_w(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1));
+ } else {
+ int64_t imm = i.InputOperand(1).immediate();
+ __ srai_w(i.OutputRegister(), i.InputRegister(0),
+ static_cast<uint16_t>(imm));
+ }
+ break;
+ case kLoong64Bstrpick_w:
+ __ bstrpick_w(i.OutputRegister(), i.InputRegister(0),
+ i.InputInt8(1) + i.InputInt8(2) - 1, i.InputInt8(1));
+ break;
+ case kLoong64Bstrins_w:
+ if (instr->InputAt(1)->IsImmediate() && i.InputInt8(1) == 0) {
+ __ bstrins_w(i.OutputRegister(), zero_reg,
+ i.InputInt8(1) + i.InputInt8(2) - 1, i.InputInt8(1));
+ } else {
+ __ bstrins_w(i.OutputRegister(), i.InputRegister(0),
+ i.InputInt8(1) + i.InputInt8(2) - 1, i.InputInt8(1));
+ }
+ break;
+ case kLoong64Bstrpick_d: {
+ __ bstrpick_d(i.OutputRegister(), i.InputRegister(0),
+ i.InputInt8(1) + i.InputInt8(2) - 1, i.InputInt8(1));
+ break;
+ }
+ case kLoong64Bstrins_d:
+ if (instr->InputAt(1)->IsImmediate() && i.InputInt8(1) == 0) {
+ __ bstrins_d(i.OutputRegister(), zero_reg,
+ i.InputInt8(1) + i.InputInt8(2) - 1, i.InputInt8(1));
+ } else {
+ __ bstrins_d(i.OutputRegister(), i.InputRegister(0),
+ i.InputInt8(1) + i.InputInt8(2) - 1, i.InputInt8(1));
+ }
+ break;
+ case kLoong64Sll_d:
+ if (instr->InputAt(1)->IsRegister()) {
+ __ sll_d(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1));
+ } else {
+ int64_t imm = i.InputOperand(1).immediate();
+ __ slli_d(i.OutputRegister(), i.InputRegister(0),
+ static_cast<uint16_t>(imm));
+ }
+ break;
+ case kLoong64Srl_d:
+ if (instr->InputAt(1)->IsRegister()) {
+ __ srl_d(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1));
+ } else {
+ int64_t imm = i.InputOperand(1).immediate();
+ __ srli_d(i.OutputRegister(), i.InputRegister(0),
+ static_cast<uint16_t>(imm));
+ }
+ break;
+ case kLoong64Sra_d:
+ if (instr->InputAt(1)->IsRegister()) {
+ __ sra_d(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1));
+ } else {
+ int64_t imm = i.InputOperand(1).immediate();
+ __ srai_d(i.OutputRegister(), i.InputRegister(0), imm);
+ }
+ break;
+ case kLoong64Rotr_w:
+ __ Rotr_w(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
+ break;
+ case kLoong64Rotr_d:
+ __ Rotr_d(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
+ break;
+ case kLoong64Tst:
+ __ And(t8, i.InputRegister(0), i.InputOperand(1));
+ // Pseudo-instruction used for cmp/branch. No opcode emitted here.
+ break;
+ case kLoong64Cmp:
+ // Pseudo-instruction used for cmp/branch. No opcode emitted here.
+ break;
+ case kLoong64Mov:
+ // TODO(LOONG_dev): Should we combine mov/li, 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 kLoong64Float32Cmp: {
+ 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(left, right, cc);
+ } break;
+ case kLoong64Float32Add:
+ __ fadd_s(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
+ i.InputDoubleRegister(1));
+ break;
+ case kLoong64Float32Sub:
+ __ fsub_s(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
+ i.InputDoubleRegister(1));
+ break;
+ case kLoong64Float32Mul:
+ __ fmul_s(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
+ i.InputDoubleRegister(1));
+ break;
+ case kLoong64Float32Div:
+ __ fdiv_s(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
+ i.InputDoubleRegister(1));
+ break;
+ case kLoong64Float32Abs:
+ __ fabs_s(i.OutputSingleRegister(), i.InputSingleRegister(0));
+ break;
+ case kLoong64Float32Neg:
+ __ Neg_s(i.OutputSingleRegister(), i.InputSingleRegister(0));
+ break;
+ case kLoong64Float32Sqrt: {
+ __ fsqrt_s(i.OutputDoubleRegister(), i.InputDoubleRegister(0));
+ break;
+ }
+ case kLoong64Float32Min: {
+ 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 kLoong64Float32Max: {
+ 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 kLoong64Float64Cmp: {
+ 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(left, right, cc);
+ } break;
+ case kLoong64Float64Add:
+ __ fadd_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
+ i.InputDoubleRegister(1));
+ break;
+ case kLoong64Float64Sub:
+ __ fsub_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
+ i.InputDoubleRegister(1));
+ break;
+ case kLoong64Float64Mul:
+ // TODO(LOONG_dev): LOONG64 add special case: right op is -1.0, see arm
+ // port.
+ __ fmul_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
+ i.InputDoubleRegister(1));
+ break;
+ case kLoong64Float64Div:
+ __ fdiv_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
+ i.InputDoubleRegister(1));
+ break;
+ case kLoong64Float64Mod: {
+ // TODO(turbofan): implement directly.
+ FrameScope scope(tasm(), StackFrame::MANUAL);
+ UseScratchRegisterScope temps(tasm());
+ Register scratch = temps.Acquire();
+ __ PrepareCallCFunction(0, 2, scratch);
+ __ CallCFunction(ExternalReference::mod_two_doubles_operation(), 0, 2);
+ break;
+ }
+ case kLoong64Float64Abs:
+ __ fabs_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0));
+ break;
+ case kLoong64Float64Neg:
+ __ Neg_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0));
+ break;
+ case kLoong64Float64Sqrt: {
+ __ fsqrt_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0));
+ break;
+ }
+ case kLoong64Float64Min: {
+ 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 kLoong64Float64Max: {
+ 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 kLoong64Float64RoundDown: {
+ __ Floor_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0));
+ break;
+ }
+ case kLoong64Float32RoundDown: {
+ __ Floor_s(i.OutputSingleRegister(), i.InputSingleRegister(0));
+ break;
+ }
+ case kLoong64Float64RoundTruncate: {
+ __ Trunc_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0));
+ break;
+ }
+ case kLoong64Float32RoundTruncate: {
+ __ Trunc_s(i.OutputSingleRegister(), i.InputSingleRegister(0));
+ break;
+ }
+ case kLoong64Float64RoundUp: {
+ __ Ceil_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0));
+ break;
+ }
+ case kLoong64Float32RoundUp: {
+ __ Ceil_s(i.OutputSingleRegister(), i.InputSingleRegister(0));
+ break;
+ }
+ case kLoong64Float64RoundTiesEven: {
+ __ Round_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0));
+ break;
+ }
+ case kLoong64Float32RoundTiesEven: {
+ __ Round_s(i.OutputSingleRegister(), i.InputSingleRegister(0));
+ break;
+ }
+ case kLoong64Float64SilenceNaN:
+ __ FPUCanonicalizeNaN(i.OutputDoubleRegister(), i.InputDoubleRegister(0));
+ break;
+ case kLoong64Float64ToFloat32:
+ __ fcvt_s_d(i.OutputSingleRegister(), i.InputDoubleRegister(0));
+ break;
+ case kLoong64Float32ToFloat64:
+ __ fcvt_d_s(i.OutputDoubleRegister(), i.InputSingleRegister(0));
+ break;
+ case kLoong64Int32ToFloat64: {
+ FPURegister scratch = kScratchDoubleReg;
+ __ movgr2fr_w(scratch, i.InputRegister(0));
+ __ ffint_d_w(i.OutputDoubleRegister(), scratch);
+ break;
+ }
+ case kLoong64Int32ToFloat32: {
+ FPURegister scratch = kScratchDoubleReg;
+ __ movgr2fr_w(scratch, i.InputRegister(0));
+ __ ffint_s_w(i.OutputDoubleRegister(), scratch);
+ break;
+ }
+ case kLoong64Uint32ToFloat32: {
+ __ Ffint_s_uw(i.OutputDoubleRegister(), i.InputRegister(0));
+ break;
+ }
+ case kLoong64Int64ToFloat32: {
+ FPURegister scratch = kScratchDoubleReg;
+ __ movgr2fr_d(scratch, i.InputRegister(0));
+ __ ffint_s_l(i.OutputDoubleRegister(), scratch);
+ break;
+ }
+ case kLoong64Int64ToFloat64: {
+ FPURegister scratch = kScratchDoubleReg;
+ __ movgr2fr_d(scratch, i.InputRegister(0));
+ __ ffint_d_l(i.OutputDoubleRegister(), scratch);
+ break;
+ }
+ case kLoong64Uint32ToFloat64: {
+ __ Ffint_d_uw(i.OutputDoubleRegister(), i.InputRegister(0));
+ break;
+ }
+ case kLoong64Uint64ToFloat64: {
+ __ Ffint_d_ul(i.OutputDoubleRegister(), i.InputRegister(0));
+ break;
+ }
+ case kLoong64Uint64ToFloat32: {
+ __ Ffint_s_ul(i.OutputDoubleRegister(), i.InputRegister(0));
+ break;
+ }
+ case kLoong64Float64ToInt32: {
+ FPURegister scratch = kScratchDoubleReg;
+ __ ftintrz_w_d(scratch, i.InputDoubleRegister(0));
+ __ movfr2gr_s(i.OutputRegister(), scratch);
+ break;
+ }
+ case kLoong64Float32ToInt32: {
+ FPURegister scratch_d = kScratchDoubleReg;
+ bool set_overflow_to_min_i32 = MiscField::decode(instr->opcode());
+ __ ftintrz_w_s(scratch_d, i.InputDoubleRegister(0));
+ __ movfr2gr_s(i.OutputRegister(), scratch_d);
+ if (set_overflow_to_min_i32) {
+ UseScratchRegisterScope temps(tasm());
+ Register scratch = temps.Acquire();
+ // Avoid INT32_MAX as an overflow indicator and use INT32_MIN instead,
+ // because INT32_MIN allows easier out-of-bounds detection.
+ __ addi_w(scratch, i.OutputRegister(), 1);
+ __ slt(scratch, scratch, i.OutputRegister());
+ __ add_w(i.OutputRegister(), i.OutputRegister(), scratch);
+ }
+ break;
+ }
+ case kLoong64Float32ToInt64: {
+ FPURegister scratch_d = kScratchDoubleReg;
+
+ bool load_status = instr->OutputCount() > 1;
+ // Other arches use round to zero here, so we follow.
+ __ ftintrz_l_s(scratch_d, i.InputDoubleRegister(0));
+ __ movfr2gr_d(i.OutputRegister(), scratch_d);
+ if (load_status) {
+ Register output2 = i.OutputRegister(1);
+ __ movfcsr2gr(output2, FCSR2);
+ // Check for overflow and NaNs.
+ __ And(output2, output2,
+ kFCSROverflowCauseMask | kFCSRInvalidOpCauseMask);
+ __ Slt(output2, zero_reg, output2);
+ __ xori(output2, output2, 1);
+ }
+ break;
+ }
+ case kLoong64Float64ToInt64: {
+ UseScratchRegisterScope temps(tasm());
+ Register scratch = temps.Acquire();
+ FPURegister scratch_d = kScratchDoubleReg;
+
+ bool set_overflow_to_min_i64 = MiscField::decode(instr->opcode());
+ bool load_status = instr->OutputCount() > 1;
+ // Other arches use round to zero here, so we follow.
+ __ ftintrz_l_d(scratch_d, i.InputDoubleRegister(0));
+ __ movfr2gr_d(i.OutputRegister(0), scratch_d);
+ if (load_status) {
+ Register output2 = i.OutputRegister(1);
+ __ movfcsr2gr(output2, FCSR2);
+ // Check for overflow and NaNs.
+ __ And(output2, output2,
+ kFCSROverflowCauseMask | kFCSRInvalidOpCauseMask);
+ __ Slt(output2, zero_reg, output2);
+ __ xori(output2, output2, 1);
+ }
+ if (set_overflow_to_min_i64) {
+ // Avoid INT64_MAX as an overflow indicator and use INT64_MIN instead,
+ // because INT64_MIN allows easier out-of-bounds detection.
+ __ addi_d(scratch, i.OutputRegister(), 1);
+ __ slt(scratch, scratch, i.OutputRegister());
+ __ add_d(i.OutputRegister(), i.OutputRegister(), scratch);
+ }
+ break;
+ }
+ case kLoong64Float64ToUint32: {
+ FPURegister scratch = kScratchDoubleReg;
+ __ Ftintrz_uw_d(i.OutputRegister(), i.InputDoubleRegister(0), scratch);
+ break;
+ }
+ case kLoong64Float32ToUint32: {
+ FPURegister scratch = kScratchDoubleReg;
+ bool set_overflow_to_min_i32 = MiscField::decode(instr->opcode());
+ __ Ftintrz_uw_s(i.OutputRegister(), i.InputDoubleRegister(0), scratch);
+ if (set_overflow_to_min_i32) {
+ UseScratchRegisterScope temps(tasm());
+ Register scratch = temps.Acquire();
+ // Avoid UINT32_MAX as an overflow indicator and use 0 instead,
+ // because 0 allows easier out-of-bounds detection.
+ __ addi_w(scratch, i.OutputRegister(), 1);
+ __ Movz(i.OutputRegister(), zero_reg, scratch);
+ }
+ break;
+ }
+ case kLoong64Float32ToUint64: {
+ FPURegister scratch = kScratchDoubleReg;
+ Register result = instr->OutputCount() > 1 ? i.OutputRegister(1) : no_reg;
+ __ Ftintrz_ul_s(i.OutputRegister(), i.InputDoubleRegister(0), scratch,
+ result);
+ break;
+ }
+ case kLoong64Float64ToUint64: {
+ FPURegister scratch = kScratchDoubleReg;
+ Register result = instr->OutputCount() > 1 ? i.OutputRegister(1) : no_reg;
+ __ Ftintrz_ul_d(i.OutputRegister(0), i.InputDoubleRegister(0), scratch,
+ result);
+ break;
+ }
+ case kLoong64BitcastDL:
+ __ movfr2gr_d(i.OutputRegister(), i.InputDoubleRegister(0));
+ break;
+ case kLoong64BitcastLD:
+ __ movgr2fr_d(i.OutputDoubleRegister(), i.InputRegister(0));
+ break;
+ case kLoong64Float64ExtractLowWord32:
+ __ FmoveLow(i.OutputRegister(), i.InputDoubleRegister(0));
+ break;
+ case kLoong64Float64ExtractHighWord32:
+ __ movfrh2gr_s(i.OutputRegister(), i.InputDoubleRegister(0));
+ break;
+ case kLoong64Float64InsertLowWord32:
+ __ FmoveLow(i.OutputDoubleRegister(), i.InputRegister(1));
+ break;
+ case kLoong64Float64InsertHighWord32:
+ __ movgr2frh_w(i.OutputDoubleRegister(), i.InputRegister(1));
+ break;
+ // ... more basic instructions ...
+
+ case kLoong64Ext_w_b:
+ __ ext_w_b(i.OutputRegister(), i.InputRegister(0));
+ break;
+ case kLoong64Ext_w_h:
+ __ ext_w_h(i.OutputRegister(), i.InputRegister(0));
+ break;
+ case kLoong64Ld_bu:
+ __ Ld_bu(i.OutputRegister(), i.MemoryOperand());
+ break;
+ case kLoong64Ld_b:
+ __ Ld_b(i.OutputRegister(), i.MemoryOperand());
+ break;
+ case kLoong64St_b:
+ __ St_b(i.InputOrZeroRegister(2), i.MemoryOperand());
+ break;
+ case kLoong64Ld_hu:
+ __ Ld_hu(i.OutputRegister(), i.MemoryOperand());
+ break;
+ case kLoong64Ld_h:
+ __ Ld_h(i.OutputRegister(), i.MemoryOperand());
+ break;
+ case kLoong64St_h:
+ __ St_h(i.InputOrZeroRegister(2), i.MemoryOperand());
+ break;
+ case kLoong64Ld_w:
+ __ Ld_w(i.OutputRegister(), i.MemoryOperand());
+ break;
+ case kLoong64Ld_wu:
+ __ Ld_wu(i.OutputRegister(), i.MemoryOperand());
+ break;
+ case kLoong64Ld_d:
+ __ Ld_d(i.OutputRegister(), i.MemoryOperand());
+ break;
+ case kLoong64St_w:
+ __ St_w(i.InputOrZeroRegister(2), i.MemoryOperand());
+ break;
+ case kLoong64St_d:
+ __ St_d(i.InputOrZeroRegister(2), i.MemoryOperand());
+ break;
+ case kLoong64Fld_s: {
+ __ Fld_s(i.OutputSingleRegister(), i.MemoryOperand());
+ break;
+ }
+ case kLoong64Fst_s: {
+ size_t index = 0;
+ MemOperand operand = i.MemoryOperand(&index);
+ FPURegister ft = i.InputOrZeroSingleRegister(index);
+ if (ft == kDoubleRegZero && !__ IsDoubleZeroRegSet()) {
+ __ Move(kDoubleRegZero, 0.0);
+ }
+
+ __ Fst_s(ft, operand);
+ break;
+ }
+ case kLoong64Fld_d:
+ __ Fld_d(i.OutputDoubleRegister(), i.MemoryOperand());
+ break;
+ case kLoong64Fst_d: {
+ FPURegister ft = i.InputOrZeroDoubleRegister(2);
+ if (ft == kDoubleRegZero && !__ IsDoubleZeroRegSet()) {
+ __ Move(kDoubleRegZero, 0.0);
+ }
+
+ __ Fst_d(ft, i.MemoryOperand());
+ break;
+ }
+ case kLoong64Dbar: {
+ __ dbar(0);
+ break;
+ }
+ case kLoong64Push:
+ if (instr->InputAt(0)->IsFPRegister()) {
+ __ Fst_d(i.InputDoubleRegister(0), MemOperand(sp, -kDoubleSize));
+ __ Sub_d(sp, sp, Operand(kDoubleSize));
+ frame_access_state()->IncreaseSPDelta(kDoubleSize / kSystemPointerSize);
+ } else {
+ __ Push(i.InputRegister(0));
+ frame_access_state()->IncreaseSPDelta(1);
+ }
+ break;
+ case kLoong64Peek: {
+ 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) {
+ __ Fld_d(i.OutputDoubleRegister(), MemOperand(fp, offset));
+ } else if (op->representation() == MachineRepresentation::kFloat32) {
+ __ Fld_s(i.OutputSingleRegister(0), MemOperand(fp, offset));
+ } else {
+ DCHECK_EQ(MachineRepresentation::kSimd128, op->representation());
+ abort();
+ }
+ } else {
+ __ Ld_d(i.OutputRegister(0), MemOperand(fp, offset));
+ }
+ break;
+ }
+ case kLoong64StackClaim: {
+ __ Sub_d(sp, sp, Operand(i.InputInt32(0)));
+ frame_access_state()->IncreaseSPDelta(i.InputInt32(0) /
+ kSystemPointerSize);
+ break;
+ }
+ case kLoong64Poke: {
+ if (instr->InputAt(0)->IsFPRegister()) {
+ __ Fst_d(i.InputDoubleRegister(0), MemOperand(sp, i.InputInt32(1)));
+ } else {
+ __ St_d(i.InputRegister(0), MemOperand(sp, i.InputInt32(1)));
+ }
+ break;
+ }
+ case kLoong64ByteSwap64: {
+ __ ByteSwapSigned(i.OutputRegister(0), i.InputRegister(0), 8);
+ break;
+ }
+ case kLoong64ByteSwap32: {
+ __ ByteSwapSigned(i.OutputRegister(0), i.InputRegister(0), 4);
+ break;
+ }
+ case kAtomicLoadInt8:
+ DCHECK_EQ(AtomicWidthField::decode(opcode), AtomicWidth::kWord32);
+ ASSEMBLE_ATOMIC_LOAD_INTEGER(Ld_b);
+ break;
+ case kAtomicLoadUint8:
+ ASSEMBLE_ATOMIC_LOAD_INTEGER(Ld_bu);
+ break;
+ case kAtomicLoadInt16:
+ DCHECK_EQ(AtomicWidthField::decode(opcode), AtomicWidth::kWord32);
+ ASSEMBLE_ATOMIC_LOAD_INTEGER(Ld_h);
+ break;
+ case kAtomicLoadUint16:
+ ASSEMBLE_ATOMIC_LOAD_INTEGER(Ld_hu);
+ break;
+ case kAtomicLoadWord32:
+ ASSEMBLE_ATOMIC_LOAD_INTEGER(Ld_w);
+ break;
+ case kLoong64Word64AtomicLoadUint32:
+ ASSEMBLE_ATOMIC_LOAD_INTEGER(Ld_wu);
+ break;
+ case kLoong64Word64AtomicLoadUint64:
+ ASSEMBLE_ATOMIC_LOAD_INTEGER(Ld_d);
+ break;
+ case kAtomicStoreWord8:
+ ASSEMBLE_ATOMIC_STORE_INTEGER(St_b);
+ break;
+ case kAtomicStoreWord16:
+ ASSEMBLE_ATOMIC_STORE_INTEGER(St_h);
+ break;
+ case kAtomicStoreWord32:
+ ASSEMBLE_ATOMIC_STORE_INTEGER(St_w);
+ break;
+ case kLoong64StoreCompressTagged:
+ case kLoong64Word64AtomicStoreWord64:
+ ASSEMBLE_ATOMIC_STORE_INTEGER(St_d);
+ break;
+ case kAtomicExchangeInt8:
+ DCHECK_EQ(AtomicWidthField::decode(opcode), AtomicWidth::kWord32);
+ ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT(Ll_w, Sc_w, true, 8, 32);
+ break;
+ case kAtomicExchangeUint8:
+ switch (AtomicWidthField::decode(opcode)) {
+ case AtomicWidth::kWord32:
+ ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT(Ll_w, Sc_w, false, 8, 32);
+ break;
+ case AtomicWidth::kWord64:
+ ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT(Ll_d, Sc_d, false, 8, 64);
+ break;
+ }
+ break;
+ case kAtomicExchangeInt16:
+ DCHECK_EQ(AtomicWidthField::decode(opcode), AtomicWidth::kWord32);
+ ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT(Ll_w, Sc_w, true, 16, 32);
+ break;
+ case kAtomicExchangeUint16:
+ switch (AtomicWidthField::decode(opcode)) {
+ case AtomicWidth::kWord32:
+ ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT(Ll_w, Sc_w, false, 16, 32);
+ break;
+ case AtomicWidth::kWord64:
+ ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT(Ll_d, Sc_d, false, 16, 64);
+ break;
+ }
+ break;
+ case kAtomicExchangeWord32:
+ switch (AtomicWidthField::decode(opcode)) {
+ case AtomicWidth::kWord32:
+ __ add_d(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1));
+ __ amswap_db_w(i.OutputRegister(0), i.InputRegister(2),
+ i.TempRegister(0));
+ break;
+ case AtomicWidth::kWord64:
+ ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT(Ll_d, Sc_d, false, 32, 64);
+ break;
+ }
+ break;
+ case kLoong64Word64AtomicExchangeUint64:
+ __ add_d(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1));
+ __ amswap_db_d(i.OutputRegister(0), i.InputRegister(2),
+ i.TempRegister(0));
+ break;
+ case kAtomicCompareExchangeInt8:
+ DCHECK_EQ(AtomicWidthField::decode(opcode), AtomicWidth::kWord32);
+ ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER_EXT(Ll_w, Sc_w, true, 8, 32);
+ break;
+ case kAtomicCompareExchangeUint8:
+ switch (AtomicWidthField::decode(opcode)) {
+ case AtomicWidth::kWord32:
+ ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER_EXT(Ll_w, Sc_w, false, 8,
+ 32);
+ break;
+ case AtomicWidth::kWord64:
+ ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER_EXT(Ll_d, Sc_d, false, 8,
+ 64);
+ break;
+ }
+ break;
+ case kAtomicCompareExchangeInt16:
+ DCHECK_EQ(AtomicWidthField::decode(opcode), AtomicWidth::kWord32);
+ ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER_EXT(Ll_w, Sc_w, true, 16, 32);
+ break;
+ case kAtomicCompareExchangeUint16:
+ switch (AtomicWidthField::decode(opcode)) {
+ case AtomicWidth::kWord32:
+ ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER_EXT(Ll_w, Sc_w, false, 16,
+ 32);
+ break;
+ case AtomicWidth::kWord64:
+ ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER_EXT(Ll_d, Sc_d, false, 16,
+ 64);
+ break;
+ }
+ break;
+ case kAtomicCompareExchangeWord32:
+ switch (AtomicWidthField::decode(opcode)) {
+ case AtomicWidth::kWord32:
+ __ slli_w(i.InputRegister(2), i.InputRegister(2), 0);
+ ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER(Ll_w, Sc_w);
+ break;
+ case AtomicWidth::kWord64:
+ ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER_EXT(Ll_d, Sc_d, false, 32,
+ 64);
+ break;
+ }
+ break;
+ case kLoong64Word64AtomicCompareExchangeUint64:
+ ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER(Ll_d, Sc_d);
+ break;
+ case kAtomicAddWord32:
+ switch (AtomicWidthField::decode(opcode)) {
+ case AtomicWidth::kWord32:
+ __ Add_d(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1));
+ __ amadd_db_w(i.OutputRegister(0), i.InputRegister(2),
+ i.TempRegister(0));
+ break;
+ case AtomicWidth::kWord64:
+ ASSEMBLE_ATOMIC_BINOP_EXT(Ll_d, Sc_d, false, 32, Add_d, 64);
+ break;
+ }
+ break;
+ case kAtomicSubWord32:
+ switch (AtomicWidthField::decode(opcode)) {
+ case AtomicWidth::kWord32:
+ ASSEMBLE_ATOMIC_BINOP(Ll_w, Sc_w, Sub_w);
+ break;
+ case AtomicWidth::kWord64:
+ ASSEMBLE_ATOMIC_BINOP_EXT(Ll_d, Sc_d, false, 32, Sub_d, 64);
+ break;
+ }
+ break;
+ case kAtomicAndWord32:
+ switch (AtomicWidthField::decode(opcode)) {
+ case AtomicWidth::kWord32:
+ __ Add_d(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1));
+ __ amand_db_w(i.OutputRegister(0), i.InputRegister(2),
+ i.TempRegister(0));
+ break;
+ case AtomicWidth::kWord64:
+ ASSEMBLE_ATOMIC_BINOP_EXT(Ll_d, Sc_d, false, 32, And, 64);
+ break;
+ }
+ break;
+ case kAtomicOrWord32:
+ switch (AtomicWidthField::decode(opcode)) {
+ case AtomicWidth::kWord32:
+ __ Add_d(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1));
+ __ amor_db_w(i.OutputRegister(0), i.InputRegister(2),
+ i.TempRegister(0));
+ break;
+ case AtomicWidth::kWord64:
+ ASSEMBLE_ATOMIC_BINOP_EXT(Ll_d, Sc_d, false, 32, Or, 64);
+ break;
+ }
+ break;
+ case kAtomicXorWord32:
+ switch (AtomicWidthField::decode(opcode)) {
+ case AtomicWidth::kWord32:
+ __ Add_d(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1));
+ __ amxor_db_w(i.OutputRegister(0), i.InputRegister(2),
+ i.TempRegister(0));
+ break;
+ case AtomicWidth::kWord64:
+ ASSEMBLE_ATOMIC_BINOP_EXT(Ll_d, Sc_d, false, 32, Xor, 64);
+ break;
+ }
+ break;
+#define ATOMIC_BINOP_CASE(op, inst32, inst64) \
+ case kAtomic##op##Int8: \
+ DCHECK_EQ(AtomicWidthField::decode(opcode), AtomicWidth::kWord32); \
+ ASSEMBLE_ATOMIC_BINOP_EXT(Ll_w, Sc_w, true, 8, inst32, 32); \
+ break; \
+ case kAtomic##op##Uint8: \
+ switch (AtomicWidthField::decode(opcode)) { \
+ case AtomicWidth::kWord32: \
+ ASSEMBLE_ATOMIC_BINOP_EXT(Ll_w, Sc_w, false, 8, inst32, 32); \
+ break; \
+ case AtomicWidth::kWord64: \
+ ASSEMBLE_ATOMIC_BINOP_EXT(Ll_d, Sc_d, false, 8, inst64, 64); \
+ break; \
+ } \
+ break; \
+ case kAtomic##op##Int16: \
+ DCHECK_EQ(AtomicWidthField::decode(opcode), AtomicWidth::kWord32); \
+ ASSEMBLE_ATOMIC_BINOP_EXT(Ll_w, Sc_w, true, 16, inst32, 32); \
+ break; \
+ case kAtomic##op##Uint16: \
+ switch (AtomicWidthField::decode(opcode)) { \
+ case AtomicWidth::kWord32: \
+ ASSEMBLE_ATOMIC_BINOP_EXT(Ll_w, Sc_w, false, 16, inst32, 32); \
+ break; \
+ case AtomicWidth::kWord64: \
+ ASSEMBLE_ATOMIC_BINOP_EXT(Ll_d, Sc_d, false, 16, inst64, 64); \
+ break; \
+ } \
+ break;
+ ATOMIC_BINOP_CASE(Add, Add_w, Add_d)
+ ATOMIC_BINOP_CASE(Sub, Sub_w, Sub_d)
+ ATOMIC_BINOP_CASE(And, And, And)
+ ATOMIC_BINOP_CASE(Or, Or, Or)
+ ATOMIC_BINOP_CASE(Xor, Xor, Xor)
+#undef ATOMIC_BINOP_CASE
+
+ case kLoong64Word64AtomicAddUint64:
+ __ Add_d(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1));
+ __ amadd_db_d(i.OutputRegister(0), i.InputRegister(2), i.TempRegister(0));
+ break;
+ case kLoong64Word64AtomicSubUint64:
+ ASSEMBLE_ATOMIC_BINOP(Ll_d, Sc_d, Sub_d);
+ break;
+ case kLoong64Word64AtomicAndUint64:
+ __ Add_d(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1));
+ __ amand_db_d(i.OutputRegister(0), i.InputRegister(2), i.TempRegister(0));
+ break;
+ case kLoong64Word64AtomicOrUint64:
+ __ Add_d(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1));
+ __ amor_db_d(i.OutputRegister(0), i.InputRegister(2), i.TempRegister(0));
+ break;
+ case kLoong64Word64AtomicXorUint64:
+ __ Add_d(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1));
+ __ amxor_db_d(i.OutputRegister(0), i.InputRegister(2), i.TempRegister(0));
+ break;
+#undef ATOMIC_BINOP_CASE
+ case kLoong64S128Const:
+ case kLoong64S128Zero:
+ case kLoong64I32x4Splat:
+ case kLoong64I32x4ExtractLane:
+ case kLoong64I32x4Add:
+ case kLoong64I32x4ReplaceLane:
+ case kLoong64I32x4Sub:
+ case kLoong64F64x2Abs:
+ default:
+ break;
+ }
+ return kSuccess;
+}
+
+#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->
+ Loong64OperandConverter i(gen, instr);
+
+ Condition cc = kNoCondition;
+ // LOONG64 does not have condition code flags, so compare and branch are
+ // implemented differently than on the other arch's. The compare operations
+ // emit loong64 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() == kLoong64Tst) {
+ cc = FlagsConditionToConditionTst(condition);
+ __ Branch(tlabel, cc, t8, Operand(zero_reg));
+ } else if (instr->arch_opcode() == kLoong64Add_d ||
+ instr->arch_opcode() == kLoong64Sub_d) {
+ UseScratchRegisterScope temps(tasm);
+ Register scratch = temps.Acquire();
+ Register scratch2 = temps.Acquire();
+ cc = FlagsConditionToConditionOvf(condition);
+ __ srai_d(scratch, i.OutputRegister(), 32);
+ __ srai_w(scratch2, i.OutputRegister(), 31);
+ __ Branch(tlabel, cc, scratch2, Operand(scratch));
+ } else if (instr->arch_opcode() == kLoong64AddOvf_d ||
+ instr->arch_opcode() == kLoong64SubOvf_d) {
+ switch (condition) {
+ // Overflow occurs if overflow register is negative
+ case kOverflow:
+ __ Branch(tlabel, lt, t8, Operand(zero_reg));
+ break;
+ case kNotOverflow:
+ __ Branch(tlabel, ge, t8, Operand(zero_reg));
+ break;
+ default:
+ UNSUPPORTED_COND(instr->arch_opcode(), condition);
+ }
+ } else if (instr->arch_opcode() == kLoong64MulOvf_w) {
+ // Overflow occurs if overflow register is not zero
+ switch (condition) {
+ case kOverflow:
+ __ Branch(tlabel, ne, t8, Operand(zero_reg));
+ break;
+ case kNotOverflow:
+ __ Branch(tlabel, eq, t8, Operand(zero_reg));
+ break;
+ default:
+ UNSUPPORTED_COND(kLoong64MulOvf_w, condition);
+ }
+ } else if (instr->arch_opcode() == kLoong64Cmp) {
+ cc = FlagsConditionToConditionCmp(condition);
+ __ Branch(tlabel, cc, i.InputRegister(0), i.InputOperand(1));
+ } else if (instr->arch_opcode() == kArchStackPointerGreaterThan) {
+ cc = FlagsConditionToConditionCmp(condition);
+ DCHECK((cc == ls) || (cc == hi));
+ if (cc == ls) {
+ __ xori(i.TempRegister(0), i.TempRegister(0), 1);
+ }
+ __ Branch(tlabel, ne, i.TempRegister(0), Operand(zero_reg));
+ } else if (instr->arch_opcode() == kLoong64Float32Cmp ||
+ instr->arch_opcode() == kLoong64Float64Cmp) {
+ 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);
+}
+
+#undef UNSUPPORTED_COND
+
+void CodeGenerator::AssembleArchDeoptBranch(Instruction* instr,
+ BranchInfo* branch) {
+ AssembleArchBranch(instr, branch);
+}
+
+void CodeGenerator::AssembleArchJump(RpoNumber target) {
+ if (!IsNextInAssemblyOrder(target)) __ Branch(GetLabel(target));
+}
+
+#if V8_ENABLE_WEBASSEMBLY
+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 {
+ Loong64OperandConverter 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->ParameterSlotCount());
+ 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);
+ 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);
+}
+#endif // V8_ENABLE_WEBASSEMBLY
+
+// Assembles boolean materializations after an instruction.
+void CodeGenerator::AssembleArchBoolean(Instruction* instr,
+ FlagsCondition condition) {
+ Loong64OperandConverter 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;
+ // Loong64 does not have condition code flags, so compare and branch are
+ // implemented differently than on the other arch's. The compare operations
+ // emit loong64 pseudo-instructions, which are checked and handled here.
+
+ if (instr->arch_opcode() == kLoong64Tst) {
+ cc = FlagsConditionToConditionTst(condition);
+ if (cc == eq) {
+ __ Sltu(result, t8, 1);
+ } else {
+ __ Sltu(result, zero_reg, t8);
+ }
+ return;
+ } else if (instr->arch_opcode() == kLoong64Add_d ||
+ instr->arch_opcode() == kLoong64Sub_d) {
+ UseScratchRegisterScope temps(tasm());
+ Register scratch = temps.Acquire();
+ cc = FlagsConditionToConditionOvf(condition);
+ // Check for overflow creates 1 or 0 for result.
+ __ srli_d(scratch, i.OutputRegister(), 63);
+ __ srli_w(result, i.OutputRegister(), 31);
+ __ xor_(result, scratch, result);
+ if (cc == eq) // Toggle result for not overflow.
+ __ xori(result, result, 1);
+ return;
+ } else if (instr->arch_opcode() == kLoong64AddOvf_d ||
+ instr->arch_opcode() == kLoong64SubOvf_d) {
+ // Overflow occurs if overflow register is negative
+ __ slt(result, t8, zero_reg);
+ } else if (instr->arch_opcode() == kLoong64MulOvf_w) {
+ // Overflow occurs if overflow register is not zero
+ __ Sgtu(result, t8, zero_reg);
+ } else if (instr->arch_opcode() == kLoong64Cmp) {
+ 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_int12(-right.immediate())) {
+ if (right.immediate() == 0) {
+ if (cc == eq) {
+ __ Sltu(result, left, 1);
+ } else {
+ __ Sltu(result, zero_reg, left);
+ }
+ } else {
+ __ Add_d(result, left, Operand(-right.immediate()));
+ if (cc == eq) {
+ __ Sltu(result, result, 1);
+ } else {
+ __ Sltu(result, zero_reg, result);
+ }
+ }
+ } else {
+ __ Xor(result, left, Operand(right));
+ 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() == kLoong64Float64Cmp ||
+ instr->arch_opcode() == kLoong64Float32Cmp) {
+ 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);
+ {
+ __ movcf2gr(result, FCC0);
+ if (!predicate) {
+ __ xori(result, result, 1);
+ }
+ }
+ return;
+ } else if (instr->arch_opcode() == kArchStackPointerGreaterThan) {
+ cc = FlagsConditionToConditionCmp(condition);
+ DCHECK((cc == ls) || (cc == hi));
+ if (cc == ls) {
+ __ xori(i.OutputRegister(), i.TempRegister(0), 1);
+ }
+ return;
+ } else {
+ PrintF("AssembleArchBranch Unimplemented arch_opcode is : %d\n",
+ instr->arch_opcode());
+ TRACE_UNIMPL();
+ UNIMPLEMENTED();
+ }
+}
+
+void CodeGenerator::AssembleArchBinarySearchSwitch(Instruction* instr) {
+ Loong64OperandConverter 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) {
+ Loong64OperandConverter 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::AssembleArchSelect(Instruction* instr,
+ FlagsCondition condition) {
+ UNIMPLEMENTED();
+}
+
+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);
+ frame->AllocateSavedCalleeRegisterSlots(count);
+ }
+}
+
+void CodeGenerator::AssembleConstructFrame() {
+ auto call_descriptor = linkage()->GetIncomingDescriptor();
+
+ if (frame_access_state()->has_frame()) {
+ if (call_descriptor->IsCFunctionCall()) {
+#if V8_ENABLE_WEBASSEMBLY
+ if (info()->GetOutputStackFrameType() == StackFrame::C_WASM_ENTRY) {
+ __ StubPrologue(StackFrame::C_WASM_ENTRY);
+ // Reserve stack space for saving the c_entry_fp later.
+ __ Sub_d(sp, sp, Operand(kSystemPointerSize));
+#else
+ // For balance.
+ if (false) {
+#endif // V8_ENABLE_WEBASSEMBLY
+ } else {
+ __ Push(ra, fp);
+ __ mov(fp, sp);
+ }
+ } else if (call_descriptor->IsJSFunctionCall()) {
+ __ Prologue();
+ } else {
+ __ StubPrologue(info()->GetOutputStackFrameType());
+#if V8_ENABLE_WEBASSEMBLY
+ 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_d(kJSFunctionRegister,
+ FieldMemOperand(kWasmInstanceRegister, Tuple2::kValue2Offset));
+ __ Ld_d(kWasmInstanceRegister,
+ FieldMemOperand(kWasmInstanceRegister, Tuple2::kValue1Offset));
+ __ Push(kWasmInstanceRegister);
+ if (call_descriptor->IsWasmCapiFunction()) {
+ // Reserve space for saving the PC later.
+ __ Sub_d(sp, sp, Operand(kSystemPointerSize));
+ }
+ }
+#endif // V8_ENABLE_WEBASSEMBLY
+ }
+ }
+
+ 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.
+ __ RecordComment("-- OSR entrypoint --");
+ osr_pc_offset_ = __ pc_offset();
+ required_slots -= osr_helper()->UnoptimizedFrameSlots();
+ }
+
+ const RegList saves = call_descriptor->CalleeSavedRegisters();
+ const RegList saves_fpu = call_descriptor->CalleeSavedFPRegisters();
+
+ if (required_slots > 0) {
+ DCHECK(frame_access_state()->has_frame());
+#if V8_ENABLE_WEBASSEMBLY
+ if (info()->IsWasm() && required_slots * kSystemPointerSize > 4 * KB) {
+ // 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 * KB) {
+ UseScratchRegisterScope temps(tasm());
+ Register scratch = temps.Acquire();
+ __ Ld_d(scratch, FieldMemOperand(
+ kWasmInstanceRegister,
+ WasmInstanceObject::kRealStackLimitAddressOffset));
+ __ Ld_d(scratch, MemOperand(scratch, 0));
+ __ Add_d(scratch, scratch,
+ Operand(required_slots * kSystemPointerSize));
+ __ Branch(&done, uge, sp, Operand(scratch));
+ }
+
+ __ Call(wasm::WasmCode::kWasmStackOverflow, RelocInfo::WASM_STUB_CALL);
+ // The call does not return, hence we can ignore any references and just
+ // define an empty safepoint.
+ ReferenceMap* reference_map = zone()->New<ReferenceMap>(zone());
+ RecordSafepoint(reference_map);
+ if (FLAG_debug_code) {
+ __ stop();
+ }
+
+ __ bind(&done);
+ }
+#endif // V8_ENABLE_WEBASSEMBLY
+ }
+
+ 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) {
+ __ Sub_d(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);
+ }
+
+ if (returns != 0) {
+ // Create space for returns.
+ __ Sub_d(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) {
+ __ Add_d(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);
+ }
+
+ Loong64OperandConverter g(this, nullptr);
+
+ const int parameter_slots =
+ static_cast<int>(call_descriptor->ParameterSlotCount());
+
+ // {aditional_pop_count} is only greater than zero if {parameter_slots = 0}.
+ // Check RawMachineAssembler::PopAndReturn.
+ if (parameter_slots != 0) {
+ if (additional_pop_count->IsImmediate()) {
+ DCHECK_EQ(g.ToConstant(additional_pop_count).ToInt32(), 0);
+ } else if (FLAG_debug_code) {
+ __ Assert(eq, AbortReason::kUnexpectedAdditionalPopValue,
+ g.ToRegister(additional_pop_count),
+ Operand(static_cast<int64_t>(0)));
+ }
+ }
+
+ // Functions with JS linkage have at least one parameter (the receiver).
+ // If {parameter_slots} == 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_slots != 0;
+
+ 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_d(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).
+ __ Add_d(t0, t0, Operand(1)); // Also pop the receiver.
+ if (parameter_slots > 1) {
+ __ li(t1, parameter_slots);
+ __ slt(t2, t0, t1);
+ __ Movn(t0, t1, t2);
+ }
+ __ slli_d(t0, t0, kSystemPointerSizeLog2);
+ __ add_d(sp, sp, t0);
+ } else if (additional_pop_count->IsImmediate()) {
+ int additional_count = g.ToConstant(additional_pop_count).ToInt32();
+ __ Drop(parameter_slots + additional_count);
+ } else {
+ Register pop_reg = g.ToRegister(additional_pop_count);
+ __ Drop(parameter_slots);
+ __ slli_d(pop_reg, pop_reg, kSystemPointerSizeLog2);
+ __ add_d(sp, sp, pop_reg);
+ }
+ __ Ret();
+}
+
+void CodeGenerator::FinishCode() {}
+
+void CodeGenerator::PrepareForDeoptimizationExits(
+ ZoneDeque<DeoptimizationExit*>* exits) {}
+
+void CodeGenerator::AssembleMove(InstructionOperand* source,
+ InstructionOperand* destination) {
+ Loong64OperandConverter 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 {
+ __ St_d(src, g.ToMemOperand(destination));
+ }
+ } else if (source->IsStackSlot()) {
+ DCHECK(destination->IsRegister() || destination->IsStackSlot());
+ MemOperand src = g.ToMemOperand(source);
+ if (destination->IsRegister()) {
+ __ Ld_d(g.ToRegister(destination), src);
+ } else {
+ UseScratchRegisterScope temps(tasm());
+ Register scratch = temps.Acquire();
+ __ Ld_d(scratch, src);
+ __ St_d(scratch, g.ToMemOperand(destination));
+ }
+ } else if (source->IsConstant()) {
+ Constant src = g.ToConstant(source);
+ if (destination->IsRegister() || destination->IsStackSlot()) {
+ UseScratchRegisterScope temps(tasm());
+ Register scratch = temps.Acquire();
+ Register dst =
+ destination->IsRegister() ? g.ToRegister(destination) : scratch;
+ 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 V8_ENABLE_WEBASSEMBLY
+ if (RelocInfo::IsWasmReference(src.rmode()))
+ __ li(dst, Operand(src.ToInt64(), src.rmode()));
+ else
+#endif // V8_ENABLE_WEBASSEMBLY
+ __ 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 LOONG64.
+ }
+ if (destination->IsStackSlot()) __ St_d(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) {
+ __ St_d(zero_reg, dst);
+ } else {
+ UseScratchRegisterScope temps(tasm());
+ Register scratch = temps.Acquire();
+ __ li(scratch, Operand(bit_cast<int32_t>(src.ToFloat32())));
+ __ St_d(scratch, 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()) {
+ __ Fst_d(dst, g.ToMemOperand(destination));
+ }
+ }
+ } else if (source->IsFPRegister()) {
+ FPURegister src = g.ToDoubleRegister(source);
+ if (destination->IsFPRegister()) {
+ FPURegister dst = g.ToDoubleRegister(destination);
+ __ Move(dst, src);
+ } else {
+ DCHECK(destination->IsFPStackSlot());
+ __ Fst_d(src, g.ToMemOperand(destination));
+ }
+ } else if (source->IsFPStackSlot()) {
+ DCHECK(destination->IsFPRegister() || destination->IsFPStackSlot());
+ MemOperand src = g.ToMemOperand(source);
+ if (destination->IsFPRegister()) {
+ __ Fld_d(g.ToDoubleRegister(destination), src);
+ } else {
+ DCHECK(destination->IsFPStackSlot());
+ FPURegister temp = kScratchDoubleReg;
+ __ Fld_d(temp, src);
+ __ Fst_d(temp, g.ToMemOperand(destination));
+ }
+ } else {
+ UNREACHABLE();
+ }
+}
+
+void CodeGenerator::AssembleSwap(InstructionOperand* source,
+ InstructionOperand* destination) {
+ Loong64OperandConverter g(this, nullptr);
+ // Dispatch on the source and destination operand kinds. Not all
+ // combinations are possible.
+ if (source->IsRegister()) {
+ UseScratchRegisterScope temps(tasm());
+ Register scratch = temps.Acquire();
+ // Register-register.
+ Register src = g.ToRegister(source);
+ if (destination->IsRegister()) {
+ Register dst = g.ToRegister(destination);
+ __ Move(scratch, src);
+ __ Move(src, dst);
+ __ Move(dst, scratch);
+ } else {
+ DCHECK(destination->IsStackSlot());
+ MemOperand dst = g.ToMemOperand(destination);
+ __ mov(scratch, src);
+ __ Ld_d(src, dst);
+ __ St_d(scratch, dst);
+ }
+ } else if (source->IsStackSlot()) {
+ DCHECK(destination->IsStackSlot());
+ // TODO(LOONG_dev): LOONG64 Optimize scratch registers usage
+ // Since the Ld instruction may need a scratch reg,
+ // we should not use both of the two scratch registers in
+ // UseScratchRegisterScope here.
+ UseScratchRegisterScope temps(tasm());
+ Register scratch = temps.Acquire();
+ FPURegister scratch_d = kScratchDoubleReg;
+ MemOperand src = g.ToMemOperand(source);
+ MemOperand dst = g.ToMemOperand(destination);
+ __ Ld_d(scratch, src);
+ __ Fld_d(scratch_d, dst);
+ __ St_d(scratch, dst);
+ __ Fst_d(scratch_d, src);
+ } else if (source->IsFPRegister()) {
+ FPURegister scratch_d = kScratchDoubleReg;
+ FPURegister src = g.ToDoubleRegister(source);
+ if (destination->IsFPRegister()) {
+ FPURegister dst = g.ToDoubleRegister(destination);
+ __ Move(scratch_d, src);
+ __ Move(src, dst);
+ __ Move(dst, scratch_d);
+ } else {
+ DCHECK(destination->IsFPStackSlot());
+ MemOperand dst = g.ToMemOperand(destination);
+ __ Move(scratch_d, src);
+ __ Fld_d(src, dst);
+ __ Fst_d(scratch_d, dst);
+ }
+ } else if (source->IsFPStackSlot()) {
+ DCHECK(destination->IsFPStackSlot());
+ UseScratchRegisterScope temps(tasm());
+ Register scratch = temps.Acquire();
+ MemOperand src0 = g.ToMemOperand(source);
+ MemOperand src1(src0.base(), src0.offset() + kIntSize);
+ MemOperand dst0 = g.ToMemOperand(destination);
+ MemOperand dst1(dst0.base(), dst0.offset() + kIntSize);
+ FPURegister scratch_d = kScratchDoubleReg;
+ __ Fld_d(scratch_d, dst0); // Save destination in temp_1.
+ __ Ld_w(scratch, src0); // Then use scratch to copy source to destination.
+ __ St_w(scratch, dst0);
+ __ Ld_w(scratch, src1);
+ __ St_w(scratch, dst1);
+ __ Fst_d(scratch_d, src0);
+ } else {
+ // No other combinations are possible.
+ UNREACHABLE();
+ }
+}
+
+void CodeGenerator::AssembleJumpTable(Label** targets, size_t target_count) {
+ // On 64-bit LOONG64 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