v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
code-generator-loong64.cc
Go to the documentation of this file.
1// Copyright 2021 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
16#include "src/compiler/osr.h"
18
19namespace v8 {
20namespace internal {
21namespace compiler {
22
23#define __ masm()->
24
25#define TRACE(...) PrintF(__VA_ARGS__)
26
27// Adds Loong64-specific methods to convert InstructionOperands.
29 public:
32
34 return ToSingleRegister(instr_->OutputAt(index));
35 }
36
38 return ToSingleRegister(instr_->InputAt(index));
39 }
40
42 // Single (Float) and Double register namespace is same on LOONG64,
43 // both are typedefs of FPURegister.
44 return ToDoubleRegister(op);
45 }
46
48 if (instr_->InputAt(index)->IsImmediate()) {
49 DCHECK_EQ(0, InputInt32(index));
50 return zero_reg;
51 }
52 return InputRegister(index);
53 }
54
56 if (instr_->InputAt(index)->IsImmediate()) return kDoubleRegZero;
57
58 return InputDoubleRegister(index);
59 }
60
62 if (instr_->InputAt(index)->IsImmediate()) return kDoubleRegZero;
63
64 return InputSingleRegister(index);
65 }
66
67 Operand InputImmediate(size_t index) {
68 Constant constant = ToConstant(instr_->InputAt(index));
69 switch (constant.type()) {
71 return Operand(constant.ToInt32());
73 return Operand(constant.ToInt64());
75 return Operand::EmbeddedNumber(constant.ToFloat32());
77 return Operand::EmbeddedNumber(constant.ToFloat64().value());
79 RootIndex root_index;
80 if (gen_->isolate()->roots_table().IsRootHandle(constant.ToHeapObject(),
81 &root_index)) {
84 Tagged_t ptr =
86 return Operand(ptr);
87 }
88 return Operand(constant.ToHeapObject());
89 }
92 break;
94 UNREACHABLE(); // TODO(titzer): RPO immediates on loong64?
95 }
97 }
98
99 Operand InputOperand(size_t index) {
100 InstructionOperand* op = instr_->InputAt(index);
101 if (op->IsRegister()) {
102 return Operand(ToRegister(op));
103 }
104 return InputImmediate(index);
105 }
106
107 MemOperand MemoryOperand(size_t* first_index) {
108 const size_t index = *first_index;
110 case kMode_None:
111 break;
112 case kMode_Root:
113 *first_index += 1;
114 return MemOperand(kRootRegister, InputInt32(index));
115 case kMode_MRI:
116 *first_index += 2;
117 return MemOperand(InputRegister(index + 0), InputInt32(index + 1));
118 case kMode_MRR:
119 *first_index += 2;
120 return MemOperand(InputRegister(index + 0), InputRegister(index + 1));
121 }
122 UNREACHABLE();
123 }
124
125 MemOperand MemoryOperand(size_t index = 0) { return MemoryOperand(&index); }
126
132
133 MemOperand SlotToMemOperand(int slot) const {
135 return MemOperand(offset.from_stack_pointer() ? sp : fp, offset.offset());
136 }
137};
138
139static inline bool HasRegisterInput(Instruction* instr, size_t index) {
140 return instr->InputAt(index)->IsRegister();
141}
142
143namespace {
144
145class OutOfLineRecordWrite final : public OutOfLineCode {
146 public:
147 OutOfLineRecordWrite(
148 CodeGenerator* gen, Register object, Operand offset, Register value,
149 RecordWriteMode mode, StubCallMode stub_mode,
150 IndirectPointerTag indirect_pointer_tag = kIndirectPointerNullTag)
151 : OutOfLineCode(gen),
152 object_(object),
154 value_(value),
155 mode_(mode),
156#if V8_ENABLE_WEBASSEMBLY
157 stub_mode_(stub_mode),
158#endif // V8_ENABLE_WEBASSEMBLY
159 must_save_lr_(!gen->frame_access_state()->has_frame()),
160 zone_(gen->zone()),
161 indirect_pointer_tag_(indirect_pointer_tag) {
162 }
163
164 void Generate() final {
165 // When storing an indirect pointer, the value will always be a
166 // full/decompressed pointer.
169 __ DecompressTagged(value_, value_);
170 }
171
173 exit());
174
175 SaveFPRegsMode const save_fp_mode = frame()->DidAllocateDoubleRegisters()
178 if (must_save_lr_) {
179 // We need to save and restore ra if the frame was elided.
180 __ Push(ra);
181 }
183 __ CallEphemeronKeyBarrier(object_, offset_, save_fp_mode);
184 } else if (mode_ == RecordWriteMode::kValueIsIndirectPointer) {
186 __ CallIndirectPointerBarrier(object_, offset_, save_fp_mode,
188#if V8_ENABLE_WEBASSEMBLY
189 } else if (stub_mode_ == StubCallMode::kCallWasmRuntimeStub) {
190 // A direct call to a wasm runtime stub defined in this module.
191 // Just encode the stub index. This will be patched when the code
192 // is added to the native module and copied into wasm code space.
193 __ CallRecordWriteStubSaveRegisters(object_, offset_, save_fp_mode,
194 StubCallMode::kCallWasmRuntimeStub);
195#endif // V8_ENABLE_WEBASSEMBLY
196 } else {
197 __ CallRecordWriteStubSaveRegisters(object_, offset_, save_fp_mode);
198 }
199 if (must_save_lr_) {
200 __ Pop(ra);
201 }
202 }
203
204 private:
205 Register const object_;
206 Operand const offset_;
207 Register const value_;
209#if V8_ENABLE_WEBASSEMBLY
210 StubCallMode const stub_mode_;
211#endif // V8_ENABLE_WEBASSEMBLY
215};
216
217#define CREATE_OOL_CLASS(ool_name, masm_ool_name, T) \
218 class ool_name final : public OutOfLineCode { \
219 public: \
220 ool_name(CodeGenerator* gen, T dst, T src1, T src2) \
221 : OutOfLineCode(gen), dst_(dst), src1_(src1), src2_(src2) {} \
222 \
223 void Generate() final { __ masm_ool_name(dst_, src1_, src2_); } \
224 \
225 private: \
226 T const dst_; \
227 T const src1_; \
228 T const src2_; \
229 }
230
231CREATE_OOL_CLASS(OutOfLineFloat32Max, Float32MaxOutOfLine, FPURegister);
232CREATE_OOL_CLASS(OutOfLineFloat32Min, Float32MinOutOfLine, FPURegister);
233CREATE_OOL_CLASS(OutOfLineFloat64Max, Float64MaxOutOfLine, FPURegister);
234CREATE_OOL_CLASS(OutOfLineFloat64Min, Float64MinOutOfLine, FPURegister);
235
236#undef CREATE_OOL_CLASS
237
238#if V8_ENABLE_WEBASSEMBLY
239class WasmOutOfLineTrap : public OutOfLineCode {
240 public:
241 WasmOutOfLineTrap(CodeGenerator* gen, Instruction* instr)
242 : OutOfLineCode(gen), gen_(gen), instr_(instr) {}
243 void Generate() override {
244 Loong64OperandConverter i(gen_, instr_);
245 TrapId trap_id =
246 static_cast<TrapId>(i.InputInt32(instr_->InputCount() - 1));
247 GenerateCallToTrap(trap_id);
248 }
249
250 protected:
251 CodeGenerator* gen_;
252
253 void GenerateWithTrapId(TrapId trap_id) { GenerateCallToTrap(trap_id); }
254
255 private:
256 void GenerateCallToTrap(TrapId trap_id) {
257 gen_->AssembleSourcePosition(instr_);
258 // A direct call to a wasm runtime stub defined in this module.
259 // Just encode the stub index. This will be patched when the code
260 // is added to the native module and copied into wasm code space.
261 __ Call(static_cast<Address>(trap_id), RelocInfo::WASM_STUB_CALL);
262 ReferenceMap* reference_map = gen_->zone()->New<ReferenceMap>(gen_->zone());
263 gen_->RecordSafepoint(reference_map);
264 __ AssertUnreachable(AbortReason::kUnexpectedReturnFromWasmTrap);
265 }
266
267 Instruction* instr_;
268};
269
270void RecordTrapInfoIfNeeded(Zone* zone, CodeGenerator* codegen,
271 InstructionCode opcode, Instruction* instr,
272 int pc) {
273 const MemoryAccessMode access_mode = AccessModeField::decode(opcode);
274 if (access_mode == kMemoryAccessProtectedMemOutOfBounds ||
276 ReferenceMap* reference_map =
277 codegen->zone()->New<ReferenceMap>(codegen->zone());
278 // The safepoint has to be recorded at the return address of a call. Address
279 // we use as the fake return address in the case of the trap handler is the
280 // fault address (here `pc`) + 1. Therefore the safepoint here has to be
281 // recorded at pc + 1;
282 codegen->RecordSafepoint(reference_map, pc + 1);
283 codegen->RecordProtectedInstruction(pc);
284 }
285}
286#else
287void RecordTrapInfoIfNeeded(Zone* zone, CodeGenerator* codegen,
288 InstructionCode opcode, Instruction* instr,
289 int pc) {
291}
292#endif // V8_ENABLE_WEBASSEMBLY
293
294Condition FlagsConditionToConditionCmp(FlagsCondition condition) {
295 switch (condition) {
296 case kEqual:
297 return eq;
298 case kNotEqual:
299 return ne;
300 case kSignedLessThan:
301 return lt;
303 return ge;
305 return le;
307 return gt;
309 return lo;
311 return hs;
313 return ls;
315 return hi;
316 case kUnorderedEqual:
318 break;
319 default:
320 break;
321 }
322 UNREACHABLE();
323}
324
325Condition FlagsConditionToConditionTst(FlagsCondition condition) {
326 switch (condition) {
327 case kNotEqual:
328 return ne;
329 case kEqual:
330 return eq;
331 default:
332 break;
333 }
334 UNREACHABLE();
335}
336
337Condition FlagsConditionToConditionOvf(FlagsCondition condition) {
338 switch (condition) {
339 case kOverflow:
340 return ne;
341 case kNotOverflow:
342 return eq;
343 default:
344 break;
345 }
346 UNREACHABLE();
347}
348
349FPUCondition FlagsConditionToConditionCmpFPU(bool* predicate,
351 switch (condition) {
352 case kEqual:
353 *predicate = true;
354 return CEQ;
355 case kNotEqual:
356 *predicate = false;
357 return CEQ;
359 case kFloatLessThan:
360 *predicate = true;
361 return CLT;
363 *predicate = false;
364 return CLT;
367 *predicate = true;
368 return CLE;
370 *predicate = false;
371 return CLE;
373 *predicate = false;
374 return CULE;
376 *predicate = false;
377 return CULT;
379 *predicate = true;
380 return CULT;
382 *predicate = false;
383 return CLE;
385 *predicate = false;
386 return CLT;
388 *predicate = true;
389 return CULE;
390 case kUnorderedEqual:
392 *predicate = true;
393 break;
394 default:
395 *predicate = true;
396 break;
397 }
398 UNREACHABLE();
399}
400
401} // namespace
402
403#define ASSEMBLE_ATOMIC_LOAD_INTEGER(asm_instr) \
404 do { \
405 RecordTrapInfoIfNeeded(zone(), this, opcode, instr, __ pc_offset()); \
406 __ asm_instr(i.OutputRegister(), i.MemoryOperand()); \
407 __ dbar(0); \
408 } while (0)
409
410// TODO(LOONG_dev): remove second dbar?
411#define ASSEMBLE_ATOMIC_STORE_INTEGER(asm_instr) \
412 do { \
413 __ dbar(0); \
414 RecordTrapInfoIfNeeded(zone(), this, opcode, instr, __ pc_offset()); \
415 __ asm_instr(i.InputOrZeroRegister(2), i.MemoryOperand()); \
416 __ dbar(0); \
417 } while (0)
418
419// only use for sub_w and sub_d
420#define ASSEMBLE_ATOMIC_BINOP(load_linked, store_conditional, bin_instr) \
421 do { \
422 Label binop; \
423 __ Add_d(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1)); \
424 __ dbar(0); \
425 __ bind(&binop); \
426 RecordTrapInfoIfNeeded(zone(), this, opcode, instr, __ pc_offset()); \
427 __ load_linked(i.OutputRegister(0), MemOperand(i.TempRegister(0), 0)); \
428 __ bin_instr(i.TempRegister(1), i.OutputRegister(0), \
429 Operand(i.InputRegister(2))); \
430 __ store_conditional(i.TempRegister(1), MemOperand(i.TempRegister(0), 0)); \
431 __ BranchShort(&binop, eq, i.TempRegister(1), Operand(zero_reg)); \
432 __ dbar(0); \
433 } while (0)
434
435// TODO(LOONG_dev): remove second dbar?
436#define ASSEMBLE_ATOMIC_BINOP_EXT(load_linked, store_conditional, sign_extend, \
437 size, bin_instr, representation) \
438 do { \
439 Label binop; \
440 __ add_d(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1)); \
441 if (representation == 32) { \
442 __ andi(i.TempRegister(3), i.TempRegister(0), 0x3); \
443 } else { \
444 DCHECK_EQ(representation, 64); \
445 __ andi(i.TempRegister(3), i.TempRegister(0), 0x7); \
446 } \
447 __ Sub_d(i.TempRegister(0), i.TempRegister(0), \
448 Operand(i.TempRegister(3))); \
449 __ slli_w(i.TempRegister(3), i.TempRegister(3), 3); \
450 __ dbar(0); \
451 __ bind(&binop); \
452 RecordTrapInfoIfNeeded(zone(), this, opcode, instr, __ pc_offset()); \
453 __ load_linked(i.TempRegister(1), MemOperand(i.TempRegister(0), 0)); \
454 __ ExtractBits(i.OutputRegister(0), i.TempRegister(1), i.TempRegister(3), \
455 size, sign_extend); \
456 __ bin_instr(i.TempRegister(2), i.OutputRegister(0), \
457 Operand(i.InputRegister(2))); \
458 __ InsertBits(i.TempRegister(1), i.TempRegister(2), i.TempRegister(3), \
459 size); \
460 __ store_conditional(i.TempRegister(1), MemOperand(i.TempRegister(0), 0)); \
461 __ BranchShort(&binop, eq, i.TempRegister(1), Operand(zero_reg)); \
462 __ dbar(0); \
463 } while (0)
464
465// TODO(LOONG_dev): remove second dbar?
466#define ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT( \
467 load_linked, store_conditional, sign_extend, size, representation) \
468 do { \
469 Label exchange; \
470 __ add_d(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1)); \
471 if (representation == 32) { \
472 __ andi(i.TempRegister(1), i.TempRegister(0), 0x3); \
473 } else { \
474 DCHECK_EQ(representation, 64); \
475 __ andi(i.TempRegister(1), i.TempRegister(0), 0x7); \
476 } \
477 __ Sub_d(i.TempRegister(0), i.TempRegister(0), \
478 Operand(i.TempRegister(1))); \
479 __ slli_w(i.TempRegister(1), i.TempRegister(1), 3); \
480 __ dbar(0); \
481 __ bind(&exchange); \
482 RecordTrapInfoIfNeeded(zone(), this, opcode, instr, __ pc_offset()); \
483 __ load_linked(i.TempRegister(2), MemOperand(i.TempRegister(0), 0)); \
484 __ ExtractBits(i.OutputRegister(0), i.TempRegister(2), i.TempRegister(1), \
485 size, sign_extend); \
486 __ InsertBits(i.TempRegister(2), i.InputRegister(2), i.TempRegister(1), \
487 size); \
488 __ store_conditional(i.TempRegister(2), MemOperand(i.TempRegister(0), 0)); \
489 __ BranchShort(&exchange, eq, i.TempRegister(2), Operand(zero_reg)); \
490 __ dbar(0); \
491 } while (0)
492
493// TODO(LOONG_dev): remove second dbar?
494#define ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER(load_linked, \
495 store_conditional) \
496 do { \
497 Label compareExchange; \
498 Label exit; \
499 __ add_d(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1)); \
500 __ dbar(0); \
501 __ bind(&compareExchange); \
502 RecordTrapInfoIfNeeded(zone(), this, opcode, instr, __ pc_offset()); \
503 __ load_linked(i.OutputRegister(0), MemOperand(i.TempRegister(0), 0)); \
504 __ BranchShort(&exit, ne, i.InputRegister(2), \
505 Operand(i.OutputRegister(0))); \
506 __ mov(i.TempRegister(2), i.InputRegister(3)); \
507 __ store_conditional(i.TempRegister(2), MemOperand(i.TempRegister(0), 0)); \
508 __ BranchShort(&compareExchange, eq, i.TempRegister(2), \
509 Operand(zero_reg)); \
510 __ bind(&exit); \
511 __ dbar(0); \
512 } while (0)
513
514// TODO(LOONG_dev): remove second dbar?
515#define ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER_EXT( \
516 load_linked, store_conditional, sign_extend, size, representation) \
517 do { \
518 Label compareExchange; \
519 Label exit; \
520 __ add_d(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1)); \
521 if (representation == 32) { \
522 __ andi(i.TempRegister(1), i.TempRegister(0), 0x3); \
523 } else { \
524 DCHECK_EQ(representation, 64); \
525 __ andi(i.TempRegister(1), i.TempRegister(0), 0x7); \
526 } \
527 __ Sub_d(i.TempRegister(0), i.TempRegister(0), \
528 Operand(i.TempRegister(1))); \
529 __ slli_w(i.TempRegister(1), i.TempRegister(1), 3); \
530 __ dbar(0); \
531 __ bind(&compareExchange); \
532 RecordTrapInfoIfNeeded(zone(), this, opcode, instr, __ pc_offset()); \
533 __ load_linked(i.TempRegister(2), MemOperand(i.TempRegister(0), 0)); \
534 __ ExtractBits(i.OutputRegister(0), i.TempRegister(2), i.TempRegister(1), \
535 size, sign_extend); \
536 __ ExtractBits(i.TempRegister(2), i.InputRegister(2), zero_reg, size, \
537 sign_extend); \
538 __ BranchShort(&exit, ne, i.TempRegister(2), \
539 Operand(i.OutputRegister(0))); \
540 __ InsertBits(i.TempRegister(2), i.InputRegister(3), i.TempRegister(1), \
541 size); \
542 __ store_conditional(i.TempRegister(2), MemOperand(i.TempRegister(0), 0)); \
543 __ BranchShort(&compareExchange, eq, i.TempRegister(2), \
544 Operand(zero_reg)); \
545 __ bind(&exit); \
546 __ dbar(0); \
547 } while (0)
548
549#define ASSEMBLE_IEEE754_BINOP(name) \
550 do { \
551 FrameScope scope(masm(), StackFrame::MANUAL); \
552 UseScratchRegisterScope temps(masm()); \
553 Register scratch = temps.Acquire(); \
554 __ PrepareCallCFunction(0, 2, scratch); \
555 __ CallCFunction(ExternalReference::ieee754_##name##_function(), 0, 2); \
556 } while (0)
557
558#define ASSEMBLE_IEEE754_UNOP(name) \
559 do { \
560 FrameScope scope(masm(), StackFrame::MANUAL); \
561 UseScratchRegisterScope temps(masm()); \
562 Register scratch = temps.Acquire(); \
563 __ PrepareCallCFunction(0, 1, scratch); \
564 __ CallCFunction(ExternalReference::ieee754_##name##_function(), 0, 1); \
565 } while (0)
566
567#define ASSEMBLE_F64X2_ARITHMETIC_BINOP(op) \
568 do { \
569 __ op(i.OutputSimd128Register(), i.InputSimd128Register(0), \
570 i.InputSimd128Register(1)); \
571 } while (0)
572
574 __ mov(sp, fp);
575 __ Pop(ra, fp);
576}
577
579 if (frame_access_state()->has_frame()) {
582 }
584}
585
586namespace {
587
588void AdjustStackPointerForTailCall(MacroAssembler* masm,
589 FrameAccessState* state,
590 int new_slot_above_sp,
591 bool allow_shrinkage = true) {
592 int current_sp_offset = state->GetSPToFPSlotCount() +
594 int stack_slot_delta = new_slot_above_sp - current_sp_offset;
595 if (stack_slot_delta > 0) {
596 masm->Sub_d(sp, sp, stack_slot_delta * kSystemPointerSize);
597 state->IncreaseSPDelta(stack_slot_delta);
598 } else if (allow_shrinkage && stack_slot_delta < 0) {
599 masm->Add_d(sp, sp, -stack_slot_delta * kSystemPointerSize);
600 state->IncreaseSPDelta(stack_slot_delta);
601 }
602}
603
604} // namespace
605
607 int first_unused_slot_offset) {
608 AdjustStackPointerForTailCall(masm(), frame_access_state(),
609 first_unused_slot_offset, false);
610}
611
613 int first_unused_slot_offset) {
614 AdjustStackPointerForTailCall(masm(), frame_access_state(),
615 first_unused_slot_offset);
616}
617
618// Check that {kJavaScriptCallCodeStartRegister} is correct.
620 UseScratchRegisterScope temps(masm());
621 Register scratch = temps.Acquire();
622 __ ComputeCodeStartAddress(scratch);
623 __ Assert(eq, AbortReason::kWrongFunctionCodeStart,
624 kJavaScriptCallCodeStartRegister, Operand(scratch));
625}
626
627#ifdef V8_ENABLE_LEAPTIERING
628// Check that {kJavaScriptCallDispatchHandleRegister} is correct.
629void CodeGenerator::AssembleDispatchHandleRegisterCheck() {
630 DCHECK(linkage()->GetIncomingDescriptor()->IsJSFunctionCall());
631
633
634 // We currently don't check this for JS builtins as those are sometimes
635 // called directly (e.g. from other builtins) and not through the dispatch
636 // table. This is fine as builtin functions don't use the dispatch handle,
637 // but we could enable this check in the future if we make sure to pass the
638 // kInvalidDispatchHandle whenever we do a direct call to a JS builtin.
640 return;
641 }
642
643 // For now, we only ensure that the register references a valid dispatch
644 // entry with the correct parameter count. In the future, we may also be able
645 // to check that the entry points back to this code.
646 UseScratchRegisterScope temps(masm());
647 Register actual_parameter_count = temps.Acquire();
648 Register scratch = temps.Acquire();
649 __ LoadParameterCountFromJSDispatchTable(
650 actual_parameter_count, kJavaScriptCallDispatchHandleRegister, scratch);
651 __ Assert(eq, AbortReason::kWrongFunctionDispatchHandle,
652 actual_parameter_count, Operand(parameter_count_));
653}
654#endif // V8_ENABLE_LEAPTIERING
655
657
658// Assembles an instruction after register allocation, producing machine code.
660 Instruction* instr) {
661 Loong64OperandConverter i(this, instr);
662 InstructionCode opcode = instr->opcode();
663 ArchOpcode arch_opcode = ArchOpcodeField::decode(opcode);
664 switch (arch_opcode) {
665 case kArchCallCodeObject: {
666 if (instr->InputAt(0)->IsImmediate()) {
667 __ Call(i.InputCode(0), RelocInfo::CODE_TARGET);
668 } else {
669 Register reg = i.InputRegister(0);
671 i.InputCodeEntrypointTag(instr->CodeEnrypointTagInputIndex());
673 instr->HasCallDescriptorFlag(CallDescriptor::kFixedTargetRegister),
675 __ CallCodeObject(reg, tag);
676 }
679 break;
680 }
681 case kArchCallBuiltinPointer: {
682 DCHECK(!instr->InputAt(0)->IsImmediate());
683 Register builtin_index = i.InputRegister(0);
684 Register target =
685 instr->HasCallDescriptorFlag(CallDescriptor::kFixedTargetRegister)
687 : builtin_index;
688 __ CallBuiltinByIndex(builtin_index, target);
691 break;
692 }
693#if V8_ENABLE_WEBASSEMBLY
694 case kArchCallWasmFunction:
695 case kArchCallWasmFunctionIndirect: {
696 if (instr->InputAt(0)->IsImmediate()) {
697 DCHECK_EQ(arch_opcode, kArchCallWasmFunction);
698 Constant constant = i.ToConstant(instr->InputAt(0));
699 Address wasm_code = static_cast<Address>(constant.ToInt64());
700 __ Call(wasm_code, constant.rmode());
701 } else if (arch_opcode == kArchCallWasmFunctionIndirect) {
702 __ CallWasmCodePointer(
703 i.InputRegister(0),
704 i.InputInt64(instr->WasmSignatureHashInputIndex()));
705 } else {
706 __ Call(i.InputRegister(0));
707 }
710 break;
711 }
712 case kArchTailCallWasm:
713 case kArchTailCallWasmIndirect: {
714 if (instr->InputAt(0)->IsImmediate()) {
715 DCHECK_EQ(arch_opcode, kArchTailCallWasm);
716 Constant constant = i.ToConstant(instr->InputAt(0));
717 Address wasm_code = static_cast<Address>(constant.ToInt64());
718 __ Jump(wasm_code, constant.rmode());
719 } else if (arch_opcode == kArchTailCallWasmIndirect) {
720 __ CallWasmCodePointer(
721 i.InputRegister(0),
722 i.InputInt64(instr->WasmSignatureHashInputIndex()),
724 } else {
725 __ Jump(i.InputRegister(0));
726 }
729 break;
730 }
731#endif // V8_ENABLE_WEBASSEMBLY
732 case kArchTailCallCodeObject: {
733 if (instr->InputAt(0)->IsImmediate()) {
734 __ Jump(i.InputCode(0), RelocInfo::CODE_TARGET);
735 } else {
736 Register reg = i.InputRegister(0);
738 i.InputCodeEntrypointTag(instr->CodeEnrypointTagInputIndex());
740 instr->HasCallDescriptorFlag(CallDescriptor::kFixedTargetRegister),
742 __ JumpCodeObject(reg, tag);
743 }
746 break;
747 }
748 case kArchTailCallAddress: {
749 CHECK(!instr->InputAt(0)->IsImmediate());
750 Register reg = i.InputRegister(0);
752 instr->HasCallDescriptorFlag(CallDescriptor::kFixedTargetRegister),
754 __ Jump(reg);
757 break;
758 }
759 case kArchCallJSFunction: {
760 Register func = i.InputRegister(0);
761 if (v8_flags.debug_code) {
762 UseScratchRegisterScope temps(masm());
763 Register scratch = temps.Acquire();
764 // Check the function's context matches the context argument.
765 __ LoadTaggedField(scratch,
766 FieldMemOperand(func, JSFunction::kContextOffset));
767 __ Assert(eq, AbortReason::kWrongFunctionContext, cp, Operand(scratch));
768 }
769 uint32_t num_arguments =
770 i.InputUint32(instr->JSCallArgumentCountInputIndex());
771 __ CallJSFunction(func, num_arguments);
774 break;
775 }
776 case kArchPrepareCallCFunction: {
777 UseScratchRegisterScope temps(masm());
778 Register scratch = temps.Acquire();
779 int const num_gp_parameters = ParamField::decode(instr->opcode());
780 int const num_fp_parameters = FPParamField::decode(instr->opcode());
781 __ PrepareCallCFunction(num_gp_parameters, num_fp_parameters, scratch);
782 // Frame alignment requires using FP-relative frame addressing.
784 break;
785 }
786 case kArchSaveCallerRegisters: {
787 fp_mode_ =
788 static_cast<SaveFPRegsMode>(MiscField::decode(instr->opcode()));
791 // kReturnRegister0 should have been saved before entering the stub.
792 int bytes = __ PushCallerSaved(fp_mode_, kReturnRegister0);
794 DCHECK_EQ(0, frame_access_state()->sp_delta());
798 break;
799 }
800 case kArchRestoreCallerRegisters: {
802 static_cast<SaveFPRegsMode>(MiscField::decode(instr->opcode())));
805 // Don't overwrite the returned value.
806 int bytes = __ PopCallerSaved(fp_mode_, kReturnRegister0);
808 DCHECK_EQ(0, frame_access_state()->sp_delta());
811 break;
812 }
813 case kArchPrepareTailCall:
815 break;
816 case kArchCallCFunctionWithFrameState:
817 case kArchCallCFunction: {
818 int const num_gp_parameters = ParamField::decode(instr->opcode());
819 int const num_fp_parameters = FPParamField::decode(instr->opcode());
820 SetIsolateDataSlots set_isolate_data_slots = SetIsolateDataSlots::kYes;
821 Label return_location;
822#if V8_ENABLE_WEBASSEMBLY
823 bool isWasmCapiFunction =
824 linkage()->GetIncomingDescriptor()->IsWasmCapiFunction();
825 if (isWasmCapiFunction) {
826 UseScratchRegisterScope temps(masm());
827 Register scratch = temps.Acquire();
828 __ LoadLabelRelative(scratch, &return_location);
829 __ St_d(scratch,
830 MemOperand(fp, WasmExitFrameConstants::kCallingPCOffset));
831 set_isolate_data_slots = SetIsolateDataSlots::kNo;
832 }
833#endif // V8_ENABLE_WEBASSEMBLY
834 int pc_offset;
835 if (instr->InputAt(0)->IsImmediate()) {
836 ExternalReference ref = i.InputExternalReference(0);
837 pc_offset = __ CallCFunction(ref, num_gp_parameters, num_fp_parameters,
838 set_isolate_data_slots, &return_location);
839 } else {
840 Register func = i.InputRegister(0);
841 pc_offset = __ CallCFunction(func, num_gp_parameters, num_fp_parameters,
842 set_isolate_data_slots, &return_location);
843 }
844 RecordSafepoint(instr->reference_map(), pc_offset);
845
846 bool const needs_frame_state =
847 (arch_opcode == kArchCallCFunctionWithFrameState);
848 if (needs_frame_state) {
850 }
851
853 // Ideally, we should decrement SP delta to match the change of stack
854 // pointer in CallCFunction. However, for certain architectures (e.g.
855 // ARM), there may be more strict alignment requirement, causing old SP
856 // to be saved on the stack. In those cases, we can not calculate the SP
857 // delta statically.
860 // Need to re-sync SP delta introduced in kArchSaveCallerRegisters.
861 // Here, we assume the sequence to be:
862 // kArchSaveCallerRegisters;
863 // kArchCallCFunction;
864 // kArchRestoreCallerRegisters;
865 int bytes =
866 __ RequiredStackSizeForCallerSaved(fp_mode_, kReturnRegister0);
868 }
869 break;
870 }
871 case kArchJmp:
872 AssembleArchJump(i.InputRpo(0));
873 break;
874 case kArchBinarySearchSwitch:
876 break;
877 case kArchTableSwitch:
879 break;
880 case kArchAbortCSADcheck:
881 DCHECK(i.InputRegister(0) == a0);
882 {
883 // We don't actually want to generate a pile of code for this, so just
884 // claim there is a stack frame, without generating one.
885 FrameScope scope(masm(), StackFrame::NO_FRAME_TYPE);
886 __ CallBuiltin(Builtin::kAbortCSADcheck);
887 }
888 __ stop();
889 break;
890 case kArchDebugBreak:
891 __ DebugBreak();
892 break;
893 case kArchComment:
894 __ RecordComment(reinterpret_cast<const char*>(i.InputInt64(0)),
896 break;
897 case kArchNop:
898 case kArchThrowTerminator:
899 // don't emit code for nops.
900 break;
901 case kArchDeoptimize: {
902 DeoptimizationExit* exit =
904 __ Branch(exit->label());
905 break;
906 }
907 case kArchRet:
908 AssembleReturn(instr->InputAt(0));
909 break;
910#if V8_ENABLE_WEBASSEMBLY
911 case kArchStackPointer:
912 // The register allocator expects an allocatable register for the output,
913 // we cannot use sp directly.
914 __ mov(i.OutputRegister(), sp);
915 break;
916 case kArchSetStackPointer: {
917 DCHECK(instr->InputAt(0)->IsRegister());
918 __ mov(sp, i.InputRegister(0));
919 break;
920 }
921#endif // V8_ENABLE_WEBASSEMBLY
922 case kArchStackPointerGreaterThan: {
923 Register lhs_register = sp;
924 uint32_t offset;
926 lhs_register = i.TempRegister(1);
927 __ Sub_d(lhs_register, sp, offset);
928 }
929 __ Sltu(i.TempRegister(0), i.InputRegister(0), lhs_register);
930 break;
931 }
932 case kArchStackCheckOffset:
933 __ Move(i.OutputRegister(), Smi::FromInt(GetStackCheckOffset()));
934 break;
935 case kArchFramePointer:
936 __ mov(i.OutputRegister(), fp);
937 break;
938 case kArchParentFramePointer:
939 if (frame_access_state()->has_frame()) {
940 __ Ld_d(i.OutputRegister(), MemOperand(fp, 0));
941 } else {
942 __ mov(i.OutputRegister(), fp);
943 }
944 break;
945 case kArchTruncateDoubleToI:
946 __ TruncateDoubleToI(isolate(), zone(), i.OutputRegister(),
947 i.InputDoubleRegister(0), DetermineStubCallMode());
948 break;
949 case kArchStoreWithWriteBarrier: {
951 // Indirect pointer writes must use a different opcode.
953 AddressingMode addressing_mode =
955 Register object = i.InputRegister(0);
956 Register value = i.InputRegister(2);
957
958 if (addressing_mode == kMode_MRI) {
959 auto ool = zone()->New<OutOfLineRecordWrite>(
960 this, object, Operand(i.InputInt64(1)), value, mode,
962 RecordTrapInfoIfNeeded(zone(), this, opcode, instr, __ pc_offset());
963 __ StoreTaggedField(value, MemOperand(object, i.InputInt64(1)));
965 __ JumpIfSmi(value, ool->exit());
966 }
967 __ CheckPageFlag(object,
969 ool->entry());
970 __ bind(ool->exit());
971 } else {
972 DCHECK_EQ(addressing_mode, kMode_MRR);
973 auto ool = zone()->New<OutOfLineRecordWrite>(
974 this, object, Operand(i.InputRegister(1)), value, mode,
976 RecordTrapInfoIfNeeded(zone(), this, opcode, instr, __ pc_offset());
977 __ StoreTaggedField(value, MemOperand(object, i.InputRegister(1)));
979 __ JumpIfSmi(value, ool->exit());
980 }
981 __ CheckPageFlag(object,
983 ool->entry());
984 __ bind(ool->exit());
985 }
986 break;
987 }
988 case kArchAtomicStoreWithWriteBarrier: {
990 // Indirect pointer writes must use a different opcode.
992 Register object = i.InputRegister(0);
993 int64_t offset = i.InputInt64(1);
994 Register value = i.InputRegister(2);
995
996 auto ool = zone()->New<OutOfLineRecordWrite>(
997 this, object, Operand(offset), value, mode, DetermineStubCallMode());
998 __ AtomicStoreTaggedField(value, MemOperand(object, offset));
999 // Skip the write barrier if the value is a Smi. However, this is only
1000 // valid if the value isn't an indirect pointer. Otherwise the value will
1001 // be a pointer table index, which will always look like a Smi (but
1002 // actually reference a pointer in the pointer table).
1004 __ JumpIfSmi(value, ool->exit());
1005 }
1007 ne, ool->entry());
1008 __ bind(ool->exit());
1009 break;
1010 }
1011 case kArchStoreIndirectWithWriteBarrier: {
1014 AddressingMode addressing_mode =
1016 IndirectPointerTag tag = static_cast<IndirectPointerTag>(i.InputInt64(3));
1018 Register object = i.InputRegister(0);
1019 Register value = i.InputRegister(2);
1020
1021 if (addressing_mode == kMode_MRI) {
1022 auto ool = zone()->New<OutOfLineRecordWrite>(
1023 this, object, Operand(i.InputInt32(1)), value, mode,
1024 DetermineStubCallMode(), tag);
1025 RecordTrapInfoIfNeeded(zone(), this, opcode, instr, __ pc_offset());
1026 __ StoreIndirectPointerField(value,
1027 MemOperand(object, i.InputInt32(1)));
1028 __ CheckPageFlag(object,
1030 ool->entry());
1031 __ bind(ool->exit());
1032 } else {
1033 DCHECK_EQ(addressing_mode, kMode_MRR);
1034 auto ool = zone()->New<OutOfLineRecordWrite>(
1035 this, object, Operand(i.InputRegister(1)), value, mode,
1036 DetermineStubCallMode(), tag);
1037 RecordTrapInfoIfNeeded(zone(), this, opcode, instr, __ pc_offset());
1038 __ StoreIndirectPointerField(value,
1039 MemOperand(object, i.InputRegister(1)));
1040 __ CheckPageFlag(object,
1042 ool->entry());
1043 __ bind(ool->exit());
1044 }
1045 break;
1046 }
1047 case kArchStackSlot: {
1048 UseScratchRegisterScope temps(masm());
1049 Register scratch = temps.Acquire();
1050 FrameOffset offset =
1051 frame_access_state()->GetFrameOffset(i.InputInt32(0));
1052 Register base_reg = offset.from_stack_pointer() ? sp : fp;
1053 __ Add_d(i.OutputRegister(), base_reg, Operand(offset.offset()));
1054 if (v8_flags.debug_code) {
1055 // Verify that the output_register is properly aligned
1056 __ And(scratch, i.OutputRegister(), Operand(kSystemPointerSize - 1));
1057 __ Assert(eq, AbortReason::kAllocationIsNotDoubleAligned, scratch,
1058 Operand(zero_reg));
1059 }
1060 break;
1061 }
1062 case kIeee754Float64Acos:
1064 break;
1065 case kIeee754Float64Acosh:
1066 ASSEMBLE_IEEE754_UNOP(acosh);
1067 break;
1068 case kIeee754Float64Asin:
1070 break;
1071 case kIeee754Float64Asinh:
1072 ASSEMBLE_IEEE754_UNOP(asinh);
1073 break;
1074 case kIeee754Float64Atan:
1076 break;
1077 case kIeee754Float64Atanh:
1078 ASSEMBLE_IEEE754_UNOP(atanh);
1079 break;
1080 case kIeee754Float64Atan2:
1082 break;
1083 case kIeee754Float64Cos:
1085 break;
1086 case kIeee754Float64Cosh:
1088 break;
1089 case kIeee754Float64Cbrt:
1091 break;
1092 case kIeee754Float64Exp:
1094 break;
1095 case kIeee754Float64Expm1:
1096 ASSEMBLE_IEEE754_UNOP(expm1);
1097 break;
1098 case kIeee754Float64Log:
1100 break;
1101 case kIeee754Float64Log1p:
1102 ASSEMBLE_IEEE754_UNOP(log1p);
1103 break;
1104 case kIeee754Float64Log2:
1106 break;
1107 case kIeee754Float64Log10:
1108 ASSEMBLE_IEEE754_UNOP(log10);
1109 break;
1110 case kIeee754Float64Pow:
1112 break;
1113 case kIeee754Float64Sin:
1115 break;
1116 case kIeee754Float64Sinh:
1118 break;
1119 case kIeee754Float64Tan:
1121 break;
1122 case kIeee754Float64Tanh:
1124 break;
1125 case kLoong64Add_w:
1126 __ Add_w(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
1127 break;
1128 case kLoong64Add_d:
1129 __ Add_d(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
1130 break;
1131 case kLoong64AddOvf_d: {
1132 UseScratchRegisterScope temps(masm());
1133 DCHECK(temps.hasAvailable());
1134 temps.Exclude(t8);
1135 __ AddOverflow_d(i.OutputRegister(), i.InputRegister(0),
1136 i.InputOperand(1), t8);
1137 break;
1138 }
1139 case kLoong64Sub_w:
1140 __ Sub_w(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
1141 break;
1142 case kLoong64Sub_d:
1143 __ Sub_d(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
1144 break;
1145 case kLoong64SubOvf_d:
1146 __ SubOverflow_d(i.OutputRegister(), i.InputRegister(0),
1147 i.InputOperand(1), t8);
1148 break;
1149 case kLoong64Mul_w:
1150 __ Mul_w(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
1151 break;
1152 case kLoong64MulOvf_w: {
1153 UseScratchRegisterScope temps(masm());
1154 DCHECK(temps.hasAvailable());
1155 temps.Exclude(t8);
1156 __ MulOverflow_w(i.OutputRegister(), i.InputRegister(0),
1157 i.InputOperand(1), t8);
1158 break;
1159 }
1160 case kLoong64MulOvf_d: {
1161 UseScratchRegisterScope temps(masm());
1162 DCHECK(temps.hasAvailable());
1163 temps.Exclude(t8);
1164 __ MulOverflow_d(i.OutputRegister(), i.InputRegister(0),
1165 i.InputOperand(1), t8);
1166 break;
1167 }
1168 case kLoong64Mulh_w:
1169 __ Mulh_w(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
1170 break;
1171 case kLoong64Mulh_wu:
1172 __ Mulh_wu(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
1173 break;
1174 case kLoong64Mulh_d:
1175 __ Mulh_d(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
1176 break;
1177 case kLoong64Mulh_du:
1178 __ Mulh_du(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
1179 break;
1180 case kLoong64Div_w:
1181 __ Div_w(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
1182 __ maskeqz(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1));
1183 break;
1184 case kLoong64Div_wu:
1185 __ Div_wu(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
1186 __ maskeqz(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1));
1187 break;
1188 case kLoong64Mod_w:
1189 __ Mod_w(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
1190 break;
1191 case kLoong64Mod_wu:
1192 __ Mod_wu(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
1193 break;
1194 case kLoong64Mul_d:
1195 __ Mul_d(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
1196 break;
1197 case kLoong64Div_d:
1198 __ Div_d(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
1199 __ maskeqz(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1));
1200 break;
1201 case kLoong64Div_du:
1202 __ Div_du(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
1203 __ maskeqz(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1));
1204 break;
1205 case kLoong64Mod_d:
1206 __ Mod_d(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
1207 break;
1208 case kLoong64Mod_du:
1209 __ Mod_du(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
1210 break;
1211 case kLoong64Alsl_d:
1212 DCHECK(instr->InputAt(2)->IsImmediate());
1213 __ Alsl_d(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1),
1214 i.InputInt8(2));
1215 break;
1216 case kLoong64Alsl_w:
1217 DCHECK(instr->InputAt(2)->IsImmediate());
1218 __ Alsl_w(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1),
1219 i.InputInt8(2));
1220 break;
1221 case kLoong64And:
1222 case kLoong64And32:
1223 __ And(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
1224 break;
1225 case kLoong64Or:
1226 case kLoong64Or32:
1227 __ Or(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
1228 break;
1229 case kLoong64Nor:
1230 case kLoong64Nor32:
1231 if (instr->InputAt(1)->IsRegister()) {
1232 __ Nor(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
1233 } else {
1234 DCHECK_EQ(0, i.InputOperand(1).immediate());
1235 __ Nor(i.OutputRegister(), i.InputRegister(0), zero_reg);
1236 }
1237 break;
1238 case kLoong64Xor:
1239 case kLoong64Xor32:
1240 __ Xor(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
1241 break;
1242 case kLoong64Clz_w:
1243 __ clz_w(i.OutputRegister(), i.InputRegister(0));
1244 break;
1245 case kLoong64Clz_d:
1246 __ clz_d(i.OutputRegister(), i.InputRegister(0));
1247 break;
1248 case kLoong64Sll_w:
1249 if (instr->InputAt(1)->IsRegister()) {
1250 __ sll_w(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1));
1251 } else {
1252 int64_t imm = i.InputOperand(1).immediate();
1253 __ slli_w(i.OutputRegister(), i.InputRegister(0),
1254 static_cast<uint16_t>(imm));
1255 }
1256 break;
1257 case kLoong64Srl_w:
1258 if (instr->InputAt(1)->IsRegister()) {
1259 __ srl_w(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1));
1260 } else {
1261 int64_t imm = i.InputOperand(1).immediate();
1262 __ srli_w(i.OutputRegister(), i.InputRegister(0),
1263 static_cast<uint16_t>(imm));
1264 }
1265 break;
1266 case kLoong64Sra_w:
1267 if (instr->InputAt(1)->IsRegister()) {
1268 __ sra_w(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1));
1269 } else {
1270 int64_t imm = i.InputOperand(1).immediate();
1271 __ srai_w(i.OutputRegister(), i.InputRegister(0),
1272 static_cast<uint16_t>(imm));
1273 }
1274 break;
1275 case kLoong64Bstrpick_w:
1276 __ bstrpick_w(i.OutputRegister(), i.InputRegister(0),
1277 i.InputInt8(1) + i.InputInt8(2) - 1, i.InputInt8(1));
1278 break;
1279 case kLoong64Bstrins_w:
1280 if (instr->InputAt(1)->IsImmediate() && i.InputInt8(1) == 0) {
1281 __ bstrins_w(i.OutputRegister(), zero_reg,
1282 i.InputInt8(1) + i.InputInt8(2) - 1, i.InputInt8(1));
1283 } else {
1284 __ bstrins_w(i.OutputRegister(), i.InputRegister(0),
1285 i.InputInt8(1) + i.InputInt8(2) - 1, i.InputInt8(1));
1286 }
1287 break;
1288 case kLoong64Bstrpick_d: {
1289 __ bstrpick_d(i.OutputRegister(), i.InputRegister(0),
1290 i.InputInt8(1) + i.InputInt8(2) - 1, i.InputInt8(1));
1291 break;
1292 }
1293 case kLoong64Bstrins_d:
1294 if (instr->InputAt(1)->IsImmediate() && i.InputInt8(1) == 0) {
1295 __ bstrins_d(i.OutputRegister(), zero_reg,
1296 i.InputInt8(1) + i.InputInt8(2) - 1, i.InputInt8(1));
1297 } else {
1298 __ bstrins_d(i.OutputRegister(), i.InputRegister(0),
1299 i.InputInt8(1) + i.InputInt8(2) - 1, i.InputInt8(1));
1300 }
1301 break;
1302 case kLoong64Sll_d:
1303 if (instr->InputAt(1)->IsRegister()) {
1304 __ sll_d(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1));
1305 } else {
1306 int64_t imm = i.InputOperand(1).immediate();
1307 __ slli_d(i.OutputRegister(), i.InputRegister(0),
1308 static_cast<uint16_t>(imm));
1309 }
1310 break;
1311 case kLoong64Srl_d:
1312 if (instr->InputAt(1)->IsRegister()) {
1313 __ srl_d(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1));
1314 } else {
1315 int64_t imm = i.InputOperand(1).immediate();
1316 __ srli_d(i.OutputRegister(), i.InputRegister(0),
1317 static_cast<uint16_t>(imm));
1318 }
1319 break;
1320 case kLoong64Sra_d:
1321 if (instr->InputAt(1)->IsRegister()) {
1322 __ sra_d(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1));
1323 } else {
1324 int64_t imm = i.InputOperand(1).immediate();
1325 __ srai_d(i.OutputRegister(), i.InputRegister(0), imm);
1326 }
1327 break;
1328 case kLoong64Rotr_w:
1329 __ Rotr_w(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
1330 break;
1331 case kLoong64Rotr_d:
1332 __ Rotr_d(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
1333 break;
1334 case kLoong64Tst: {
1335 UseScratchRegisterScope temps(masm());
1336 DCHECK(temps.hasAvailable());
1337 temps.Exclude(t8);
1338 __ And(t8, i.InputRegister(0), i.InputOperand(1));
1339 // Pseudo-instruction used for cmp/branch. No opcode emitted here.
1340 break;
1341 }
1342 case kLoong64Cmp32:
1343 case kLoong64Cmp64:
1344 // Pseudo-instruction used for cmp/branch. No opcode emitted here.
1345 break;
1346 case kLoong64Mov:
1347 // TODO(LOONG_dev): Should we combine mov/li, or use separate instr?
1348 // - Also see x64 ASSEMBLE_BINOP & RegisterOrOperandType
1349 if (HasRegisterInput(instr, 0)) {
1350 __ mov(i.OutputRegister(), i.InputRegister(0));
1351 } else {
1352 __ li(i.OutputRegister(), i.InputOperand(0));
1353 }
1354 break;
1355
1356 case kLoong64Float32Cmp: {
1357 FPURegister left = i.InputOrZeroSingleRegister(0);
1358 FPURegister right = i.InputOrZeroSingleRegister(1);
1359 bool predicate;
1361 FlagsConditionToConditionCmpFPU(&predicate, instr->flags_condition());
1362
1363 if ((left == kDoubleRegZero || right == kDoubleRegZero) &&
1364 !__ IsDoubleZeroRegSet()) {
1365 __ Move(kDoubleRegZero, 0.0);
1366 }
1367
1368 __ CompareF32(left, right, cc);
1369 } break;
1370 case kLoong64Float32Add:
1371 __ fadd_s(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
1372 i.InputDoubleRegister(1));
1373 break;
1374 case kLoong64Float32Sub:
1375 __ fsub_s(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
1376 i.InputDoubleRegister(1));
1377 break;
1378 case kLoong64Float32Mul:
1379 __ fmul_s(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
1380 i.InputDoubleRegister(1));
1381 break;
1382 case kLoong64Float32Div:
1383 __ fdiv_s(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
1384 i.InputDoubleRegister(1));
1385 break;
1386 case kLoong64Float32Abs:
1387 __ fabs_s(i.OutputSingleRegister(), i.InputSingleRegister(0));
1388 break;
1389 case kLoong64Float32Neg:
1390 __ Neg_s(i.OutputSingleRegister(), i.InputSingleRegister(0));
1391 break;
1392 case kLoong64Float32Sqrt: {
1393 __ fsqrt_s(i.OutputDoubleRegister(), i.InputDoubleRegister(0));
1394 break;
1395 }
1396 case kLoong64Float32Min: {
1397 FPURegister dst = i.OutputSingleRegister();
1398 FPURegister src1 = i.InputSingleRegister(0);
1399 FPURegister src2 = i.InputSingleRegister(1);
1400 auto ool = zone()->New<OutOfLineFloat32Min>(this, dst, src1, src2);
1401 __ Float32Min(dst, src1, src2, ool->entry());
1402 __ bind(ool->exit());
1403 break;
1404 }
1405 case kLoong64Float32Max: {
1406 FPURegister dst = i.OutputSingleRegister();
1407 FPURegister src1 = i.InputSingleRegister(0);
1408 FPURegister src2 = i.InputSingleRegister(1);
1409 auto ool = zone()->New<OutOfLineFloat32Max>(this, dst, src1, src2);
1410 __ Float32Max(dst, src1, src2, ool->entry());
1411 __ bind(ool->exit());
1412 break;
1413 }
1414 case kLoong64Float64Cmp: {
1415 FPURegister left = i.InputOrZeroDoubleRegister(0);
1416 FPURegister right = i.InputOrZeroDoubleRegister(1);
1417 bool predicate;
1419 FlagsConditionToConditionCmpFPU(&predicate, instr->flags_condition());
1420 if ((left == kDoubleRegZero || right == kDoubleRegZero) &&
1421 !__ IsDoubleZeroRegSet()) {
1422 __ Move(kDoubleRegZero, 0.0);
1423 }
1424
1425 __ CompareF64(left, right, cc);
1426 } break;
1427 case kLoong64Float64Add:
1428 __ fadd_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
1429 i.InputDoubleRegister(1));
1430 break;
1431 case kLoong64Float64Sub:
1432 __ fsub_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
1433 i.InputDoubleRegister(1));
1434 break;
1435 case kLoong64Float64Mul:
1436 // TODO(LOONG_dev): LOONG64 add special case: right op is -1.0, see arm
1437 // port.
1438 __ fmul_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
1439 i.InputDoubleRegister(1));
1440 break;
1441 case kLoong64Float64Div:
1442 __ fdiv_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
1443 i.InputDoubleRegister(1));
1444 break;
1445 case kLoong64Float64Mod: {
1446 // TODO(turbofan): implement directly.
1447 FrameScope scope(masm(), StackFrame::MANUAL);
1448 UseScratchRegisterScope temps(masm());
1449 Register scratch = temps.Acquire();
1450 __ PrepareCallCFunction(0, 2, scratch);
1451 __ CallCFunction(ExternalReference::mod_two_doubles_operation(), 0, 2);
1452 break;
1453 }
1454 case kLoong64Float64Abs:
1455 __ fabs_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0));
1456 break;
1457 case kLoong64Float64Neg:
1458 __ Neg_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0));
1459 break;
1460 case kLoong64Float64Sqrt: {
1461 __ fsqrt_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0));
1462 break;
1463 }
1464 case kLoong64Float64Min: {
1465 FPURegister dst = i.OutputDoubleRegister();
1466 FPURegister src1 = i.InputDoubleRegister(0);
1467 FPURegister src2 = i.InputDoubleRegister(1);
1468 auto ool = zone()->New<OutOfLineFloat64Min>(this, dst, src1, src2);
1469 __ Float64Min(dst, src1, src2, ool->entry());
1470 __ bind(ool->exit());
1471 break;
1472 }
1473 case kLoong64Float64Max: {
1474 FPURegister dst = i.OutputDoubleRegister();
1475 FPURegister src1 = i.InputDoubleRegister(0);
1476 FPURegister src2 = i.InputDoubleRegister(1);
1477 auto ool = zone()->New<OutOfLineFloat64Max>(this, dst, src1, src2);
1478 __ Float64Max(dst, src1, src2, ool->entry());
1479 __ bind(ool->exit());
1480 break;
1481 }
1482 case kLoong64Float64RoundDown: {
1483 __ Floor_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0));
1484 break;
1485 }
1486 case kLoong64Float32RoundDown: {
1487 __ Floor_s(i.OutputSingleRegister(), i.InputSingleRegister(0));
1488 break;
1489 }
1490 case kLoong64Float64RoundTruncate: {
1491 __ Trunc_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0));
1492 break;
1493 }
1494 case kLoong64Float32RoundTruncate: {
1495 __ Trunc_s(i.OutputSingleRegister(), i.InputSingleRegister(0));
1496 break;
1497 }
1498 case kLoong64Float64RoundUp: {
1499 __ Ceil_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0));
1500 break;
1501 }
1502 case kLoong64Float32RoundUp: {
1503 __ Ceil_s(i.OutputSingleRegister(), i.InputSingleRegister(0));
1504 break;
1505 }
1506 case kLoong64Float64RoundTiesEven: {
1507 __ Round_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0));
1508 break;
1509 }
1510 case kLoong64Float32RoundTiesEven: {
1511 __ Round_s(i.OutputSingleRegister(), i.InputSingleRegister(0));
1512 break;
1513 }
1514 case kLoong64Float64SilenceNaN:
1515 __ FPUCanonicalizeNaN(i.OutputDoubleRegister(), i.InputDoubleRegister(0));
1516 break;
1517 case kLoong64Float64ToFloat32:
1518 __ fcvt_s_d(i.OutputSingleRegister(), i.InputDoubleRegister(0));
1519 break;
1520 case kLoong64Float32ToFloat64:
1521 __ fcvt_d_s(i.OutputDoubleRegister(), i.InputSingleRegister(0));
1522 break;
1523 case kLoong64Int32ToFloat64: {
1524 FPURegister scratch = kScratchDoubleReg;
1525 __ movgr2fr_w(scratch, i.InputRegister(0));
1526 __ ffint_d_w(i.OutputDoubleRegister(), scratch);
1527 break;
1528 }
1529 case kLoong64Int32ToFloat32: {
1530 FPURegister scratch = kScratchDoubleReg;
1531 __ movgr2fr_w(scratch, i.InputRegister(0));
1532 __ ffint_s_w(i.OutputDoubleRegister(), scratch);
1533 break;
1534 }
1535 case kLoong64Uint32ToFloat32: {
1536 __ Ffint_s_uw(i.OutputDoubleRegister(), i.InputRegister(0));
1537 break;
1538 }
1539 case kLoong64Int64ToFloat32: {
1540 FPURegister scratch = kScratchDoubleReg;
1541 __ movgr2fr_d(scratch, i.InputRegister(0));
1542 __ ffint_s_l(i.OutputDoubleRegister(), scratch);
1543 break;
1544 }
1545 case kLoong64Int64ToFloat64: {
1546 FPURegister scratch = kScratchDoubleReg;
1547 __ movgr2fr_d(scratch, i.InputRegister(0));
1548 __ ffint_d_l(i.OutputDoubleRegister(), scratch);
1549 break;
1550 }
1551 case kLoong64Uint32ToFloat64: {
1552 __ Ffint_d_uw(i.OutputDoubleRegister(), i.InputRegister(0));
1553 break;
1554 }
1555 case kLoong64Uint64ToFloat64: {
1556 __ Ffint_d_ul(i.OutputDoubleRegister(), i.InputRegister(0));
1557 break;
1558 }
1559 case kLoong64Uint64ToFloat32: {
1560 __ Ffint_s_ul(i.OutputDoubleRegister(), i.InputRegister(0));
1561 break;
1562 }
1563 case kLoong64Float64ToInt32: {
1564 FPURegister scratch = kScratchDoubleReg;
1565 __ ftintrz_w_d(scratch, i.InputDoubleRegister(0));
1566 __ movfr2gr_s(i.OutputRegister(), scratch);
1567 if (instr->OutputCount() > 1) {
1568 // Check for inputs below INT32_MIN and NaN.
1569 __ li(i.OutputRegister(1), 1);
1570 __ Move(scratch, static_cast<double>(INT32_MIN));
1571 __ CompareF64(scratch, i.InputDoubleRegister(0), CLE);
1572 __ LoadZeroIfNotFPUCondition(i.OutputRegister(1));
1573 __ Move(scratch, static_cast<double>(INT32_MAX) + 1);
1574 __ CompareF64(scratch, i.InputDoubleRegister(0), CLE);
1575 __ LoadZeroIfFPUCondition(i.OutputRegister(1));
1576 }
1577 break;
1578 }
1579 case kLoong64Float32ToInt32: {
1580 FPURegister scratch_d = kScratchDoubleReg;
1581 bool set_overflow_to_min_i32 = MiscField::decode(instr->opcode());
1582 __ ftintrz_w_s(scratch_d, i.InputDoubleRegister(0));
1583 __ movfr2gr_s(i.OutputRegister(), scratch_d);
1584 if (set_overflow_to_min_i32) {
1585 UseScratchRegisterScope temps(masm());
1586 Register scratch = temps.Acquire();
1587 // Avoid INT32_MAX as an overflow indicator and use INT32_MIN instead,
1588 // because INT32_MIN allows easier out-of-bounds detection.
1589 __ addi_w(scratch, i.OutputRegister(), 1);
1590 __ slt(scratch, scratch, i.OutputRegister());
1591 __ add_w(i.OutputRegister(), i.OutputRegister(), scratch);
1592 }
1593 break;
1594 }
1595 case kLoong64Float32ToInt64: {
1596 FPURegister scratch_d = kScratchDoubleReg;
1597
1598 bool load_status = instr->OutputCount() > 1;
1599 // Other arches use round to zero here, so we follow.
1600 __ ftintrz_l_s(scratch_d, i.InputDoubleRegister(0));
1601 __ movfr2gr_d(i.OutputRegister(), scratch_d);
1602 if (load_status) {
1603 Register output2 = i.OutputRegister(1);
1604 __ movfcsr2gr(output2, FCSR2);
1605 // Check for overflow and NaNs.
1606 __ And(output2, output2,
1608 __ Slt(output2, zero_reg, output2);
1609 __ xori(output2, output2, 1);
1610 }
1611 break;
1612 }
1613 case kLoong64Float64ToInt64: {
1614 UseScratchRegisterScope temps(masm());
1615 Register scratch = temps.Acquire();
1616 FPURegister scratch_d = kScratchDoubleReg;
1617
1618 bool set_overflow_to_min_i64 = MiscField::decode(instr->opcode());
1619 bool load_status = instr->OutputCount() > 1;
1620 // Other arches use round to zero here, so we follow.
1621 __ ftintrz_l_d(scratch_d, i.InputDoubleRegister(0));
1622 __ movfr2gr_d(i.OutputRegister(0), scratch_d);
1623 if (load_status) {
1624 Register output2 = i.OutputRegister(1);
1625 __ movfcsr2gr(output2, FCSR2);
1626 // Check for overflow and NaNs.
1627 __ And(output2, output2,
1629 __ Slt(output2, zero_reg, output2);
1630 __ xori(output2, output2, 1);
1631 }
1632 if (set_overflow_to_min_i64) {
1633 // Avoid INT64_MAX as an overflow indicator and use INT64_MIN instead,
1634 // because INT64_MIN allows easier out-of-bounds detection.
1635 __ addi_d(scratch, i.OutputRegister(), 1);
1636 __ slt(scratch, scratch, i.OutputRegister());
1637 __ add_d(i.OutputRegister(), i.OutputRegister(), scratch);
1638 }
1639 break;
1640 }
1641 case kLoong64Float64ToUint32: {
1642 FPURegister scratch = kScratchDoubleReg;
1643 __ Ftintrz_uw_d(i.OutputRegister(), i.InputDoubleRegister(0), scratch);
1644 if (instr->OutputCount() > 1) {
1645 __ li(i.OutputRegister(1), 1);
1646 __ Move(scratch, static_cast<double>(-1.0));
1647 __ CompareF64(scratch, i.InputDoubleRegister(0), CLT);
1648 __ LoadZeroIfNotFPUCondition(i.OutputRegister(1));
1649 __ Move(scratch, static_cast<double>(UINT32_MAX) + 1);
1650 __ CompareF64(scratch, i.InputDoubleRegister(0), CLE);
1651 __ LoadZeroIfFPUCondition(i.OutputRegister(1));
1652 }
1653 break;
1654 }
1655 case kLoong64Float32ToUint32: {
1656 FPURegister scratch = kScratchDoubleReg;
1657 bool set_overflow_to_min_i32 = MiscField::decode(instr->opcode());
1658 __ Ftintrz_uw_s(i.OutputRegister(), i.InputDoubleRegister(0), scratch);
1659 if (set_overflow_to_min_i32) {
1660 UseScratchRegisterScope temps(masm());
1661 Register scratch = temps.Acquire();
1662 // Avoid UINT32_MAX as an overflow indicator and use 0 instead,
1663 // because 0 allows easier out-of-bounds detection.
1664 __ addi_w(scratch, i.OutputRegister(), 1);
1665 __ Movz(i.OutputRegister(), zero_reg, scratch);
1666 }
1667 break;
1668 }
1669 case kLoong64Float32ToUint64: {
1670 FPURegister scratch = kScratchDoubleReg;
1671 Register result = instr->OutputCount() > 1 ? i.OutputRegister(1) : no_reg;
1672 __ Ftintrz_ul_s(i.OutputRegister(), i.InputDoubleRegister(0), scratch,
1673 result);
1674 break;
1675 }
1676 case kLoong64Float64ToUint64: {
1677 FPURegister scratch = kScratchDoubleReg;
1678 Register result = instr->OutputCount() > 1 ? i.OutputRegister(1) : no_reg;
1679 __ Ftintrz_ul_d(i.OutputRegister(0), i.InputDoubleRegister(0), scratch,
1680 result);
1681 break;
1682 }
1683 case kLoong64BitcastDL:
1684 __ movfr2gr_d(i.OutputRegister(), i.InputDoubleRegister(0));
1685 break;
1686 case kLoong64BitcastLD:
1687 __ movgr2fr_d(i.OutputDoubleRegister(), i.InputRegister(0));
1688 break;
1689 case kLoong64Float64ExtractLowWord32:
1690 __ FmoveLow(i.OutputRegister(), i.InputDoubleRegister(0));
1691 break;
1692 case kLoong64Float64ExtractHighWord32:
1693 __ movfrh2gr_s(i.OutputRegister(), i.InputDoubleRegister(0));
1694 break;
1695 case kLoong64Float64FromWord32Pair:
1696 __ movgr2fr_w(i.OutputDoubleRegister(), i.InputRegister(1));
1697 __ movgr2frh_w(i.OutputDoubleRegister(), i.InputRegister(0));
1698 break;
1699 case kLoong64Float64InsertLowWord32:
1700 __ FmoveLow(i.OutputDoubleRegister(), i.InputRegister(1));
1701 break;
1702 case kLoong64Float64InsertHighWord32:
1703 __ movgr2frh_w(i.OutputDoubleRegister(), i.InputRegister(1));
1704 break;
1705 // ... more basic instructions ...
1706
1707 case kLoong64Ext_w_b:
1708 __ ext_w_b(i.OutputRegister(), i.InputRegister(0));
1709 break;
1710 case kLoong64Ext_w_h:
1711 __ ext_w_h(i.OutputRegister(), i.InputRegister(0));
1712 break;
1713 case kLoong64Ld_bu:
1714 RecordTrapInfoIfNeeded(zone(), this, opcode, instr, __ pc_offset());
1715 __ Ld_bu(i.OutputRegister(), i.MemoryOperand());
1716 break;
1717 case kLoong64Ld_b:
1718 RecordTrapInfoIfNeeded(zone(), this, opcode, instr, __ pc_offset());
1719 __ Ld_b(i.OutputRegister(), i.MemoryOperand());
1720 break;
1721 case kLoong64St_b: {
1722 size_t index = 0;
1723 MemOperand mem = i.MemoryOperand(&index);
1724 RecordTrapInfoIfNeeded(zone(), this, opcode, instr, __ pc_offset());
1725 __ St_b(i.InputOrZeroRegister(index), mem);
1726 break;
1727 }
1728 case kLoong64Ld_hu:
1729 RecordTrapInfoIfNeeded(zone(), this, opcode, instr, __ pc_offset());
1730 __ Ld_hu(i.OutputRegister(), i.MemoryOperand());
1731 break;
1732 case kLoong64Ld_h:
1733 RecordTrapInfoIfNeeded(zone(), this, opcode, instr, __ pc_offset());
1734 __ Ld_h(i.OutputRegister(), i.MemoryOperand());
1735 break;
1736 case kLoong64St_h: {
1737 size_t index = 0;
1738 MemOperand mem = i.MemoryOperand(&index);
1739 RecordTrapInfoIfNeeded(zone(), this, opcode, instr, __ pc_offset());
1740 __ St_h(i.InputOrZeroRegister(index), mem);
1741 break;
1742 }
1743 case kLoong64Ld_w:
1744 RecordTrapInfoIfNeeded(zone(), this, opcode, instr, __ pc_offset());
1745 __ Ld_w(i.OutputRegister(), i.MemoryOperand());
1746 break;
1747 case kLoong64Ld_wu:
1748 RecordTrapInfoIfNeeded(zone(), this, opcode, instr, __ pc_offset());
1749 __ Ld_wu(i.OutputRegister(), i.MemoryOperand());
1750 break;
1751 case kLoong64Ld_d:
1752 RecordTrapInfoIfNeeded(zone(), this, opcode, instr, __ pc_offset());
1753 __ Ld_d(i.OutputRegister(), i.MemoryOperand());
1754 break;
1755 case kLoong64St_w: {
1756 size_t index = 0;
1757 MemOperand mem = i.MemoryOperand(&index);
1758 RecordTrapInfoIfNeeded(zone(), this, opcode, instr, __ pc_offset());
1759 __ St_w(i.InputOrZeroRegister(index), mem);
1760 break;
1761 }
1762 case kLoong64St_d: {
1763 size_t index = 0;
1764 MemOperand mem = i.MemoryOperand(&index);
1765 RecordTrapInfoIfNeeded(zone(), this, opcode, instr, __ pc_offset());
1766 __ St_d(i.InputOrZeroRegister(index), mem);
1767 break;
1768 }
1769 case kLoong64LoadDecompressTaggedSigned:
1770 RecordTrapInfoIfNeeded(zone(), this, opcode, instr, __ pc_offset());
1771 __ DecompressTaggedSigned(i.OutputRegister(), i.MemoryOperand());
1772 break;
1773 case kLoong64LoadDecompressTagged:
1774 RecordTrapInfoIfNeeded(zone(), this, opcode, instr, __ pc_offset());
1775 __ DecompressTagged(i.OutputRegister(), i.MemoryOperand());
1776 break;
1777 case kLoong64LoadDecompressProtected:
1778 RecordTrapInfoIfNeeded(zone(), this, opcode, instr, __ pc_offset());
1779 __ DecompressProtected(i.OutputRegister(), i.MemoryOperand());
1780 break;
1781 case kLoong64StoreCompressTagged: {
1782 size_t index = 0;
1783 MemOperand mem = i.MemoryOperand(&index);
1784 RecordTrapInfoIfNeeded(zone(), this, opcode, instr, __ pc_offset());
1785 __ StoreTaggedField(i.InputOrZeroRegister(index), mem);
1786 break;
1787 }
1788 case kLoong64LoadDecodeSandboxedPointer:
1789 __ LoadSandboxedPointerField(i.OutputRegister(), i.MemoryOperand());
1790 break;
1791 case kLoong64StoreEncodeSandboxedPointer: {
1792 size_t index = 0;
1793 MemOperand mem = i.MemoryOperand(&index);
1794 __ StoreSandboxedPointerField(i.InputOrZeroRegister(index), mem);
1795 break;
1796 }
1797 case kLoong64StoreIndirectPointer: {
1798 size_t index = 0;
1799 MemOperand mem = i.MemoryOperand(&index);
1800 __ StoreIndirectPointerField(i.InputOrZeroRegister(index), mem);
1801 break;
1802 }
1803 case kLoong64AtomicLoadDecompressTaggedSigned:
1804 __ AtomicDecompressTaggedSigned(i.OutputRegister(), i.MemoryOperand());
1805 break;
1806 case kLoong64AtomicLoadDecompressTagged:
1807 __ AtomicDecompressTagged(i.OutputRegister(), i.MemoryOperand());
1808 break;
1809 case kLoong64AtomicStoreCompressTagged: {
1810 size_t index = 0;
1811 MemOperand mem = i.MemoryOperand(&index);
1812 __ AtomicStoreTaggedField(i.InputOrZeroRegister(index), mem);
1813 break;
1814 }
1815 case kLoong64Fld_s: {
1816 RecordTrapInfoIfNeeded(zone(), this, opcode, instr, __ pc_offset());
1817 __ Fld_s(i.OutputSingleRegister(), i.MemoryOperand());
1818 break;
1819 }
1820 case kLoong64Fst_s: {
1821 size_t index = 0;
1822 MemOperand operand = i.MemoryOperand(&index);
1823 FPURegister ft = i.InputOrZeroSingleRegister(index);
1824 if (ft == kDoubleRegZero && !__ IsDoubleZeroRegSet()) {
1825 __ Move(kDoubleRegZero, 0.0);
1826 }
1827 RecordTrapInfoIfNeeded(zone(), this, opcode, instr, __ pc_offset());
1828 __ Fst_s(ft, operand);
1829 break;
1830 }
1831 case kLoong64Fld_d:
1832 RecordTrapInfoIfNeeded(zone(), this, opcode, instr, __ pc_offset());
1833 __ Fld_d(i.OutputDoubleRegister(), i.MemoryOperand());
1834 break;
1835 case kLoong64Fst_d: {
1836 size_t index = 0;
1837 MemOperand operand = i.MemoryOperand(&index);
1838 FPURegister ft = i.InputOrZeroDoubleRegister(index);
1839 if (ft == kDoubleRegZero && !__ IsDoubleZeroRegSet()) {
1840 __ Move(kDoubleRegZero, 0.0);
1841 }
1842 RecordTrapInfoIfNeeded(zone(), this, opcode, instr, __ pc_offset());
1843 __ Fst_d(ft, operand);
1844 break;
1845 }
1846 case kLoong64Dbar: {
1847 __ dbar(0);
1848 break;
1849 }
1850 case kLoong64Push:
1851 if (instr->InputAt(0)->IsFPRegister()) {
1852 __ Fst_d(i.InputDoubleRegister(0), MemOperand(sp, -kDoubleSize));
1853 __ Sub_d(sp, sp, Operand(kDoubleSize));
1855 } else {
1856 __ Push(i.InputRegister(0));
1858 }
1859 break;
1860 case kLoong64Peek: {
1861 int reverse_slot = i.InputInt32(0);
1862 int offset =
1863 FrameSlotToFPOffset(frame()->GetTotalFrameSlotCount() - reverse_slot);
1864 if (instr->OutputAt(0)->IsFPRegister()) {
1865 LocationOperand* op = LocationOperand::cast(instr->OutputAt(0));
1866 if (op->representation() == MachineRepresentation::kFloat64) {
1867 __ Fld_d(i.OutputDoubleRegister(), MemOperand(fp, offset));
1868 } else if (op->representation() == MachineRepresentation::kFloat32) {
1869 __ Fld_s(i.OutputSingleRegister(0), MemOperand(fp, offset));
1870 } else {
1871 DCHECK_EQ(MachineRepresentation::kSimd128, op->representation());
1872 abort();
1873 }
1874 } else {
1875 __ Ld_d(i.OutputRegister(0), MemOperand(fp, offset));
1876 }
1877 break;
1878 }
1879 case kLoong64StackClaim: {
1880 __ Sub_d(sp, sp, Operand(i.InputInt32(0)));
1881 frame_access_state()->IncreaseSPDelta(i.InputInt32(0) /
1883 break;
1884 }
1885 case kLoong64Poke: {
1886 if (instr->InputAt(0)->IsFPRegister()) {
1887 __ Fst_d(i.InputDoubleRegister(0), MemOperand(sp, i.InputInt32(1)));
1888 } else {
1889 __ St_d(i.InputRegister(0), MemOperand(sp, i.InputInt32(1)));
1890 }
1891 break;
1892 }
1893 case kLoong64ByteSwap64: {
1894 __ ByteSwap(i.OutputRegister(0), i.InputRegister(0), 8);
1895 break;
1896 }
1897 case kLoong64ByteSwap32: {
1898 __ ByteSwap(i.OutputRegister(0), i.InputRegister(0), 4);
1899 break;
1900 }
1901 case kAtomicLoadInt8:
1904 break;
1905 case kAtomicLoadUint8:
1907 break;
1908 case kAtomicLoadInt16:
1911 break;
1912 case kAtomicLoadUint16:
1914 break;
1915 case kAtomicLoadWord32:
1917 break;
1918 case kLoong64Word64AtomicLoadUint32:
1920 break;
1921 case kLoong64Word64AtomicLoadUint64:
1923 break;
1924 case kAtomicStoreWord8:
1926 break;
1927 case kAtomicStoreWord16:
1929 break;
1930 case kAtomicStoreWord32:
1932 break;
1933 case kLoong64Word64AtomicStoreWord64:
1935 break;
1936 case kAtomicExchangeInt8:
1938 ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT(Ll_w, Sc_w, true, 8, 32);
1939 break;
1940 case kAtomicExchangeUint8:
1941 switch (AtomicWidthField::decode(opcode)) {
1943 ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT(Ll_w, Sc_w, false, 8, 32);
1944 break;
1946 ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT(Ll_d, Sc_d, false, 8, 64);
1947 break;
1948 }
1949 break;
1950 case kAtomicExchangeInt16:
1952 ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT(Ll_w, Sc_w, true, 16, 32);
1953 break;
1954 case kAtomicExchangeUint16:
1955 switch (AtomicWidthField::decode(opcode)) {
1957 ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT(Ll_w, Sc_w, false, 16, 32);
1958 break;
1960 ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT(Ll_d, Sc_d, false, 16, 64);
1961 break;
1962 }
1963 break;
1964 case kAtomicExchangeWord32:
1965 switch (AtomicWidthField::decode(opcode)) {
1967 __ add_d(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1));
1968 RecordTrapInfoIfNeeded(zone(), this, opcode, instr, __ pc_offset());
1969 __ amswap_db_w(i.OutputRegister(0), i.InputRegister(2),
1970 i.TempRegister(0));
1971 break;
1973 ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT(Ll_d, Sc_d, false, 32, 64);
1974 break;
1975 }
1976 break;
1977 case kLoong64Word64AtomicExchangeUint64:
1978 __ add_d(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1));
1979 RecordTrapInfoIfNeeded(zone(), this, opcode, instr, __ pc_offset());
1980 __ amswap_db_d(i.OutputRegister(0), i.InputRegister(2),
1981 i.TempRegister(0));
1982 break;
1983 case kAtomicCompareExchangeInt8:
1985 ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER_EXT(Ll_w, Sc_w, true, 8, 32);
1986 break;
1987 case kAtomicCompareExchangeUint8:
1988 switch (AtomicWidthField::decode(opcode)) {
1991 32);
1992 break;
1995 64);
1996 break;
1997 }
1998 break;
1999 case kAtomicCompareExchangeInt16:
2001 ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER_EXT(Ll_w, Sc_w, true, 16, 32);
2002 break;
2003 case kAtomicCompareExchangeUint16:
2004 switch (AtomicWidthField::decode(opcode)) {
2007 32);
2008 break;
2011 64);
2012 break;
2013 }
2014 break;
2015 case kAtomicCompareExchangeWord32:
2016 switch (AtomicWidthField::decode(opcode)) {
2018 __ slli_w(i.InputRegister(2), i.InputRegister(2), 0);
2020 break;
2023 64);
2024 break;
2025 }
2026 break;
2027 case kLoong64Word64AtomicCompareExchangeUint64:
2029 break;
2030 case kAtomicAddWord32:
2031 switch (AtomicWidthField::decode(opcode)) {
2033 __ Add_d(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1));
2034 RecordTrapInfoIfNeeded(zone(), this, opcode, instr, __ pc_offset());
2035 __ amadd_db_w(i.OutputRegister(0), i.InputRegister(2),
2036 i.TempRegister(0));
2037 break;
2039 ASSEMBLE_ATOMIC_BINOP_EXT(Ll_d, Sc_d, false, 32, Add_d, 64);
2040 break;
2041 }
2042 break;
2043 case kAtomicSubWord32:
2044 switch (AtomicWidthField::decode(opcode)) {
2046 ASSEMBLE_ATOMIC_BINOP(Ll_w, Sc_w, Sub_w);
2047 break;
2049 ASSEMBLE_ATOMIC_BINOP_EXT(Ll_d, Sc_d, false, 32, Sub_d, 64);
2050 break;
2051 }
2052 break;
2053 case kAtomicAndWord32:
2054 switch (AtomicWidthField::decode(opcode)) {
2056 __ Add_d(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1));
2057 RecordTrapInfoIfNeeded(zone(), this, opcode, instr, __ pc_offset());
2058 __ amand_db_w(i.OutputRegister(0), i.InputRegister(2),
2059 i.TempRegister(0));
2060 break;
2062 ASSEMBLE_ATOMIC_BINOP_EXT(Ll_d, Sc_d, false, 32, And, 64);
2063 break;
2064 }
2065 break;
2066 case kAtomicOrWord32:
2067 switch (AtomicWidthField::decode(opcode)) {
2069 __ Add_d(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1));
2070 RecordTrapInfoIfNeeded(zone(), this, opcode, instr, __ pc_offset());
2071 __ amor_db_w(i.OutputRegister(0), i.InputRegister(2),
2072 i.TempRegister(0));
2073 break;
2075 ASSEMBLE_ATOMIC_BINOP_EXT(Ll_d, Sc_d, false, 32, Or, 64);
2076 break;
2077 }
2078 break;
2079 case kAtomicXorWord32:
2080 switch (AtomicWidthField::decode(opcode)) {
2082 __ Add_d(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1));
2083 RecordTrapInfoIfNeeded(zone(), this, opcode, instr, __ pc_offset());
2084 __ amxor_db_w(i.OutputRegister(0), i.InputRegister(2),
2085 i.TempRegister(0));
2086 break;
2088 ASSEMBLE_ATOMIC_BINOP_EXT(Ll_d, Sc_d, false, 32, Xor, 64);
2089 break;
2090 }
2091 break;
2092#define ATOMIC_BINOP_CASE(op, inst32, inst64) \
2093 case kAtomic##op##Int8: \
2094 DCHECK_EQ(AtomicWidthField::decode(opcode), AtomicWidth::kWord32); \
2095 ASSEMBLE_ATOMIC_BINOP_EXT(Ll_w, Sc_w, true, 8, inst32, 32); \
2096 break; \
2097 case kAtomic##op##Uint8: \
2098 switch (AtomicWidthField::decode(opcode)) { \
2099 case AtomicWidth::kWord32: \
2100 ASSEMBLE_ATOMIC_BINOP_EXT(Ll_w, Sc_w, false, 8, inst32, 32); \
2101 break; \
2102 case AtomicWidth::kWord64: \
2103 ASSEMBLE_ATOMIC_BINOP_EXT(Ll_d, Sc_d, false, 8, inst64, 64); \
2104 break; \
2105 } \
2106 break; \
2107 case kAtomic##op##Int16: \
2108 DCHECK_EQ(AtomicWidthField::decode(opcode), AtomicWidth::kWord32); \
2109 ASSEMBLE_ATOMIC_BINOP_EXT(Ll_w, Sc_w, true, 16, inst32, 32); \
2110 break; \
2111 case kAtomic##op##Uint16: \
2112 switch (AtomicWidthField::decode(opcode)) { \
2113 case AtomicWidth::kWord32: \
2114 ASSEMBLE_ATOMIC_BINOP_EXT(Ll_w, Sc_w, false, 16, inst32, 32); \
2115 break; \
2116 case AtomicWidth::kWord64: \
2117 ASSEMBLE_ATOMIC_BINOP_EXT(Ll_d, Sc_d, false, 16, inst64, 64); \
2118 break; \
2119 } \
2120 break;
2121 ATOMIC_BINOP_CASE(Add, Add_w, Add_d)
2122 ATOMIC_BINOP_CASE(Sub, Sub_w, Sub_d)
2123 ATOMIC_BINOP_CASE(And, And, And)
2124 ATOMIC_BINOP_CASE(Or, Or, Or)
2125 ATOMIC_BINOP_CASE(Xor, Xor, Xor)
2126#undef ATOMIC_BINOP_CASE
2127
2128 case kLoong64Word64AtomicAddUint64:
2129 __ Add_d(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1));
2130 RecordTrapInfoIfNeeded(zone(), this, opcode, instr, __ pc_offset());
2131 __ amadd_db_d(i.OutputRegister(0), i.InputRegister(2), i.TempRegister(0));
2132 break;
2133 case kLoong64Word64AtomicSubUint64:
2134 ASSEMBLE_ATOMIC_BINOP(Ll_d, Sc_d, Sub_d);
2135 break;
2136 case kLoong64Word64AtomicAndUint64:
2137 __ Add_d(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1));
2138 RecordTrapInfoIfNeeded(zone(), this, opcode, instr, __ pc_offset());
2139 __ amand_db_d(i.OutputRegister(0), i.InputRegister(2), i.TempRegister(0));
2140 break;
2141 case kLoong64Word64AtomicOrUint64:
2142 __ Add_d(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1));
2143 RecordTrapInfoIfNeeded(zone(), this, opcode, instr, __ pc_offset());
2144 __ amor_db_d(i.OutputRegister(0), i.InputRegister(2), i.TempRegister(0));
2145 break;
2146 case kLoong64Word64AtomicXorUint64:
2147 __ Add_d(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1));
2148 RecordTrapInfoIfNeeded(zone(), this, opcode, instr, __ pc_offset());
2149 __ amxor_db_d(i.OutputRegister(0), i.InputRegister(2), i.TempRegister(0));
2150 break;
2151#undef ATOMIC_BINOP_CASE
2152 case kLoong64S128Const:
2153 case kLoong64S128Zero:
2154 case kLoong64I32x4Splat:
2155 case kLoong64I32x4ExtractLane:
2156 case kLoong64I32x4Add:
2157 case kLoong64I32x4ReplaceLane:
2158 case kLoong64I32x4Sub:
2159 case kLoong64F64x2Abs:
2160 default:
2161 break;
2162 }
2163 return kSuccess;
2164}
2165
2166#define UNSUPPORTED_COND(opcode, condition) \
2167 StdoutStream{} << "Unsupported " << #opcode << " condition: \"" << condition \
2168 << "\""; \
2169 UNIMPLEMENTED();
2170
2172 Operand* right, Register* temp0, Register* temp1) {
2173 bool need_signed = false;
2174 MachineRepresentation rep_left =
2176 need_signed = IsAnyTagged(rep_left) || IsAnyCompressed(rep_left) ||
2178 if (need_signed) {
2179 masm->slli_w(*temp0, *left, 0);
2180 *left = *temp0;
2181 }
2182
2183 if (instr->InputAt(1)->IsAnyLocationOperand()) {
2184 MachineRepresentation rep_right =
2186 need_signed = IsAnyTagged(rep_right) || IsAnyCompressed(rep_right) ||
2187 rep_right == MachineRepresentation::kWord64;
2188 if (need_signed && right->is_reg()) {
2189 DCHECK(*temp1 != no_reg);
2190 masm->slli_w(*temp1, right->rm(), 0);
2191 *right = Operand(*temp1);
2192 }
2193 }
2194}
2195
2198 Label* tlabel, Label* flabel, bool fallthru) {
2199#undef __
2200#define __ masm->
2202
2203 // LOONG64 does not have condition code flags, so compare and branch are
2204 // implemented differently than on the other arch's. The compare operations
2205 // emit loong64 pseudo-instructions, which are handled here by branch
2206 // instructions that do the actual comparison. Essential that the input
2207 // registers to compare pseudo-op are not modified before this branch op, as
2208 // they are tested here.
2209
2210 if (instr->arch_opcode() == kLoong64Tst) {
2211 Condition cc = FlagsConditionToConditionTst(condition);
2212 __ Branch(tlabel, cc, t8, Operand(zero_reg));
2213 UseScratchRegisterScope temps(masm);
2214 temps.Include(t8);
2215 } else if (instr->arch_opcode() == kLoong64Add_d ||
2216 instr->arch_opcode() == kLoong64Sub_d) {
2217 UseScratchRegisterScope temps(masm);
2218 Register scratch = temps.Acquire();
2219 Register scratch2 = temps.Acquire();
2220 Condition cc = FlagsConditionToConditionOvf(condition);
2221 __ srai_d(scratch, i.OutputRegister(), 32);
2222 __ srai_w(scratch2, i.OutputRegister(), 31);
2223 __ Branch(tlabel, cc, scratch2, Operand(scratch));
2224 } else if (instr->arch_opcode() == kLoong64AddOvf_d ||
2225 instr->arch_opcode() == kLoong64SubOvf_d) {
2226 switch (condition) {
2227 // Overflow occurs if overflow register is negative
2228 case kOverflow:
2229 __ Branch(tlabel, lt, t8, Operand(zero_reg));
2230 break;
2231 case kNotOverflow:
2232 __ Branch(tlabel, ge, t8, Operand(zero_reg));
2233 break;
2234 default:
2236 }
2237 UseScratchRegisterScope temps(masm);
2238 temps.Include(t8);
2239 } else if (instr->arch_opcode() == kLoong64MulOvf_w ||
2240 instr->arch_opcode() == kLoong64MulOvf_d) {
2241 // Overflow occurs if overflow register is not zero
2242 switch (condition) {
2243 case kOverflow:
2244 __ Branch(tlabel, ne, t8, Operand(zero_reg));
2245 break;
2246 case kNotOverflow:
2247 __ Branch(tlabel, eq, t8, Operand(zero_reg));
2248 break;
2249 default:
2251 }
2252 UseScratchRegisterScope temps(masm);
2253 temps.Include(t8);
2254 } else if (instr->arch_opcode() == kLoong64Cmp32 ||
2255 instr->arch_opcode() == kLoong64Cmp64) {
2256 Condition cc = FlagsConditionToConditionCmp(condition);
2257 Register left = i.InputRegister(0);
2258 Operand right = i.InputOperand(1);
2259 // Word32Compare has two temp registers.
2260 if (COMPRESS_POINTERS_BOOL && (instr->arch_opcode() == kLoong64Cmp32)) {
2261 Register temp0 = i.TempRegister(0);
2262 Register temp1 = right.is_reg() ? i.TempRegister(1) : no_reg;
2263 SignExtend(masm, instr, &left, &right, &temp0, &temp1);
2264 }
2265 __ Branch(tlabel, cc, left, right);
2266 } else if (instr->arch_opcode() == kArchStackPointerGreaterThan) {
2267 Condition cc = FlagsConditionToConditionCmp(condition);
2268 DCHECK((cc == ls) || (cc == hi));
2269 if (cc == ls) {
2270 __ xori(i.TempRegister(0), i.TempRegister(0), 1);
2271 }
2272 __ Branch(tlabel, ne, i.TempRegister(0), Operand(zero_reg));
2273 } else if (instr->arch_opcode() == kLoong64Float32Cmp ||
2274 instr->arch_opcode() == kLoong64Float64Cmp) {
2275 bool predicate;
2276 FlagsConditionToConditionCmpFPU(&predicate, condition);
2277 if (predicate) {
2278 __ BranchTrueF(tlabel);
2279 } else {
2280 __ BranchFalseF(tlabel);
2281 }
2282 } else {
2283 PrintF("AssembleArchBranch Unimplemented arch_opcode: %d\n",
2284 instr->arch_opcode());
2285 UNIMPLEMENTED();
2286 }
2287 if (!fallthru) __ Branch(flabel); // no fallthru to flabel.
2288#undef __
2289#define __ masm()->
2290}
2291
2292// Assembles branches after an instruction.
2293void CodeGenerator::AssembleArchBranch(Instruction* instr, BranchInfo* branch) {
2294 Label* tlabel = branch->true_label;
2295 Label* flabel = branch->false_label;
2296
2297 AssembleBranchToLabels(this, masm(), instr, branch->condition, tlabel, flabel,
2298 branch->fallthru);
2299}
2300
2302 BranchInfo* branch) {
2303 UNREACHABLE();
2304}
2305
2306#undef UNSUPPORTED_COND
2307
2309 BranchInfo* branch) {
2310 AssembleArchBranch(instr, branch);
2311}
2312
2314 RpoNumber target) {
2315 __ Branch(GetLabel(target));
2316}
2317
2318#if V8_ENABLE_WEBASSEMBLY
2319void CodeGenerator::AssembleArchTrap(Instruction* instr,
2321 auto ool = zone()->New<WasmOutOfLineTrap>(this, instr);
2322 Label* tlabel = ool->entry();
2323 AssembleBranchToLabels(this, masm(), instr, condition, tlabel, nullptr, true);
2324}
2325#endif // V8_ENABLE_WEBASSEMBLY
2326
2327// Assembles boolean materializations after an instruction.
2330 Loong64OperandConverter i(this, instr);
2331
2332 // Materialize a full 64-bit 1 or 0 value. The result register is always the
2333 // last output of the instruction.
2334 DCHECK_NE(0u, instr->OutputCount());
2335 Register result = i.OutputRegister(instr->OutputCount() - 1);
2336 // Loong64 does not have condition code flags, so compare and branch are
2337 // implemented differently than on the other arch's. The compare operations
2338 // emit loong64 pseudo-instructions, which are checked and handled here.
2339
2340 if (instr->arch_opcode() == kLoong64Tst) {
2341 Condition cc = FlagsConditionToConditionTst(condition);
2342 if (cc == eq) {
2343 __ Sltu(result, t8, 1);
2344 } else {
2345 __ Sltu(result, zero_reg, t8);
2346 }
2347 UseScratchRegisterScope temps(masm());
2348 temps.Include(t8);
2349 return;
2350 } else if (instr->arch_opcode() == kLoong64Add_d ||
2351 instr->arch_opcode() == kLoong64Sub_d) {
2352 UseScratchRegisterScope temps(masm());
2353 Register scratch = temps.Acquire();
2354 Condition cc = FlagsConditionToConditionOvf(condition);
2355 // Check for overflow creates 1 or 0 for result.
2356 __ srli_d(scratch, i.OutputRegister(), 63);
2357 __ srli_w(result, i.OutputRegister(), 31);
2358 __ xor_(result, scratch, result);
2359 if (cc == eq) // Toggle result for not overflow.
2360 __ xori(result, result, 1);
2361 return;
2362 } else if (instr->arch_opcode() == kLoong64AddOvf_d ||
2363 instr->arch_opcode() == kLoong64SubOvf_d) {
2364 // Overflow occurs if overflow register is negative
2365 __ slt(result, t8, zero_reg);
2366 UseScratchRegisterScope temps(masm());
2367 temps.Include(t8);
2368 } else if (instr->arch_opcode() == kLoong64MulOvf_w ||
2369 instr->arch_opcode() == kLoong64MulOvf_d) {
2370 // Overflow occurs if overflow register is not zero
2371 __ Sgtu(result, t8, zero_reg);
2372 UseScratchRegisterScope temps(masm());
2373 temps.Include(t8);
2374 } else if (instr->arch_opcode() == kLoong64Cmp32 ||
2375 instr->arch_opcode() == kLoong64Cmp64) {
2376 Condition cc = FlagsConditionToConditionCmp(condition);
2377 Register left = i.InputRegister(0);
2378 Operand right = i.InputOperand(1);
2379 if (COMPRESS_POINTERS_BOOL && (instr->arch_opcode() == kLoong64Cmp32)) {
2380 Register temp0 = i.TempRegister(0);
2381 Register temp1 = right.is_reg() ? i.TempRegister(1) : no_reg;
2382 SignExtend(masm(), instr, &left, &right, &temp0, &temp1);
2383 }
2384 __ CompareWord(cc, result, left, right);
2385 return;
2386 } else if (instr->arch_opcode() == kLoong64Float64Cmp ||
2387 instr->arch_opcode() == kLoong64Float32Cmp) {
2388 FPURegister left = i.InputOrZeroDoubleRegister(0);
2389 FPURegister right = i.InputOrZeroDoubleRegister(1);
2390 if ((left == kDoubleRegZero || right == kDoubleRegZero) &&
2391 !__ IsDoubleZeroRegSet()) {
2392 __ Move(kDoubleRegZero, 0.0);
2393 }
2394 bool predicate;
2395 FlagsConditionToConditionCmpFPU(&predicate, condition);
2396 {
2397 __ movcf2gr(result, FCC0);
2398 if (!predicate) {
2399 __ xori(result, result, 1);
2400 }
2401 }
2402 return;
2403 } else if (instr->arch_opcode() == kArchStackPointerGreaterThan) {
2404 Condition cc = FlagsConditionToConditionCmp(condition);
2405 DCHECK((cc == ls) || (cc == hi));
2406 if (cc == ls) {
2407 __ xori(i.OutputRegister(), i.TempRegister(0), 1);
2408 }
2409 return;
2410 } else {
2411 PrintF("AssembleArchBranch Unimplemented arch_opcode is : %d\n",
2412 instr->arch_opcode());
2413 TRACE("UNIMPLEMENTED code_generator_loong64: %s at line %d\n", __FUNCTION__,
2414 __LINE__);
2415 UNIMPLEMENTED();
2416 }
2417}
2418
2420 UNREACHABLE();
2421}
2422
2424 Loong64OperandConverter i(this, instr);
2425 Register input = i.InputRegister(0);
2426 std::vector<std::pair<int32_t, Label*>> cases;
2427 for (size_t index = 2; index < instr->InputCount(); index += 2) {
2428 cases.push_back({i.InputInt32(index + 0), GetLabel(i.InputRpo(index + 1))});
2429 }
2430
2431 UseScratchRegisterScope temps(masm());
2432 Register scratch = temps.Acquire();
2433 // The input register may contains dirty data in upper 32 bits, explicitly
2434 // sign-extend it here.
2435 __ slli_w(scratch, input, 0);
2436 AssembleArchBinarySearchSwitchRange(scratch, i.InputRpo(1), cases.data(),
2437 cases.data() + cases.size());
2438}
2439
2441 Loong64OperandConverter i(this, instr);
2442 Register input = i.InputRegister(0);
2443 size_t const case_count = instr->InputCount() - 2;
2444
2445 UseScratchRegisterScope temps(masm());
2446 Register scratch = temps.Acquire();
2447 // The input register may contains dirty data in upper 32 bits, explicitly
2448 // sign-extend it here.
2449 __ slli_w(scratch, input, 0);
2450 __ Branch(GetLabel(i.InputRpo(1)), hs, scratch, Operand(case_count));
2451 __ GenerateSwitchTable(scratch, case_count, [&i, this](size_t index) {
2452 return GetLabel(i.InputRpo(index + 2));
2453 });
2454}
2455
2458 UNIMPLEMENTED();
2459}
2460
2461void CodeGenerator::FinishFrame(Frame* frame) {
2462 auto call_descriptor = linkage()->GetIncomingDescriptor();
2463
2464 const DoubleRegList saves_fpu = call_descriptor->CalleeSavedFPRegisters();
2465 if (!saves_fpu.is_empty()) {
2466 int count = saves_fpu.Count();
2468 frame->AllocateSavedCalleeRegisterSlots(count *
2470 }
2471
2472 const RegList saves = call_descriptor->CalleeSavedRegisters();
2473 if (!saves.is_empty()) {
2474 int count = saves.Count();
2475 frame->AllocateSavedCalleeRegisterSlots(count);
2476 }
2477}
2478
2480 auto call_descriptor = linkage()->GetIncomingDescriptor();
2481
2482 if (frame_access_state()->has_frame()) {
2483 if (call_descriptor->IsCFunctionCall()) {
2484#if V8_ENABLE_WEBASSEMBLY
2485 if (info()->GetOutputStackFrameType() == StackFrame::C_WASM_ENTRY) {
2486 __ StubPrologue(StackFrame::C_WASM_ENTRY);
2487 // Reserve stack space for saving the c_entry_fp later.
2488 __ Sub_d(sp, sp, Operand(kSystemPointerSize));
2489#else
2490 // For balance.
2491 if (false) {
2492#endif // V8_ENABLE_WEBASSEMBLY
2493 } else {
2494 __ Push(ra, fp);
2495 __ mov(fp, sp);
2496 }
2497 } else if (call_descriptor->IsJSFunctionCall()) {
2498 __ Prologue();
2499 } else {
2500 __ StubPrologue(info()->GetOutputStackFrameType());
2501#if V8_ENABLE_WEBASSEMBLY
2502 if (call_descriptor->IsAnyWasmFunctionCall() ||
2503 call_descriptor->IsWasmImportWrapper() ||
2504 call_descriptor->IsWasmCapiFunction()) {
2505 // For import wrappers and C-API functions, this stack slot is only used
2506 // for printing stack traces in V8. Also, it holds a WasmImportData
2507 // instead of the trusted instance data, which is taken care of in the
2508 // frames accessors.
2510 }
2511 if (call_descriptor->IsWasmCapiFunction()) {
2512 // Reserve space for saving the PC later.
2513 __ Sub_d(sp, sp, Operand(kSystemPointerSize));
2514 }
2515#endif // V8_ENABLE_WEBASSEMBLY
2516 }
2517 }
2518
2519 int required_slots =
2520 frame()->GetTotalFrameSlotCount() - frame()->GetFixedSlotCount();
2521
2522 if (info()->is_osr()) {
2523 // TurboFan OSR-compiled functions cannot be entered directly.
2524 __ Abort(AbortReason::kShouldNotDirectlyEnterOsrFunction);
2525
2526 // Unoptimized code jumps directly to this entrypoint while the unoptimized
2527 // frame is still on the stack. Optimized code uses OSR values directly from
2528 // the unoptimized frame. Thus, all that needs to be done is to allocate the
2529 // remaining stack slots.
2530 __ RecordComment("-- OSR entrypoint --");
2532 required_slots -= osr_helper()->UnoptimizedFrameSlots();
2533 }
2534
2535 const RegList saves = call_descriptor->CalleeSavedRegisters();
2536 const DoubleRegList saves_fpu = call_descriptor->CalleeSavedFPRegisters();
2537
2538 if (required_slots > 0) {
2539 DCHECK(frame_access_state()->has_frame());
2540#if V8_ENABLE_WEBASSEMBLY
2541 if (info()->IsWasm() && required_slots * kSystemPointerSize > 4 * KB) {
2542 // For WebAssembly functions with big frames we have to do the stack
2543 // overflow check before we construct the frame. Otherwise we may not
2544 // have enough space on the stack to call the runtime for the stack
2545 // overflow.
2546 Label done;
2547
2548 // If the frame is bigger than the stack, we throw the stack overflow
2549 // exception unconditionally. Thereby we can avoid the integer overflow
2550 // check in the condition code.
2551 if (required_slots * kSystemPointerSize < v8_flags.stack_size * KB) {
2552 UseScratchRegisterScope temps(masm());
2553 Register stack_limit = temps.Acquire();
2554 __ LoadStackLimit(stack_limit,
2555 MacroAssembler::StackLimitKind::kRealStackLimit);
2556 __ Add_d(stack_limit, stack_limit,
2557 Operand(required_slots * kSystemPointerSize));
2558 __ Branch(&done, uge, sp, Operand(stack_limit));
2559 }
2560
2561 if (v8_flags.experimental_wasm_growable_stacks) {
2564 regs_to_save.set(
2565 WasmHandleStackOverflowDescriptor::FrameBaseRegister());
2566
2567 for (auto reg : wasm::kGpParamRegisters) regs_to_save.set(reg);
2568 __ MultiPush(regs_to_save);
2569 DoubleRegList fp_regs_to_save;
2570 for (auto reg : wasm::kFpParamRegisters) fp_regs_to_save.set(reg);
2571 __ MultiPushFPU(fp_regs_to_save);
2573 required_slots * kSystemPointerSize);
2574 __ Add_d(
2575 WasmHandleStackOverflowDescriptor::FrameBaseRegister(), fp,
2576 Operand(call_descriptor->ParameterSlotCount() * kSystemPointerSize +
2578 __ CallBuiltin(Builtin::kWasmHandleStackOverflow);
2579 __ MultiPopFPU(fp_regs_to_save);
2580 __ MultiPop(regs_to_save);
2581 } else {
2582 __ Call(static_cast<intptr_t>(Builtin::kWasmStackOverflow),
2584 // The call does not return, hence we can ignore any references and just
2585 // define an empty safepoint.
2586 ReferenceMap* reference_map = zone()->New<ReferenceMap>(zone());
2587 RecordSafepoint(reference_map);
2588 if (v8_flags.debug_code) {
2589 __ stop();
2590 }
2591 }
2592
2593 __ bind(&done);
2594 }
2595#endif // V8_ENABLE_WEBASSEMBLY
2596 }
2597
2598 const int returns = frame()->GetReturnSlotCount();
2599
2600 // Skip callee-saved and return slots, which are pushed below.
2601 required_slots -= saves.Count();
2602 required_slots -= saves_fpu.Count();
2603 required_slots -= returns;
2604 if (required_slots > 0) {
2605 __ Sub_d(sp, sp, Operand(required_slots * kSystemPointerSize));
2606 }
2607
2608 if (!saves_fpu.is_empty()) {
2609 // Save callee-saved FPU registers.
2610 __ MultiPushFPU(saves_fpu);
2611 DCHECK_EQ(kNumCalleeSavedFPU, saves_fpu.Count());
2612 }
2613
2614 if (!saves.is_empty()) {
2615 // Save callee-saved registers.
2616 __ MultiPush(saves);
2617 }
2618
2619 if (returns != 0) {
2620 // Create space for returns.
2621 __ Sub_d(sp, sp, Operand(returns * kSystemPointerSize));
2622 }
2623
2624 for (int spill_slot : frame()->tagged_slots()) {
2625 FrameOffset offset = frame_access_state()->GetFrameOffset(spill_slot);
2626 DCHECK(offset.from_frame_pointer());
2627 __ St_d(zero_reg, MemOperand(fp, offset.offset()));
2628 }
2629}
2630
2631void CodeGenerator::AssembleReturn(InstructionOperand* additional_pop_count) {
2632 auto call_descriptor = linkage()->GetIncomingDescriptor();
2633
2634 const int returns = frame()->GetReturnSlotCount();
2635 if (returns != 0) {
2636 __ Add_d(sp, sp, Operand(returns * kSystemPointerSize));
2637 }
2638
2639 // Restore GP registers.
2640 const RegList saves = call_descriptor->CalleeSavedRegisters();
2641 if (!saves.is_empty()) {
2642 __ MultiPop(saves);
2643 }
2644
2645 // Restore FPU registers.
2646 const DoubleRegList saves_fpu = call_descriptor->CalleeSavedFPRegisters();
2647 if (!saves_fpu.is_empty()) {
2648 __ MultiPopFPU(saves_fpu);
2649 }
2650
2651 Loong64OperandConverter g(this, nullptr);
2652
2653 const int parameter_slots =
2654 static_cast<int>(call_descriptor->ParameterSlotCount());
2655
2656 // {aditional_pop_count} is only greater than zero if {parameter_slots = 0}.
2657 // Check RawMachineAssembler::PopAndReturn.
2658 if (parameter_slots != 0) {
2659 if (additional_pop_count->IsImmediate()) {
2660 DCHECK_EQ(g.ToConstant(additional_pop_count).ToInt32(), 0);
2661 } else if (v8_flags.debug_code) {
2662 __ Assert(eq, AbortReason::kUnexpectedAdditionalPopValue,
2663 g.ToRegister(additional_pop_count),
2664 Operand(static_cast<int64_t>(0)));
2665 }
2666 }
2667
2668#if V8_ENABLE_WEBASSEMBLY
2669 if (call_descriptor->IsAnyWasmFunctionCall() &&
2670 v8_flags.experimental_wasm_growable_stacks) {
2671 Label done;
2672 {
2673 UseScratchRegisterScope temps{masm()};
2674 Register scratch = temps.Acquire();
2676 __ BranchShort(
2677 &done, ne, scratch,
2678 Operand(StackFrame::TypeToMarker(StackFrame::WASM_SEGMENT_START)));
2679 }
2682 __ MultiPush(regs_to_save);
2684 {
2685 UseScratchRegisterScope temps{masm()};
2686 Register scratch = temps.Acquire();
2687 __ PrepareCallCFunction(1, scratch);
2688 }
2689 __ CallCFunction(ExternalReference::wasm_shrink_stack(), 1);
2690 __ mov(fp, kReturnRegister0);
2691 __ MultiPop(regs_to_save);
2692 __ bind(&done);
2693 }
2694#endif // V8_ENABLE_WEBASSEMBLY
2695
2696 // Functions with JS linkage have at least one parameter (the receiver).
2697 // If {parameter_slots} == 0, it means it is a builtin with
2698 // kDontAdaptArgumentsSentinel, which takes care of JS arguments popping
2699 // itself.
2700 const bool drop_jsargs = frame_access_state()->has_frame() &&
2701 call_descriptor->IsJSFunctionCall() &&
2702 parameter_slots != 0;
2703
2704 if (call_descriptor->IsCFunctionCall()) {
2706 } else if (frame_access_state()->has_frame()) {
2707 // Canonicalize JSFunction return sites for now unless they have an variable
2708 // number of stack slot pops.
2709 if (additional_pop_count->IsImmediate() &&
2710 g.ToConstant(additional_pop_count).ToInt32() == 0) {
2711 if (return_label_.is_bound()) {
2712 __ Branch(&return_label_);
2713 return;
2714 } else {
2715 __ bind(&return_label_);
2716 }
2717 }
2718 if (drop_jsargs) {
2719 // Get the actual argument count
2721 }
2723 }
2724 if (drop_jsargs) {
2725 // We must pop all arguments from the stack (including the receiver). This
2726 // number of arguments is given by max(1 + argc_reg, parameter_count).
2727 if (parameter_slots > 1) {
2728 __ li(t1, parameter_slots);
2729 __ slt(t2, t0, t1);
2730 __ Movn(t0, t1, t2);
2731 }
2732 __ Alsl_d(sp, t0, sp, kSystemPointerSizeLog2);
2733 } else if (additional_pop_count->IsImmediate()) {
2734 int additional_count = g.ToConstant(additional_pop_count).ToInt32();
2735 __ Drop(parameter_slots + additional_count);
2736 } else {
2737 Register pop_reg = g.ToRegister(additional_pop_count);
2738 __ Drop(parameter_slots);
2739 __ Alsl_d(sp, pop_reg, sp, kSystemPointerSizeLog2);
2740 }
2741 __ Ret();
2742}
2743
2745
2747 ZoneDeque<DeoptimizationExit*>* exits) {}
2748
2749AllocatedOperand CodeGenerator::Push(InstructionOperand* source) {
2750 auto rep = LocationOperand::cast(source)->representation();
2751 int new_slots = ElementSizeInPointers(rep);
2752 Loong64OperandConverter g(this, nullptr);
2753 int last_frame_slot_id =
2754 frame_access_state_->frame()->GetTotalFrameSlotCount() - 1;
2755 int sp_delta = frame_access_state_->sp_delta();
2756 int slot_id = last_frame_slot_id + sp_delta + new_slots;
2757 AllocatedOperand stack_slot(LocationOperand::STACK_SLOT, rep, slot_id);
2758 if (source->IsRegister()) {
2759 __ Push(g.ToRegister(source));
2760 frame_access_state()->IncreaseSPDelta(new_slots);
2761 } else if (source->IsStackSlot()) {
2762 UseScratchRegisterScope temps(masm());
2763 Register scratch = temps.Acquire();
2764 __ Ld_d(scratch, g.ToMemOperand(source));
2765 __ Push(scratch);
2766 frame_access_state()->IncreaseSPDelta(new_slots);
2767 } else {
2768 // No push instruction for this operand type. Bump the stack pointer and
2769 // assemble the move.
2770 __ Sub_d(sp, sp, Operand(new_slots * kSystemPointerSize));
2771 frame_access_state()->IncreaseSPDelta(new_slots);
2772 AssembleMove(source, &stack_slot);
2773 }
2774 temp_slots_ += new_slots;
2775 return stack_slot;
2776}
2777
2778void CodeGenerator::Pop(InstructionOperand* dest, MachineRepresentation rep) {
2779 Loong64OperandConverter g(this, nullptr);
2780 int dropped_slots = ElementSizeInPointers(rep);
2781 if (dest->IsRegister()) {
2782 frame_access_state()->IncreaseSPDelta(-dropped_slots);
2783 __ Pop(g.ToRegister(dest));
2784 } else if (dest->IsStackSlot()) {
2785 frame_access_state()->IncreaseSPDelta(-dropped_slots);
2786 UseScratchRegisterScope temps(masm());
2787 Register scratch = temps.Acquire();
2788 __ Pop(scratch);
2789 __ St_d(scratch, g.ToMemOperand(dest));
2790 } else {
2791 int last_frame_slot_id =
2792 frame_access_state_->frame()->GetTotalFrameSlotCount() - 1;
2793 int sp_delta = frame_access_state_->sp_delta();
2794 int slot_id = last_frame_slot_id + sp_delta;
2795 AllocatedOperand stack_slot(LocationOperand::STACK_SLOT, rep, slot_id);
2796 AssembleMove(&stack_slot, dest);
2797 frame_access_state()->IncreaseSPDelta(-dropped_slots);
2798 __ Add_d(sp, sp, Operand(dropped_slots * kSystemPointerSize));
2799 }
2800 temp_slots_ -= dropped_slots;
2801}
2802
2804 if (temp_slots_ > 0) {
2806 __ Add_d(sp, sp, Operand(temp_slots_ * kSystemPointerSize));
2807 temp_slots_ = 0;
2808 }
2809}
2810
2811void CodeGenerator::MoveToTempLocation(InstructionOperand* source,
2813 // Must be kept in sync with {MoveTempLocationTo}.
2814 DCHECK(!source->IsImmediate());
2815 move_cycle_.temps.emplace(masm());
2816 auto& temps = *move_cycle_.temps;
2817 // Temporarily exclude the reserved scratch registers while we pick one to
2818 // resolve the move cycle. Re-include them immediately afterwards as they
2819 // might be needed for the move to the temp location.
2820 temps.Exclude(move_cycle_.scratch_regs);
2821 temps.ExcludeFp(move_cycle_.scratch_fpregs);
2822 if (!IsFloatingPoint(rep)) {
2823 if (temps.hasAvailable()) {
2824 Register scratch = move_cycle_.temps->Acquire();
2825 move_cycle_.scratch_reg.emplace(scratch);
2826 } else if (temps.hasAvailableFp()) {
2827 // Try to use an FP register if no GP register is available for non-FP
2828 // moves.
2829 FPURegister scratch = move_cycle_.temps->AcquireFp();
2830 move_cycle_.scratch_fpreg.emplace(scratch);
2831 }
2832 } else {
2833 DCHECK(temps.hasAvailableFp());
2834 FPURegister scratch = move_cycle_.temps->AcquireFp();
2835 move_cycle_.scratch_fpreg.emplace(scratch);
2836 }
2837 temps.Include(move_cycle_.scratch_regs);
2838 temps.IncludeFp(move_cycle_.scratch_fpregs);
2839 if (move_cycle_.scratch_reg.has_value()) {
2840 // A scratch register is available for this rep.
2841 AllocatedOperand scratch(LocationOperand::REGISTER, rep,
2842 move_cycle_.scratch_reg->code());
2843 AssembleMove(source, &scratch);
2844 } else if (move_cycle_.scratch_fpreg.has_value()) {
2845 // A scratch fp register is available for this rep.
2846 if (!IsFloatingPoint(rep)) {
2847 AllocatedOperand scratch(LocationOperand::REGISTER,
2849 move_cycle_.scratch_fpreg->code());
2850 Loong64OperandConverter g(this, nullptr);
2851 if (source->IsStackSlot()) {
2852 __ Fld_d(g.ToDoubleRegister(&scratch), g.ToMemOperand(source));
2853 } else {
2854 DCHECK(source->IsRegister());
2855 __ movgr2fr_d(g.ToDoubleRegister(&scratch), g.ToRegister(source));
2856 }
2857 } else {
2858 AllocatedOperand scratch(LocationOperand::REGISTER, rep,
2859 move_cycle_.scratch_fpreg->code());
2860 AssembleMove(source, &scratch);
2861 }
2862 } else {
2863 // The scratch registers are blocked by pending moves. Use the stack
2864 // instead.
2865 Push(source);
2866 }
2867}
2868
2869void CodeGenerator::MoveTempLocationTo(InstructionOperand* dest,
2871 if (move_cycle_.scratch_reg.has_value()) {
2872 AllocatedOperand scratch(LocationOperand::REGISTER, rep,
2873 move_cycle_.scratch_reg->code());
2874 AssembleMove(&scratch, dest);
2875 } else if (move_cycle_.scratch_fpreg.has_value()) {
2876 if (!IsFloatingPoint(rep)) {
2877 // We used a DoubleRegister to move a non-FP operand, change the
2878 // representation to correctly interpret the InstructionOperand's code.
2879 AllocatedOperand scratch(LocationOperand::REGISTER,
2881 move_cycle_.scratch_fpreg->code());
2882 Loong64OperandConverter g(this, nullptr);
2883 if (dest->IsStackSlot()) {
2884 __ Fst_d(g.ToDoubleRegister(&scratch), g.ToMemOperand(dest));
2885 } else {
2886 DCHECK(dest->IsRegister());
2887 __ movfr2gr_d(g.ToRegister(dest), g.ToDoubleRegister(&scratch));
2888 }
2889 } else {
2890 AllocatedOperand scratch(LocationOperand::REGISTER, rep,
2891 move_cycle_.scratch_fpreg->code());
2892 AssembleMove(&scratch, dest);
2893 }
2894 } else {
2895 Pop(dest, rep);
2896 }
2897 // Restore the default state to release the {UseScratchRegisterScope} and to
2898 // prepare for the next cycle.
2899 move_cycle_ = MoveCycleState();
2900}
2901
2902void CodeGenerator::SetPendingMove(MoveOperands* move) {
2903 InstructionOperand* src = &move->source();
2904 InstructionOperand* dst = &move->destination();
2905 UseScratchRegisterScope temps(masm());
2906 if (src->IsConstant() || (src->IsStackSlot() && dst->IsStackSlot())) {
2907 Register temp = temps.Acquire();
2909 }
2910 if (src->IsAnyStackSlot() || dst->IsAnyStackSlot()) {
2911 Loong64OperandConverter g(this, nullptr);
2912 bool src_need_scratch = false;
2913 bool dst_need_scratch = false;
2914 if (src->IsStackSlot()) {
2915 // Doubleword load/store
2916 MemOperand src_mem = g.ToMemOperand(src);
2917 src_need_scratch =
2918 (!is_int16(src_mem.offset()) || (src_mem.offset() & 0b11) != 0) &&
2919 (!is_int12(src_mem.offset()) && !src_mem.hasIndexReg());
2920 } else if (src->IsFPStackSlot()) {
2921 // DoubleWord float-pointing load/store.
2922 MemOperand src_mem = g.ToMemOperand(src);
2923 src_need_scratch = !is_int12(src_mem.offset()) && !src_mem.hasIndexReg();
2924 }
2925 if (dst->IsStackSlot()) {
2926 // Doubleword load/store
2927 MemOperand dst_mem = g.ToMemOperand(dst);
2928 dst_need_scratch =
2929 (!is_int16(dst_mem.offset()) || (dst_mem.offset() & 0b11) != 0) &&
2930 (!is_int12(dst_mem.offset()) && !dst_mem.hasIndexReg());
2931 } else if (dst->IsFPStackSlot()) {
2932 // DoubleWord float-pointing load/store.
2933 MemOperand dst_mem = g.ToMemOperand(dst);
2934 dst_need_scratch = !is_int12(dst_mem.offset()) && !dst_mem.hasIndexReg();
2935 }
2936 if (src_need_scratch || dst_need_scratch) {
2937 Register temp = temps.Acquire();
2939 }
2940 }
2941}
2942
2943namespace {
2944
2945bool Is32BitOperand(InstructionOperand* operand) {
2946 DCHECK(operand->IsStackSlot() || operand->IsRegister());
2948 return mr == MachineRepresentation::kWord32 ||
2951}
2952
2953// When we need only 32 bits, move only 32 bits, otherwise the destination
2954// register' upper 32 bits may contain dirty data.
2955bool Use32BitMove(InstructionOperand* source, InstructionOperand* destination) {
2956 return Is32BitOperand(source) && Is32BitOperand(destination);
2957}
2958
2959} // namespace
2960
2961void CodeGenerator::AssembleMove(InstructionOperand* source,
2962 InstructionOperand* destination) {
2963 Loong64OperandConverter g(this, nullptr);
2964 // Dispatch on the source and destination operand kinds. Not all
2965 // combinations are possible.
2966 if (source->IsRegister()) {
2967 DCHECK(destination->IsRegister() || destination->IsStackSlot());
2968 Register src = g.ToRegister(source);
2969 if (destination->IsRegister()) {
2970 __ mov(g.ToRegister(destination), src);
2971 } else {
2972 __ St_d(src, g.ToMemOperand(destination));
2973 }
2974 } else if (source->IsStackSlot()) {
2975 DCHECK(destination->IsRegister() || destination->IsStackSlot());
2976 MemOperand src = g.ToMemOperand(source);
2977 if (destination->IsRegister()) {
2978 if (Use32BitMove(source, destination)) {
2979 __ Ld_w(g.ToRegister(destination), src);
2980 } else {
2981 __ Ld_d(g.ToRegister(destination), src);
2982 }
2983 } else {
2984 UseScratchRegisterScope temps(masm());
2985 Register scratch = temps.Acquire();
2986 __ Ld_d(scratch, src);
2987 __ St_d(scratch, g.ToMemOperand(destination));
2988 }
2989 } else if (source->IsConstant()) {
2990 Constant src = g.ToConstant(source);
2991 if (destination->IsRegister() || destination->IsStackSlot()) {
2992 UseScratchRegisterScope temps(masm());
2993 Register scratch = temps.Acquire();
2994 Register dst =
2995 destination->IsRegister() ? g.ToRegister(destination) : scratch;
2996 switch (src.type()) {
2997 case Constant::kInt32:
2998 __ li(dst, Operand(src.ToInt32(), src.rmode()));
2999 break;
3000 case Constant::kFloat32:
3001 __ li(dst, Operand::EmbeddedNumber(src.ToFloat32()));
3002 break;
3003 case Constant::kInt64:
3004 __ li(dst, Operand(src.ToInt64(), src.rmode()));
3005 break;
3006 case Constant::kFloat64:
3007 __ li(dst, Operand::EmbeddedNumber(src.ToFloat64().value()));
3008 break;
3010 __ li(dst, src.ToExternalReference());
3011 break;
3012 case Constant::kHeapObject: {
3013 Handle<HeapObject> src_object = src.ToHeapObject();
3015 if (IsMaterializableFromRoot(src_object, &index)) {
3016 __ LoadRoot(dst, index);
3017 } else {
3018 __ li(dst, src_object);
3019 }
3020 break;
3021 }
3023 Handle<HeapObject> src_object = src.ToHeapObject();
3025 if (IsMaterializableFromRoot(src_object, &index)) {
3026 __ LoadTaggedRoot(dst, index);
3027 } else {
3028 __ li(dst, src_object, RelocInfo::COMPRESSED_EMBEDDED_OBJECT);
3029 }
3030 break;
3031 }
3033 UNREACHABLE(); // TODO(titzer): loading RPO numbers on LOONG64.
3034 }
3035 if (destination->IsStackSlot()) __ St_d(dst, g.ToMemOperand(destination));
3036 } else if (src.type() == Constant::kFloat32) {
3037 if (destination->IsFPStackSlot()) {
3038 MemOperand dst = g.ToMemOperand(destination);
3039 if (base::bit_cast<int32_t>(src.ToFloat32()) == 0) {
3040 __ St_d(zero_reg, dst);
3041 } else {
3042 UseScratchRegisterScope temps(masm());
3043 Register scratch = temps.Acquire();
3044 __ li(scratch, Operand(base::bit_cast<int32_t>(src.ToFloat32())));
3045 __ St_d(scratch, dst);
3046 }
3047 } else {
3048 DCHECK(destination->IsFPRegister());
3049 FloatRegister dst = g.ToSingleRegister(destination);
3050 __ Move(dst, src.ToFloat32());
3051 }
3052 } else {
3053 DCHECK_EQ(Constant::kFloat64, src.type());
3054 DoubleRegister dst = destination->IsFPRegister()
3055 ? g.ToDoubleRegister(destination)
3057 __ Move(dst, src.ToFloat64().value());
3058 if (destination->IsFPStackSlot()) {
3059 __ Fst_d(dst, g.ToMemOperand(destination));
3060 }
3061 }
3062 } else if (source->IsFPRegister()) {
3063 FPURegister src = g.ToDoubleRegister(source);
3064 if (destination->IsFPRegister()) {
3065 FPURegister dst = g.ToDoubleRegister(destination);
3066 __ Move(dst, src);
3067 } else {
3068 DCHECK(destination->IsFPStackSlot());
3069 __ Fst_d(src, g.ToMemOperand(destination));
3070 }
3071 } else if (source->IsFPStackSlot()) {
3072 DCHECK(destination->IsFPRegister() || destination->IsFPStackSlot());
3073 MemOperand src = g.ToMemOperand(source);
3074 if (destination->IsFPRegister()) {
3075 __ Fld_d(g.ToDoubleRegister(destination), src);
3076 } else {
3077 DCHECK(destination->IsFPStackSlot());
3078 FPURegister temp = kScratchDoubleReg;
3079 __ Fld_d(temp, src);
3080 __ Fst_d(temp, g.ToMemOperand(destination));
3081 }
3082 } else {
3083 UNREACHABLE();
3084 }
3085}
3086
3087void CodeGenerator::AssembleSwap(InstructionOperand* source,
3088 InstructionOperand* destination) {
3089 Loong64OperandConverter g(this, nullptr);
3090 // Dispatch on the source and destination operand kinds. Not all
3091 // combinations are possible.
3092 if (source->IsRegister()) {
3093 UseScratchRegisterScope temps(masm());
3094 Register scratch = temps.Acquire();
3095 // Register-register.
3096 Register src = g.ToRegister(source);
3097 if (destination->IsRegister()) {
3098 Register dst = g.ToRegister(destination);
3099 __ Move(scratch, src);
3100 __ Move(src, dst);
3101 __ Move(dst, scratch);
3102 } else {
3103 DCHECK(destination->IsStackSlot());
3104 MemOperand dst = g.ToMemOperand(destination);
3105 __ mov(scratch, src);
3106 __ Ld_d(src, dst);
3107 __ St_d(scratch, dst);
3108 }
3109 } else if (source->IsStackSlot()) {
3110 DCHECK(destination->IsStackSlot());
3111 // TODO(LOONG_dev): LOONG64 Optimize scratch registers usage
3112 // Since the Ld instruction may need a scratch reg,
3113 // we should not use both of the two scratch registers in
3114 // UseScratchRegisterScope here.
3115 UseScratchRegisterScope temps(masm());
3116 Register scratch = temps.Acquire();
3117 FPURegister scratch_d = kScratchDoubleReg;
3118 MemOperand src = g.ToMemOperand(source);
3119 MemOperand dst = g.ToMemOperand(destination);
3120 __ Ld_d(scratch, src);
3121 __ Fld_d(scratch_d, dst);
3122 __ St_d(scratch, dst);
3123 __ Fst_d(scratch_d, src);
3124 } else if (source->IsFPRegister()) {
3125 FPURegister scratch_d = kScratchDoubleReg;
3126 FPURegister src = g.ToDoubleRegister(source);
3127 if (destination->IsFPRegister()) {
3128 FPURegister dst = g.ToDoubleRegister(destination);
3129 __ Move(scratch_d, src);
3130 __ Move(src, dst);
3131 __ Move(dst, scratch_d);
3132 } else {
3133 DCHECK(destination->IsFPStackSlot());
3134 MemOperand dst = g.ToMemOperand(destination);
3135 __ Move(scratch_d, src);
3136 __ Fld_d(src, dst);
3137 __ Fst_d(scratch_d, dst);
3138 }
3139 } else if (source->IsFPStackSlot()) {
3140 DCHECK(destination->IsFPStackSlot());
3141 UseScratchRegisterScope temps(masm());
3142 Register scratch = temps.Acquire();
3143 FPURegister scratch_d = kScratchDoubleReg;
3144 MemOperand src = g.ToMemOperand(source);
3145 MemOperand dst = g.ToMemOperand(destination);
3146 __ Fld_d(scratch_d, src);
3147 __ Ld_d(scratch, dst);
3148 __ Fst_d(scratch_d, dst);
3149 __ St_d(scratch, src);
3150 } else {
3151 // No other combinations are possible.
3152 UNREACHABLE();
3153 }
3154}
3155
3156void CodeGenerator::AssembleJumpTable(base::Vector<Label*> targets) {
3157 // On 64-bit LOONG64 we emit the jump tables inline.
3158 UNREACHABLE();
3159}
3160
3161#undef ASSEMBLE_ATOMIC_LOAD_INTEGER
3162#undef ASSEMBLE_ATOMIC_STORE_INTEGER
3163#undef ASSEMBLE_ATOMIC_BINOP
3164#undef ASSEMBLE_ATOMIC_BINOP_EXT
3165#undef ASSEMBLE_ATOMIC_EXCHANGE_INTEGER
3166#undef ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT
3167#undef ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER
3168#undef ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER_EXT
3169#undef ASSEMBLE_IEEE754_BINOP
3170#undef ASSEMBLE_IEEE754_UNOP
3171
3172#undef TRACE
3173#undef __
3174
3175} // namespace compiler
3176} // namespace internal
3177} // namespace v8
friend Zone
Definition asm-types.cc:195
#define Assert(condition)
static constexpr T decode(U value)
Definition bit-field.h:66
void slli_w(Register rd, Register rj, int32_t ui5)
static constexpr bool IsBuiltinId(Builtin builtin)
Definition builtins.h:128
static constexpr int kFixedSlotCountAboveFp
static constexpr int kFixedFrameSizeAboveFp
static V8_EXPORT_PRIVATE ExternalReference isolate_address()
Bootstrapper * bootstrapper()
Definition isolate.h:1178
RootsTable & roots_table()
Definition isolate.h:1250
Tagged_t ReadOnlyRootPtr(RootIndex index)
static constexpr MainThreadFlags kPointersToHereAreInterestingMask
static constexpr MainThreadFlags kPointersFromHereAreInterestingMask
static Operand EmbeddedNumber(double number)
constexpr void set(RegisterT reg)
bool IsRootHandle(IndirectHandle< T > handle, RootIndex *index) const
Definition roots-inl.h:65
static constexpr Tagged< Smi > FromInt(int value)
Definition smi.h:38
static constexpr int32_t TypeToMarker(Type type)
Definition frames.h:196
static constexpr int kFrameTypeOffset
void Include(const Register &reg1, const Register &reg2=no_reg)
T * New(Args &&... args)
Definition zone.h:114
void MoveToTempLocation(InstructionOperand *src, MachineRepresentation rep) final
void AssembleTailCallAfterGap(Instruction *instr, int first_unused_stack_slot)
void AssembleReturn(InstructionOperand *pop)
void AssembleTailCallBeforeGap(Instruction *instr, int first_unused_stack_slot)
FrameAccessState * frame_access_state() const
CodeGenResult AssembleArchInstruction(Instruction *instr)
DeoptimizationExit * BuildTranslation(Instruction *instr, int pc_offset, size_t frame_state_offset, size_t immediate_args_count, OutputFrameStateCombine state_combine)
void AssembleArchBinarySearchSwitch(Instruction *instr)
void AssembleArchJumpRegardlessOfAssemblyOrder(RpoNumber target)
void AssembleArchBoolean(Instruction *instr, FlagsCondition condition)
void AssembleJumpTable(base::Vector< Label * > targets)
void AssembleArchBranch(Instruction *instr, BranchInfo *branch)
void AssembleMove(InstructionOperand *source, InstructionOperand *destination) final
void SetPendingMove(MoveOperands *move) final
bool ShouldApplyOffsetToStackCheck(Instruction *instr, uint32_t *offset)
void RecordSafepoint(ReferenceMap *references, int pc_offset=0)
void AssembleArchBinarySearchSwitchRange(Register input, RpoNumber def_block, std::pair< int32_t, Label * > *begin, std::pair< int32_t, Label * > *end)
void PrepareForDeoptimizationExits(ZoneDeque< DeoptimizationExit * > *exits)
void AssembleArchTableSwitch(Instruction *instr)
bool IsMaterializableFromRoot(Handle< HeapObject > object, RootIndex *index_return)
void AssembleArchConditionalBranch(Instruction *instr, BranchInfo *branch)
AllocatedOperand Push(InstructionOperand *src) final
void MoveTempLocationTo(InstructionOperand *dst, MachineRepresentation rep) final
void AssembleArchDeoptBranch(Instruction *instr, BranchInfo *branch)
void RecordCallPosition(Instruction *instr)
void AssembleSwap(InstructionOperand *source, InstructionOperand *destination) final
void AssembleArchConditionalBoolean(Instruction *instr)
void RecordDeoptInfo(Instruction *instr, int pc_offset)
OptimizedCompilationInfo * info() const
void AssembleArchSelect(Instruction *instr, FlagsCondition condition)
void Pop(InstructionOperand *src, MachineRepresentation rep) final
FrameOffset GetFrameOffset(int spill_slot) const
Definition frame.cc:61
DoubleRegister ToDoubleRegister(InstructionOperand *op)
Constant ToConstant(InstructionOperand *op) const
Register ToRegister(InstructionOperand *op) const
const InstructionOperand * OutputAt(size_t i) const
InstructionCode opcode() const
const InstructionOperand * InputAt(size_t i) const
CallDescriptor * GetIncomingDescriptor() const
Definition linkage.h:405
MachineRepresentation representation() const
static LocationOperand * cast(InstructionOperand *op)
Loong64OperandConverter(CodeGenerator *gen, Instruction *instr)
MemOperand ToMemOperand(InstructionOperand *op) const
FloatRegister ToSingleRegister(InstructionOperand *op)
static OutputFrameStateCombine Ignore()
IndirectPointerTag indirect_pointer_tag_
#define ATOMIC_BINOP_CASE(op, inst)
#define ASSEMBLE_ATOMIC_LOAD_INTEGER(asm_instr)
bool must_save_lr_
#define ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER(load_instr, store_instr, cmp_reg)
Zone * zone_
#define ASSEMBLE_IEEE754_UNOP(name)
Register const object_
Operand const offset_
#define ASSEMBLE_IEEE754_BINOP(name)
Register const value_
#define ASSEMBLE_ATOMIC_STORE_INTEGER(asm_instr, order)
#define ASSEMBLE_ATOMIC_BINOP(load_instr, store_instr, bin_instr)
RecordWriteMode const mode_
#define ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT( load_linked, store_conditional, sign_extend, size, representation)
#define ASSEMBLE_ATOMIC_BINOP_EXT(load_linked, store_conditional, sign_extend, size, bin_instr, representation)
#define UNSUPPORTED_COND(opcode, condition)
#define CREATE_OOL_CLASS(ool_name, masm_ool_name, T)
#define ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER_EXT( load_linked, store_conditional, sign_extend, size, representation)
#define COMPRESS_POINTERS_BOOL
Definition globals.h:99
#define V8_JS_LINKAGE_INCLUDES_DISPATCH_HANDLE_BOOL
Definition globals.h:161
int32_t offset
Instruction * instr
ZoneVector< RpoNumber > & result
#define TRACE(...)
Builtin builtin
LiftoffRegister reg
int pc_offset
LiftoffRegList regs_to_save
SetIsolateDataSlots
InstructionOperand destination
v8::SourceLocation SourceLocation
unsigned short uint16_t
Definition unicode.cc:39
V8_INLINE Dest bit_cast(Source const &source)
Definition macros.h:95
constexpr T ByteSwap(T value)
uintptr_t Address
Definition memory.h:13
void SignExtend(MacroAssembler *masm, Instruction *instr, Register *left, Operand *right, Register *temp0, Register *temp1)
static bool HasRegisterInput(Instruction *instr, size_t index)
void AssembleBranchToLabels(CodeGenerator *gen, MacroAssembler *masm, Instruction *instr, FlagsCondition condition, Label *tlabel, Label *flabel, bool fallthru)
void Or(LiftoffAssembler *lasm, Register dst, Register lhs, Register rhs)
void Xor(LiftoffAssembler *lasm, Register dst, Register lhs, Register rhs)
void And(LiftoffAssembler *lasm, Register dst, Register lhs, Register rhs)
constexpr Register kGpParamRegisters[]
constexpr DoubleRegister kFpParamRegisters[]
constexpr Register kGpReturnRegisters[]
constexpr Register no_reg
constexpr Register kRootRegister
RegListBase< DoubleRegister > DoubleRegList
Definition reglist-arm.h:15
V8_EXPORT_PRIVATE constexpr int ElementSizeInPointers(MachineRepresentation rep)
DwVfpRegister DoubleRegister
const uint32_t kFCSRInvalidOpCauseMask
void PrintF(const char *format,...)
Definition utils.cc:39
constexpr DoubleRegister kScratchDoubleReg
RegListBase< Register > RegList
Definition reglist-arm.h:14
constexpr FPUControlRegister FCSR2
V8_INLINE constexpr bool IsValidIndirectPointerTag(IndirectPointerTag tag)
Address Tagged_t
Definition globals.h:547
constexpr int kSystemPointerSizeLog2
Definition globals.h:494
too high values may cause the compiler to set high thresholds for inlining to as much as possible avoid inlined allocation of objects that cannot escape trace load stores from virtual maglev objects use TurboFan fast string builder analyze liveness of environment slots and zap dead values trace TurboFan load elimination emit data about basic block usage in builtins to this enable builtin reordering when run mksnapshot flag for emit warnings when applying builtin profile data verify register allocation in TurboFan randomly schedule instructions to stress dependency tracking enable store store elimination in TurboFan rewrite far to near simulate GC compiler thread race related to allow float parameters to be passed in simulator mode JS Wasm Run additional turbo_optimize_inlined_js_wasm_wrappers enable experimental feedback collection in generic lowering enable Turboshaft s WasmLoadElimination enable Turboshaft s low level load elimination for JS enable Turboshaft s escape analysis for string concatenation use enable Turbolev features that we want to ship in the not too far future trace individual Turboshaft reduction steps trace intermediate Turboshaft reduction steps invocation count threshold for early optimization Enables optimizations which favor memory size over execution speed Enables sampling allocation profiler with X as a sample interval min size of a semi the new space consists of two semi spaces max size of the Collect garbage after Collect garbage after keeps maps alive for< n > old space garbage collections print one detailed trace line in allocation gc speed threshold for starting incremental marking via a task in percent of available threshold for starting incremental marking immediately in percent of available Use a single schedule for determining a marking schedule between JS and C objects schedules the minor GC task with kUserVisible priority max worker number of concurrent for NumberOfWorkerThreads start background threads that allocate memory concurrent_array_buffer_sweeping use parallel threads to clear weak refs in the atomic pause trace progress of the incremental marking trace object counts and memory usage report a tick only when allocated zone memory changes by this amount TracingFlags::gc_stats TracingFlags::gc_stats track native contexts that are expected to be garbage collected verify heap pointers before and after GC memory reducer runs GC with ReduceMemoryFootprint flag Maximum number of memory reducer GCs scheduled Old gen GC speed is computed directly from gc tracer counters Perform compaction on full GCs based on V8 s default heuristics Perform compaction on every full GC Perform code space compaction when finalizing a full GC with stack Stress GC compaction to flush out bugs with moving objects flush of baseline code when it has not been executed recently Use time base code flushing instead of age Use a progress bar to scan large objects in increments when incremental marking is active force incremental marking for small heaps and run it more often force marking at random points between and force scavenge at random points between and reclaim otherwise unreachable unmodified wrapper objects when possible less compaction in non memory reducing mode use high priority threads for concurrent Marking Test mode only flag It allows an unit test to select evacuation candidates use incremental marking for CppHeap cppheap_concurrent_marking c value for membalancer A special constant to balance between memory and space tradeoff The smaller the more memory it uses enable use of SSE4 instructions if available enable use of AVX VNNI instructions if available enable use of POPCNT instruction if available force all emitted branches to be in long mode(MIPS/PPC only)") DEFINE_BOOL(partial_constant_pool
constexpr bool IsAnyTagged(MachineRepresentation rep)
constexpr bool IsAnyCompressed(MachineRepresentation rep)
MemOperand FieldMemOperand(Register object, int offset)
constexpr int kSystemPointerSize
Definition globals.h:410
constexpr bool IsFloatingPoint(MachineRepresentation rep)
constexpr Register kReturnRegister0
const uint32_t kFCSROverflowCauseMask
constexpr Register kWasmImplicitArgRegister
constexpr LowDwVfpRegister kDoubleRegZero
V8_EXPORT_PRIVATE FlagValues v8_flags
constexpr Register kJavaScriptCallCodeStartRegister
return value
Definition map-inl.h:893
constexpr Register cp
const int kNumCalleeSavedFPU
constexpr Register kCArgRegs[]
constexpr int kDoubleSize
Definition globals.h:407
constexpr Register kJavaScriptCallDispatchHandleRegister
static int FrameSlotToFPOffset(int slot)
SwVfpRegister FloatRegister
BodyGen *const gen_
BodyGen * gen
ro::BitSet tagged_slots
#define CHECK(condition)
Definition logging.h:124
#define DCHECK_NOT_NULL(val)
Definition logging.h:492
#define DCHECK_IMPLIES(v1, v2)
Definition logging.h:493
#define DCHECK_NE(v1, v2)
Definition logging.h:486
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
constexpr bool IsAligned(T value, U alignment)
Definition macros.h:403
std::optional< CPURegister > scratch_reg
std::optional< DoubleRegister > scratch_fpreg
std::optional< UseScratchRegisterScope > temps
#define V8_STATIC_ROOTS_BOOL
Definition v8config.h:1001