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
5 #include "src/codegen/assembler-inl.h"
6 #include "src/codegen/callable.h"
7 #include "src/codegen/loong64/constants-loong64.h"
8 #include "src/codegen/macro-assembler.h"
9 #include "src/codegen/optimized-compilation-info.h"
10 #include "src/compiler/backend/code-generator-impl.h"
11 #include "src/compiler/backend/code-generator.h"
12 #include "src/compiler/backend/gap-resolver.h"
13 #include "src/compiler/node-matchers.h"
14 #include "src/compiler/osr.h"
15 #include "src/heap/memory-chunk.h"
16
17 #if V8_ENABLE_WEBASSEMBLY
18 #include "src/wasm/wasm-code-manager.h"
19 #endif // V8_ENABLE_WEBASSEMBLY
20
21 namespace v8 {
22 namespace internal {
23 namespace compiler {
24
25 #define __ tasm()->
26
27 // TODO(LOONG_dev): consider renaming these macros.
28 #define TRACE_MSG(msg) \
29 PrintF("code_gen: \'%s\' in function %s at line %d\n", msg, __FUNCTION__, \
30 __LINE__)
31
32 #define TRACE_UNIMPL() \
33 PrintF("UNIMPLEMENTED code_generator_loong64: %s at line %d\n", \
34 __FUNCTION__, __LINE__)
35
36 // Adds Loong64-specific methods to convert InstructionOperands.
37 class Loong64OperandConverter final : public InstructionOperandConverter {
38 public:
Loong64OperandConverter(CodeGenerator * gen,Instruction * instr)39 Loong64OperandConverter(CodeGenerator* gen, Instruction* instr)
40 : InstructionOperandConverter(gen, instr) {}
41
OutputSingleRegister(size_t index=0)42 FloatRegister OutputSingleRegister(size_t index = 0) {
43 return ToSingleRegister(instr_->OutputAt(index));
44 }
45
InputSingleRegister(size_t index)46 FloatRegister InputSingleRegister(size_t index) {
47 return ToSingleRegister(instr_->InputAt(index));
48 }
49
ToSingleRegister(InstructionOperand * op)50 FloatRegister ToSingleRegister(InstructionOperand* op) {
51 // Single (Float) and Double register namespace is same on LOONG64,
52 // both are typedefs of FPURegister.
53 return ToDoubleRegister(op);
54 }
55
InputOrZeroRegister(size_t index)56 Register InputOrZeroRegister(size_t index) {
57 if (instr_->InputAt(index)->IsImmediate()) {
58 DCHECK_EQ(0, InputInt32(index));
59 return zero_reg;
60 }
61 return InputRegister(index);
62 }
63
InputOrZeroDoubleRegister(size_t index)64 DoubleRegister InputOrZeroDoubleRegister(size_t index) {
65 if (instr_->InputAt(index)->IsImmediate()) return kDoubleRegZero;
66
67 return InputDoubleRegister(index);
68 }
69
InputOrZeroSingleRegister(size_t index)70 DoubleRegister InputOrZeroSingleRegister(size_t index) {
71 if (instr_->InputAt(index)->IsImmediate()) return kDoubleRegZero;
72
73 return InputSingleRegister(index);
74 }
75
InputImmediate(size_t index)76 Operand InputImmediate(size_t index) {
77 Constant constant = ToConstant(instr_->InputAt(index));
78 switch (constant.type()) {
79 case Constant::kInt32:
80 return Operand(constant.ToInt32());
81 case Constant::kInt64:
82 return Operand(constant.ToInt64());
83 case Constant::kFloat32:
84 return Operand::EmbeddedNumber(constant.ToFloat32());
85 case Constant::kFloat64:
86 return Operand::EmbeddedNumber(constant.ToFloat64().value());
87 case Constant::kExternalReference:
88 case Constant::kCompressedHeapObject:
89 case Constant::kHeapObject:
90 break;
91 case Constant::kDelayedStringConstant:
92 return Operand::EmbeddedStringConstant(
93 constant.ToDelayedStringConstant());
94 case Constant::kRpoNumber:
95 UNREACHABLE(); // TODO(titzer): RPO immediates on loong64?
96 }
97 UNREACHABLE();
98 }
99
InputOperand(size_t index)100 Operand InputOperand(size_t index) {
101 InstructionOperand* op = instr_->InputAt(index);
102 if (op->IsRegister()) {
103 return Operand(ToRegister(op));
104 }
105 return InputImmediate(index);
106 }
107
MemoryOperand(size_t * first_index)108 MemOperand MemoryOperand(size_t* first_index) {
109 const size_t index = *first_index;
110 switch (AddressingModeField::decode(instr_->opcode())) {
111 case kMode_None:
112 break;
113 case kMode_Root:
114 *first_index += 1;
115 return MemOperand(kRootRegister, InputInt32(index));
116 case kMode_MRI:
117 *first_index += 2;
118 return MemOperand(InputRegister(index + 0), InputInt32(index + 1));
119 case kMode_MRR:
120 *first_index += 2;
121 return MemOperand(InputRegister(index + 0), InputRegister(index + 1));
122 }
123 UNREACHABLE();
124 }
125
MemoryOperand(size_t index=0)126 MemOperand MemoryOperand(size_t index = 0) { return MemoryOperand(&index); }
127
ToMemOperand(InstructionOperand * op) const128 MemOperand ToMemOperand(InstructionOperand* op) const {
129 DCHECK_NOT_NULL(op);
130 DCHECK(op->IsStackSlot() || op->IsFPStackSlot());
131 return SlotToMemOperand(AllocatedOperand::cast(op)->index());
132 }
133
SlotToMemOperand(int slot) const134 MemOperand SlotToMemOperand(int slot) const {
135 FrameOffset offset = frame_access_state()->GetFrameOffset(slot);
136 return MemOperand(offset.from_stack_pointer() ? sp : fp, offset.offset());
137 }
138 };
139
HasRegisterInput(Instruction * instr,size_t index)140 static inline bool HasRegisterInput(Instruction* instr, size_t index) {
141 return instr->InputAt(index)->IsRegister();
142 }
143
144 namespace {
145
146 class OutOfLineRecordWrite final : public OutOfLineCode {
147 public:
OutOfLineRecordWrite(CodeGenerator * gen,Register object,Operand offset,Register value,RecordWriteMode mode,StubCallMode stub_mode)148 OutOfLineRecordWrite(CodeGenerator* gen, Register object, Operand offset,
149 Register value, RecordWriteMode mode,
150 StubCallMode stub_mode)
151 : OutOfLineCode(gen),
152 object_(object),
153 offset_(offset),
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 }
162
Generate()163 void Generate() final {
164 __ CheckPageFlag(value_, MemoryChunk::kPointersToHereAreInterestingMask, eq,
165 exit());
166 RememberedSetAction const remembered_set_action =
167 mode_ > RecordWriteMode::kValueIsMap ||
168 FLAG_use_full_record_write_builtin
169 ? RememberedSetAction::kEmit
170 : RememberedSetAction::kOmit;
171 SaveFPRegsMode const save_fp_mode = frame()->DidAllocateDoubleRegisters()
172 ? SaveFPRegsMode::kSave
173 : SaveFPRegsMode::kIgnore;
174 if (must_save_lr_) {
175 // We need to save and restore ra if the frame was elided.
176 __ Push(ra);
177 }
178 if (mode_ == RecordWriteMode::kValueIsEphemeronKey) {
179 __ CallEphemeronKeyBarrier(object_, offset_, save_fp_mode);
180 #if V8_ENABLE_WEBASSEMBLY
181 } else if (stub_mode_ == StubCallMode::kCallWasmRuntimeStub) {
182 // A direct call to a wasm runtime stub defined in this module.
183 // Just encode the stub index. This will be patched when the code
184 // is added to the native module and copied into wasm code space.
185 __ CallRecordWriteStubSaveRegisters(object_, offset_,
186 remembered_set_action, save_fp_mode,
187 StubCallMode::kCallWasmRuntimeStub);
188 #endif // V8_ENABLE_WEBASSEMBLY
189 } else {
190 __ CallRecordWriteStubSaveRegisters(object_, offset_,
191 remembered_set_action, save_fp_mode);
192 }
193 if (must_save_lr_) {
194 __ Pop(ra);
195 }
196 }
197
198 private:
199 Register const object_;
200 Operand const offset_;
201 Register const value_;
202 RecordWriteMode const mode_;
203 #if V8_ENABLE_WEBASSEMBLY
204 StubCallMode const stub_mode_;
205 #endif // V8_ENABLE_WEBASSEMBLY
206 bool must_save_lr_;
207 Zone* zone_;
208 };
209
210 #define CREATE_OOL_CLASS(ool_name, tasm_ool_name, T) \
211 class ool_name final : public OutOfLineCode { \
212 public: \
213 ool_name(CodeGenerator* gen, T dst, T src1, T src2) \
214 : OutOfLineCode(gen), dst_(dst), src1_(src1), src2_(src2) {} \
215 \
216 void Generate() final { __ tasm_ool_name(dst_, src1_, src2_); } \
217 \
218 private: \
219 T const dst_; \
220 T const src1_; \
221 T const src2_; \
222 }
223
224 CREATE_OOL_CLASS(OutOfLineFloat32Max, Float32MaxOutOfLine, FPURegister);
225 CREATE_OOL_CLASS(OutOfLineFloat32Min, Float32MinOutOfLine, FPURegister);
226 CREATE_OOL_CLASS(OutOfLineFloat64Max, Float64MaxOutOfLine, FPURegister);
227 CREATE_OOL_CLASS(OutOfLineFloat64Min, Float64MinOutOfLine, FPURegister);
228
229 #undef CREATE_OOL_CLASS
230
FlagsConditionToConditionCmp(FlagsCondition condition)231 Condition FlagsConditionToConditionCmp(FlagsCondition condition) {
232 switch (condition) {
233 case kEqual:
234 return eq;
235 case kNotEqual:
236 return ne;
237 case kSignedLessThan:
238 return lt;
239 case kSignedGreaterThanOrEqual:
240 return ge;
241 case kSignedLessThanOrEqual:
242 return le;
243 case kSignedGreaterThan:
244 return gt;
245 case kUnsignedLessThan:
246 return lo;
247 case kUnsignedGreaterThanOrEqual:
248 return hs;
249 case kUnsignedLessThanOrEqual:
250 return ls;
251 case kUnsignedGreaterThan:
252 return hi;
253 case kUnorderedEqual:
254 case kUnorderedNotEqual:
255 break;
256 default:
257 break;
258 }
259 UNREACHABLE();
260 }
261
FlagsConditionToConditionTst(FlagsCondition condition)262 Condition FlagsConditionToConditionTst(FlagsCondition condition) {
263 switch (condition) {
264 case kNotEqual:
265 return ne;
266 case kEqual:
267 return eq;
268 default:
269 break;
270 }
271 UNREACHABLE();
272 }
273
FlagsConditionToConditionOvf(FlagsCondition condition)274 Condition FlagsConditionToConditionOvf(FlagsCondition condition) {
275 switch (condition) {
276 case kOverflow:
277 return ne;
278 case kNotOverflow:
279 return eq;
280 default:
281 break;
282 }
283 UNREACHABLE();
284 }
285
FlagsConditionToConditionCmpFPU(bool * predicate,FlagsCondition condition)286 FPUCondition FlagsConditionToConditionCmpFPU(bool* predicate,
287 FlagsCondition condition) {
288 switch (condition) {
289 case kEqual:
290 *predicate = true;
291 return CEQ;
292 case kNotEqual:
293 *predicate = false;
294 return CEQ;
295 case kUnsignedLessThan:
296 *predicate = true;
297 return CLT;
298 case kUnsignedGreaterThanOrEqual:
299 *predicate = false;
300 return CLT;
301 case kUnsignedLessThanOrEqual:
302 *predicate = true;
303 return CLE;
304 case kUnsignedGreaterThan:
305 *predicate = false;
306 return CLE;
307 case kUnorderedEqual:
308 case kUnorderedNotEqual:
309 *predicate = true;
310 break;
311 default:
312 *predicate = true;
313 break;
314 }
315 UNREACHABLE();
316 }
317
318 } // namespace
319
320 #define ASSEMBLE_ATOMIC_LOAD_INTEGER(asm_instr) \
321 do { \
322 __ asm_instr(i.OutputRegister(), i.MemoryOperand()); \
323 __ dbar(0); \
324 } while (0)
325
326 // TODO(LOONG_dev): remove second dbar?
327 #define ASSEMBLE_ATOMIC_STORE_INTEGER(asm_instr) \
328 do { \
329 __ dbar(0); \
330 __ asm_instr(i.InputOrZeroRegister(2), i.MemoryOperand()); \
331 __ dbar(0); \
332 } while (0)
333
334 // only use for sub_w and sub_d
335 #define ASSEMBLE_ATOMIC_BINOP(load_linked, store_conditional, bin_instr) \
336 do { \
337 Label binop; \
338 __ Add_d(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1)); \
339 __ dbar(0); \
340 __ bind(&binop); \
341 __ load_linked(i.OutputRegister(0), MemOperand(i.TempRegister(0), 0)); \
342 __ bin_instr(i.TempRegister(1), i.OutputRegister(0), \
343 Operand(i.InputRegister(2))); \
344 __ store_conditional(i.TempRegister(1), MemOperand(i.TempRegister(0), 0)); \
345 __ BranchShort(&binop, eq, i.TempRegister(1), Operand(zero_reg)); \
346 __ dbar(0); \
347 } while (0)
348
349 // TODO(LOONG_dev): remove second dbar?
350 #define ASSEMBLE_ATOMIC_BINOP_EXT(load_linked, store_conditional, sign_extend, \
351 size, bin_instr, representation) \
352 do { \
353 Label binop; \
354 __ add_d(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1)); \
355 if (representation == 32) { \
356 __ andi(i.TempRegister(3), i.TempRegister(0), 0x3); \
357 } else { \
358 DCHECK_EQ(representation, 64); \
359 __ andi(i.TempRegister(3), i.TempRegister(0), 0x7); \
360 } \
361 __ Sub_d(i.TempRegister(0), i.TempRegister(0), \
362 Operand(i.TempRegister(3))); \
363 __ slli_w(i.TempRegister(3), i.TempRegister(3), 3); \
364 __ dbar(0); \
365 __ bind(&binop); \
366 __ load_linked(i.TempRegister(1), MemOperand(i.TempRegister(0), 0)); \
367 __ ExtractBits(i.OutputRegister(0), i.TempRegister(1), i.TempRegister(3), \
368 size, sign_extend); \
369 __ bin_instr(i.TempRegister(2), i.OutputRegister(0), \
370 Operand(i.InputRegister(2))); \
371 __ InsertBits(i.TempRegister(1), i.TempRegister(2), i.TempRegister(3), \
372 size); \
373 __ store_conditional(i.TempRegister(1), MemOperand(i.TempRegister(0), 0)); \
374 __ BranchShort(&binop, eq, i.TempRegister(1), Operand(zero_reg)); \
375 __ dbar(0); \
376 } while (0)
377
378 // TODO(LOONG_dev): remove second dbar?
379 #define ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT( \
380 load_linked, store_conditional, sign_extend, size, representation) \
381 do { \
382 Label exchange; \
383 __ add_d(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1)); \
384 if (representation == 32) { \
385 __ andi(i.TempRegister(1), i.TempRegister(0), 0x3); \
386 } else { \
387 DCHECK_EQ(representation, 64); \
388 __ andi(i.TempRegister(1), i.TempRegister(0), 0x7); \
389 } \
390 __ Sub_d(i.TempRegister(0), i.TempRegister(0), \
391 Operand(i.TempRegister(1))); \
392 __ slli_w(i.TempRegister(1), i.TempRegister(1), 3); \
393 __ dbar(0); \
394 __ bind(&exchange); \
395 __ load_linked(i.TempRegister(2), MemOperand(i.TempRegister(0), 0)); \
396 __ ExtractBits(i.OutputRegister(0), i.TempRegister(2), i.TempRegister(1), \
397 size, sign_extend); \
398 __ InsertBits(i.TempRegister(2), i.InputRegister(2), i.TempRegister(1), \
399 size); \
400 __ store_conditional(i.TempRegister(2), MemOperand(i.TempRegister(0), 0)); \
401 __ BranchShort(&exchange, eq, i.TempRegister(2), Operand(zero_reg)); \
402 __ dbar(0); \
403 } while (0)
404
405 // TODO(LOONG_dev): remove second dbar?
406 #define ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER(load_linked, \
407 store_conditional) \
408 do { \
409 Label compareExchange; \
410 Label exit; \
411 __ add_d(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1)); \
412 __ dbar(0); \
413 __ bind(&compareExchange); \
414 __ load_linked(i.OutputRegister(0), MemOperand(i.TempRegister(0), 0)); \
415 __ BranchShort(&exit, ne, i.InputRegister(2), \
416 Operand(i.OutputRegister(0))); \
417 __ mov(i.TempRegister(2), i.InputRegister(3)); \
418 __ store_conditional(i.TempRegister(2), MemOperand(i.TempRegister(0), 0)); \
419 __ BranchShort(&compareExchange, eq, i.TempRegister(2), \
420 Operand(zero_reg)); \
421 __ bind(&exit); \
422 __ dbar(0); \
423 } while (0)
424
425 // TODO(LOONG_dev): remove second dbar?
426 #define ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER_EXT( \
427 load_linked, store_conditional, sign_extend, size, representation) \
428 do { \
429 Label compareExchange; \
430 Label exit; \
431 __ add_d(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1)); \
432 if (representation == 32) { \
433 __ andi(i.TempRegister(1), i.TempRegister(0), 0x3); \
434 } else { \
435 DCHECK_EQ(representation, 64); \
436 __ andi(i.TempRegister(1), i.TempRegister(0), 0x7); \
437 } \
438 __ Sub_d(i.TempRegister(0), i.TempRegister(0), \
439 Operand(i.TempRegister(1))); \
440 __ slli_w(i.TempRegister(1), i.TempRegister(1), 3); \
441 __ dbar(0); \
442 __ bind(&compareExchange); \
443 __ load_linked(i.TempRegister(2), MemOperand(i.TempRegister(0), 0)); \
444 __ ExtractBits(i.OutputRegister(0), i.TempRegister(2), i.TempRegister(1), \
445 size, sign_extend); \
446 __ ExtractBits(i.TempRegister(2), i.InputRegister(2), zero_reg, size, \
447 sign_extend); \
448 __ BranchShort(&exit, ne, i.TempRegister(2), \
449 Operand(i.OutputRegister(0))); \
450 __ InsertBits(i.TempRegister(2), i.InputRegister(3), i.TempRegister(1), \
451 size); \
452 __ store_conditional(i.TempRegister(2), MemOperand(i.TempRegister(0), 0)); \
453 __ BranchShort(&compareExchange, eq, i.TempRegister(2), \
454 Operand(zero_reg)); \
455 __ bind(&exit); \
456 __ dbar(0); \
457 } while (0)
458
459 #define ASSEMBLE_IEEE754_BINOP(name) \
460 do { \
461 FrameScope scope(tasm(), StackFrame::MANUAL); \
462 UseScratchRegisterScope temps(tasm()); \
463 Register scratch = temps.Acquire(); \
464 __ PrepareCallCFunction(0, 2, scratch); \
465 __ CallCFunction(ExternalReference::ieee754_##name##_function(), 0, 2); \
466 } while (0)
467
468 #define ASSEMBLE_IEEE754_UNOP(name) \
469 do { \
470 FrameScope scope(tasm(), StackFrame::MANUAL); \
471 UseScratchRegisterScope temps(tasm()); \
472 Register scratch = temps.Acquire(); \
473 __ PrepareCallCFunction(0, 1, scratch); \
474 __ CallCFunction(ExternalReference::ieee754_##name##_function(), 0, 1); \
475 } while (0)
476
477 #define ASSEMBLE_F64X2_ARITHMETIC_BINOP(op) \
478 do { \
479 __ op(i.OutputSimd128Register(), i.InputSimd128Register(0), \
480 i.InputSimd128Register(1)); \
481 } while (0)
482
AssembleDeconstructFrame()483 void CodeGenerator::AssembleDeconstructFrame() {
484 __ mov(sp, fp);
485 __ Pop(ra, fp);
486 }
487
AssemblePrepareTailCall()488 void CodeGenerator::AssemblePrepareTailCall() {
489 if (frame_access_state()->has_frame()) {
490 __ Ld_d(ra, MemOperand(fp, StandardFrameConstants::kCallerPCOffset));
491 __ Ld_d(fp, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
492 }
493 frame_access_state()->SetFrameAccessToSP();
494 }
495
496 namespace {
497
AdjustStackPointerForTailCall(TurboAssembler * tasm,FrameAccessState * state,int new_slot_above_sp,bool allow_shrinkage=true)498 void AdjustStackPointerForTailCall(TurboAssembler* tasm,
499 FrameAccessState* state,
500 int new_slot_above_sp,
501 bool allow_shrinkage = true) {
502 int current_sp_offset = state->GetSPToFPSlotCount() +
503 StandardFrameConstants::kFixedSlotCountAboveFp;
504 int stack_slot_delta = new_slot_above_sp - current_sp_offset;
505 if (stack_slot_delta > 0) {
506 tasm->Sub_d(sp, sp, stack_slot_delta * kSystemPointerSize);
507 state->IncreaseSPDelta(stack_slot_delta);
508 } else if (allow_shrinkage && stack_slot_delta < 0) {
509 tasm->Add_d(sp, sp, -stack_slot_delta * kSystemPointerSize);
510 state->IncreaseSPDelta(stack_slot_delta);
511 }
512 }
513
514 } // namespace
515
AssembleTailCallBeforeGap(Instruction * instr,int first_unused_slot_offset)516 void CodeGenerator::AssembleTailCallBeforeGap(Instruction* instr,
517 int first_unused_slot_offset) {
518 AdjustStackPointerForTailCall(tasm(), frame_access_state(),
519 first_unused_slot_offset, false);
520 }
521
AssembleTailCallAfterGap(Instruction * instr,int first_unused_slot_offset)522 void CodeGenerator::AssembleTailCallAfterGap(Instruction* instr,
523 int first_unused_slot_offset) {
524 AdjustStackPointerForTailCall(tasm(), frame_access_state(),
525 first_unused_slot_offset);
526 }
527
528 // Check that {kJavaScriptCallCodeStartRegister} is correct.
AssembleCodeStartRegisterCheck()529 void CodeGenerator::AssembleCodeStartRegisterCheck() {
530 UseScratchRegisterScope temps(tasm());
531 Register scratch = temps.Acquire();
532 __ ComputeCodeStartAddress(scratch);
533 __ Assert(eq, AbortReason::kWrongFunctionCodeStart,
534 kJavaScriptCallCodeStartRegister, Operand(scratch));
535 }
536
537 // Check if the code object is marked for deoptimization. If it is, then it
538 // jumps to the CompileLazyDeoptimizedCode builtin. In order to do this we need
539 // to:
540 // 1. read from memory the word that contains that bit, which can be found in
541 // the flags in the referenced {CodeDataContainer} object;
542 // 2. test kMarkedForDeoptimizationBit in those flags; and
543 // 3. if it is not zero then it jumps to the builtin.
BailoutIfDeoptimized()544 void CodeGenerator::BailoutIfDeoptimized() {
545 UseScratchRegisterScope temps(tasm());
546 Register scratch = temps.Acquire();
547 int offset = Code::kCodeDataContainerOffset - Code::kHeaderSize;
548 __ Ld_d(scratch, MemOperand(kJavaScriptCallCodeStartRegister, offset));
549 __ Ld_w(scratch, FieldMemOperand(
550 scratch, CodeDataContainer::kKindSpecificFlagsOffset));
551 __ And(scratch, scratch, Operand(1 << Code::kMarkedForDeoptimizationBit));
552 __ Jump(BUILTIN_CODE(isolate(), CompileLazyDeoptimizedCode),
553 RelocInfo::CODE_TARGET, ne, scratch, Operand(zero_reg));
554 }
555
556 // Assembles an instruction after register allocation, producing machine code.
AssembleArchInstruction(Instruction * instr)557 CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction(
558 Instruction* instr) {
559 Loong64OperandConverter i(this, instr);
560 InstructionCode opcode = instr->opcode();
561 ArchOpcode arch_opcode = ArchOpcodeField::decode(opcode);
562 switch (arch_opcode) {
563 case kArchCallCodeObject: {
564 if (instr->InputAt(0)->IsImmediate()) {
565 __ Call(i.InputCode(0), RelocInfo::CODE_TARGET);
566 } else {
567 Register reg = i.InputRegister(0);
568 DCHECK_IMPLIES(
569 instr->HasCallDescriptorFlag(CallDescriptor::kFixedTargetRegister),
570 reg == kJavaScriptCallCodeStartRegister);
571 __ CallCodeObject(reg);
572 }
573 RecordCallPosition(instr);
574 frame_access_state()->ClearSPDelta();
575 break;
576 }
577 case kArchCallBuiltinPointer: {
578 DCHECK(!instr->InputAt(0)->IsImmediate());
579 Register builtin_index = i.InputRegister(0);
580 __ CallBuiltinByIndex(builtin_index);
581 RecordCallPosition(instr);
582 frame_access_state()->ClearSPDelta();
583 break;
584 }
585 #if V8_ENABLE_WEBASSEMBLY
586 case kArchCallWasmFunction: {
587 if (instr->InputAt(0)->IsImmediate()) {
588 Constant constant = i.ToConstant(instr->InputAt(0));
589 Address wasm_code = static_cast<Address>(constant.ToInt64());
590 __ Call(wasm_code, constant.rmode());
591 } else {
592 __ Call(i.InputRegister(0));
593 }
594 RecordCallPosition(instr);
595 frame_access_state()->ClearSPDelta();
596 break;
597 }
598 case kArchTailCallWasm: {
599 if (instr->InputAt(0)->IsImmediate()) {
600 Constant constant = i.ToConstant(instr->InputAt(0));
601 Address wasm_code = static_cast<Address>(constant.ToInt64());
602 __ Jump(wasm_code, constant.rmode());
603 } else {
604 __ Jump(i.InputRegister(0));
605 }
606 frame_access_state()->ClearSPDelta();
607 frame_access_state()->SetFrameAccessToDefault();
608 break;
609 }
610 #endif // V8_ENABLE_WEBASSEMBLY
611 case kArchTailCallCodeObject: {
612 if (instr->InputAt(0)->IsImmediate()) {
613 __ Jump(i.InputCode(0), RelocInfo::CODE_TARGET);
614 } else {
615 Register reg = i.InputRegister(0);
616 DCHECK_IMPLIES(
617 instr->HasCallDescriptorFlag(CallDescriptor::kFixedTargetRegister),
618 reg == kJavaScriptCallCodeStartRegister);
619 __ JumpCodeObject(reg);
620 }
621 frame_access_state()->ClearSPDelta();
622 frame_access_state()->SetFrameAccessToDefault();
623 break;
624 }
625 case kArchTailCallAddress: {
626 CHECK(!instr->InputAt(0)->IsImmediate());
627 Register reg = i.InputRegister(0);
628 DCHECK_IMPLIES(
629 instr->HasCallDescriptorFlag(CallDescriptor::kFixedTargetRegister),
630 reg == kJavaScriptCallCodeStartRegister);
631 __ Jump(reg);
632 frame_access_state()->ClearSPDelta();
633 frame_access_state()->SetFrameAccessToDefault();
634 break;
635 }
636 case kArchCallJSFunction: {
637 Register func = i.InputRegister(0);
638 if (FLAG_debug_code) {
639 UseScratchRegisterScope temps(tasm());
640 Register scratch = temps.Acquire();
641 // Check the function's context matches the context argument.
642 __ Ld_d(scratch, FieldMemOperand(func, JSFunction::kContextOffset));
643 __ Assert(eq, AbortReason::kWrongFunctionContext, cp, Operand(scratch));
644 }
645 static_assert(kJavaScriptCallCodeStartRegister == a2, "ABI mismatch");
646 __ Ld_d(a2, FieldMemOperand(func, JSFunction::kCodeOffset));
647 __ CallCodeObject(a2);
648 RecordCallPosition(instr);
649 frame_access_state()->ClearSPDelta();
650 break;
651 }
652 case kArchPrepareCallCFunction: {
653 UseScratchRegisterScope temps(tasm());
654 Register scratch = temps.Acquire();
655 int const num_parameters = MiscField::decode(instr->opcode());
656 __ PrepareCallCFunction(num_parameters, scratch);
657 // Frame alignment requires using FP-relative frame addressing.
658 frame_access_state()->SetFrameAccessToFP();
659 break;
660 }
661 case kArchSaveCallerRegisters: {
662 fp_mode_ =
663 static_cast<SaveFPRegsMode>(MiscField::decode(instr->opcode()));
664 DCHECK(fp_mode_ == SaveFPRegsMode::kIgnore ||
665 fp_mode_ == SaveFPRegsMode::kSave);
666 // kReturnRegister0 should have been saved before entering the stub.
667 int bytes = __ PushCallerSaved(fp_mode_, kReturnRegister0);
668 DCHECK(IsAligned(bytes, kSystemPointerSize));
669 DCHECK_EQ(0, frame_access_state()->sp_delta());
670 frame_access_state()->IncreaseSPDelta(bytes / kSystemPointerSize);
671 DCHECK(!caller_registers_saved_);
672 caller_registers_saved_ = true;
673 break;
674 }
675 case kArchRestoreCallerRegisters: {
676 DCHECK(fp_mode_ ==
677 static_cast<SaveFPRegsMode>(MiscField::decode(instr->opcode())));
678 DCHECK(fp_mode_ == SaveFPRegsMode::kIgnore ||
679 fp_mode_ == SaveFPRegsMode::kSave);
680 // Don't overwrite the returned value.
681 int bytes = __ PopCallerSaved(fp_mode_, kReturnRegister0);
682 frame_access_state()->IncreaseSPDelta(-(bytes / kSystemPointerSize));
683 DCHECK_EQ(0, frame_access_state()->sp_delta());
684 DCHECK(caller_registers_saved_);
685 caller_registers_saved_ = false;
686 break;
687 }
688 case kArchPrepareTailCall:
689 AssemblePrepareTailCall();
690 break;
691 case kArchCallCFunction: {
692 int const num_parameters = MiscField::decode(instr->opcode());
693 #if V8_ENABLE_WEBASSEMBLY
694 Label start_call;
695 bool isWasmCapiFunction =
696 linkage()->GetIncomingDescriptor()->IsWasmCapiFunction();
697 // from start_call to return address.
698 int offset = __ root_array_available() ? 36 : 80; // 9 or 20 instrs
699 #endif // V8_ENABLE_WEBASSEMBLY
700 #if V8_HOST_ARCH_LOONG64
701 if (FLAG_debug_code) {
702 offset += 12; // see CallCFunction
703 }
704 #endif
705 #if V8_ENABLE_WEBASSEMBLY
706 if (isWasmCapiFunction) {
707 __ bind(&start_call);
708 __ pcaddi(t7, offset >> kInstrSizeLog2);
709 __ St_d(t7, MemOperand(fp, WasmExitFrameConstants::kCallingPCOffset));
710 }
711 #endif // V8_ENABLE_WEBASSEMBLY
712 if (instr->InputAt(0)->IsImmediate()) {
713 ExternalReference ref = i.InputExternalReference(0);
714 __ CallCFunction(ref, num_parameters);
715 } else {
716 Register func = i.InputRegister(0);
717 __ CallCFunction(func, num_parameters);
718 }
719 #if V8_ENABLE_WEBASSEMBLY
720 if (isWasmCapiFunction) {
721 CHECK_EQ(offset, __ SizeOfCodeGeneratedSince(&start_call));
722 RecordSafepoint(instr->reference_map());
723 }
724 #endif // V8_ENABLE_WEBASSEMBLY
725 frame_access_state()->SetFrameAccessToDefault();
726 // Ideally, we should decrement SP delta to match the change of stack
727 // pointer in CallCFunction. However, for certain architectures (e.g.
728 // ARM), there may be more strict alignment requirement, causing old SP
729 // to be saved on the stack. In those cases, we can not calculate the SP
730 // delta statically.
731 frame_access_state()->ClearSPDelta();
732 if (caller_registers_saved_) {
733 // Need to re-sync SP delta introduced in kArchSaveCallerRegisters.
734 // Here, we assume the sequence to be:
735 // kArchSaveCallerRegisters;
736 // kArchCallCFunction;
737 // kArchRestoreCallerRegisters;
738 int bytes =
739 __ RequiredStackSizeForCallerSaved(fp_mode_, kReturnRegister0);
740 frame_access_state()->IncreaseSPDelta(bytes / kSystemPointerSize);
741 }
742 break;
743 }
744 case kArchJmp:
745 AssembleArchJump(i.InputRpo(0));
746 break;
747 case kArchBinarySearchSwitch:
748 AssembleArchBinarySearchSwitch(instr);
749 break;
750 case kArchTableSwitch:
751 AssembleArchTableSwitch(instr);
752 break;
753 case kArchAbortCSADcheck:
754 DCHECK(i.InputRegister(0) == a0);
755 {
756 // We don't actually want to generate a pile of code for this, so just
757 // claim there is a stack frame, without generating one.
758 FrameScope scope(tasm(), StackFrame::NO_FRAME_TYPE);
759 __ Call(isolate()->builtins()->code_handle(Builtin::kAbortCSADcheck),
760 RelocInfo::CODE_TARGET);
761 }
762 __ stop();
763 break;
764 case kArchDebugBreak:
765 __ DebugBreak();
766 break;
767 case kArchComment:
768 __ RecordComment(reinterpret_cast<const char*>(i.InputInt64(0)));
769 break;
770 case kArchNop:
771 case kArchThrowTerminator:
772 // don't emit code for nops.
773 break;
774 case kArchDeoptimize: {
775 DeoptimizationExit* exit =
776 BuildTranslation(instr, -1, 0, 0, OutputFrameStateCombine::Ignore());
777 __ Branch(exit->label());
778 break;
779 }
780 case kArchRet:
781 AssembleReturn(instr->InputAt(0));
782 break;
783 case kArchStackPointerGreaterThan: {
784 Register lhs_register = sp;
785 uint32_t offset;
786 if (ShouldApplyOffsetToStackCheck(instr, &offset)) {
787 lhs_register = i.TempRegister(1);
788 __ Sub_d(lhs_register, sp, offset);
789 }
790 __ Sltu(i.TempRegister(0), i.InputRegister(0), lhs_register);
791 break;
792 }
793 case kArchStackCheckOffset:
794 __ Move(i.OutputRegister(), Smi::FromInt(GetStackCheckOffset()));
795 break;
796 case kArchFramePointer:
797 __ mov(i.OutputRegister(), fp);
798 break;
799 case kArchParentFramePointer:
800 if (frame_access_state()->has_frame()) {
801 __ Ld_d(i.OutputRegister(), MemOperand(fp, 0));
802 } else {
803 __ mov(i.OutputRegister(), fp);
804 }
805 break;
806 case kArchTruncateDoubleToI:
807 __ TruncateDoubleToI(isolate(), zone(), i.OutputRegister(),
808 i.InputDoubleRegister(0), DetermineStubCallMode());
809 break;
810 case kArchStoreWithWriteBarrier: // Fall through.
811 case kArchAtomicStoreWithWriteBarrier: {
812 RecordWriteMode mode =
813 static_cast<RecordWriteMode>(MiscField::decode(instr->opcode()));
814 AddressingMode addressing_mode =
815 AddressingModeField::decode(instr->opcode());
816 Register object = i.InputRegister(0);
817 Operand offset(zero_reg);
818 if (addressing_mode == kMode_MRI) {
819 offset = Operand(i.InputInt64(1));
820 } else {
821 DCHECK_EQ(addressing_mode, kMode_MRR);
822 offset = Operand(i.InputRegister(1));
823 }
824 Register value = i.InputRegister(2);
825
826 auto ool = zone()->New<OutOfLineRecordWrite>(
827 this, object, offset, value, mode, DetermineStubCallMode());
828 if (arch_opcode == kArchStoreWithWriteBarrier) {
829 if (addressing_mode == kMode_MRI) {
830 __ St_d(value, MemOperand(object, i.InputInt64(1)));
831 } else {
832 DCHECK_EQ(addressing_mode, kMode_MRR);
833 __ St_d(value, MemOperand(object, i.InputRegister(1)));
834 }
835 } else {
836 DCHECK_EQ(kArchAtomicStoreWithWriteBarrier, arch_opcode);
837 DCHECK_EQ(addressing_mode, kMode_MRI);
838 UseScratchRegisterScope temps(tasm());
839 Register scratch = temps.Acquire();
840 __ Add_d(scratch, object, Operand(i.InputInt64(1)));
841 __ amswap_db_d(zero_reg, value, scratch);
842 }
843 if (mode > RecordWriteMode::kValueIsPointer) {
844 __ JumpIfSmi(value, ool->exit());
845 }
846 __ CheckPageFlag(object, MemoryChunk::kPointersFromHereAreInterestingMask,
847 ne, ool->entry());
848 __ bind(ool->exit());
849 break;
850 }
851 case kArchStackSlot: {
852 UseScratchRegisterScope temps(tasm());
853 Register scratch = temps.Acquire();
854 FrameOffset offset =
855 frame_access_state()->GetFrameOffset(i.InputInt32(0));
856 Register base_reg = offset.from_stack_pointer() ? sp : fp;
857 __ Add_d(i.OutputRegister(), base_reg, Operand(offset.offset()));
858 if (FLAG_debug_code) {
859 // Verify that the output_register is properly aligned
860 __ And(scratch, i.OutputRegister(), Operand(kSystemPointerSize - 1));
861 __ Assert(eq, AbortReason::kAllocationIsNotDoubleAligned, scratch,
862 Operand(zero_reg));
863 }
864 break;
865 }
866 case kIeee754Float64Acos:
867 ASSEMBLE_IEEE754_UNOP(acos);
868 break;
869 case kIeee754Float64Acosh:
870 ASSEMBLE_IEEE754_UNOP(acosh);
871 break;
872 case kIeee754Float64Asin:
873 ASSEMBLE_IEEE754_UNOP(asin);
874 break;
875 case kIeee754Float64Asinh:
876 ASSEMBLE_IEEE754_UNOP(asinh);
877 break;
878 case kIeee754Float64Atan:
879 ASSEMBLE_IEEE754_UNOP(atan);
880 break;
881 case kIeee754Float64Atanh:
882 ASSEMBLE_IEEE754_UNOP(atanh);
883 break;
884 case kIeee754Float64Atan2:
885 ASSEMBLE_IEEE754_BINOP(atan2);
886 break;
887 case kIeee754Float64Cos:
888 ASSEMBLE_IEEE754_UNOP(cos);
889 break;
890 case kIeee754Float64Cosh:
891 ASSEMBLE_IEEE754_UNOP(cosh);
892 break;
893 case kIeee754Float64Cbrt:
894 ASSEMBLE_IEEE754_UNOP(cbrt);
895 break;
896 case kIeee754Float64Exp:
897 ASSEMBLE_IEEE754_UNOP(exp);
898 break;
899 case kIeee754Float64Expm1:
900 ASSEMBLE_IEEE754_UNOP(expm1);
901 break;
902 case kIeee754Float64Log:
903 ASSEMBLE_IEEE754_UNOP(log);
904 break;
905 case kIeee754Float64Log1p:
906 ASSEMBLE_IEEE754_UNOP(log1p);
907 break;
908 case kIeee754Float64Log2:
909 ASSEMBLE_IEEE754_UNOP(log2);
910 break;
911 case kIeee754Float64Log10:
912 ASSEMBLE_IEEE754_UNOP(log10);
913 break;
914 case kIeee754Float64Pow:
915 ASSEMBLE_IEEE754_BINOP(pow);
916 break;
917 case kIeee754Float64Sin:
918 ASSEMBLE_IEEE754_UNOP(sin);
919 break;
920 case kIeee754Float64Sinh:
921 ASSEMBLE_IEEE754_UNOP(sinh);
922 break;
923 case kIeee754Float64Tan:
924 ASSEMBLE_IEEE754_UNOP(tan);
925 break;
926 case kIeee754Float64Tanh:
927 ASSEMBLE_IEEE754_UNOP(tanh);
928 break;
929 case kLoong64Add_w:
930 __ Add_w(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
931 break;
932 case kLoong64Add_d:
933 __ Add_d(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
934 break;
935 case kLoong64AddOvf_d:
936 __ AddOverflow_d(i.OutputRegister(), i.InputRegister(0),
937 i.InputOperand(1), t8);
938 break;
939 case kLoong64Sub_w:
940 __ Sub_w(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
941 break;
942 case kLoong64Sub_d:
943 __ Sub_d(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
944 break;
945 case kLoong64SubOvf_d:
946 __ SubOverflow_d(i.OutputRegister(), i.InputRegister(0),
947 i.InputOperand(1), t8);
948 break;
949 case kLoong64Mul_w:
950 __ Mul_w(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
951 break;
952 case kLoong64MulOvf_w:
953 __ MulOverflow_w(i.OutputRegister(), i.InputRegister(0),
954 i.InputOperand(1), t8);
955 break;
956 case kLoong64Mulh_w:
957 __ Mulh_w(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
958 break;
959 case kLoong64Mulh_wu:
960 __ Mulh_wu(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
961 break;
962 case kLoong64Mulh_d:
963 __ Mulh_d(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
964 break;
965 case kLoong64Div_w:
966 __ Div_w(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
967 __ maskeqz(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1));
968 break;
969 case kLoong64Div_wu:
970 __ Div_wu(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
971 __ maskeqz(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1));
972 break;
973 case kLoong64Mod_w:
974 __ Mod_w(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
975 break;
976 case kLoong64Mod_wu:
977 __ Mod_wu(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
978 break;
979 case kLoong64Mul_d:
980 __ Mul_d(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
981 break;
982 case kLoong64Div_d:
983 __ Div_d(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
984 __ maskeqz(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1));
985 break;
986 case kLoong64Div_du:
987 __ Div_du(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
988 __ maskeqz(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1));
989 break;
990 case kLoong64Mod_d:
991 __ Mod_d(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
992 break;
993 case kLoong64Mod_du:
994 __ Mod_du(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
995 break;
996 case kLoong64Alsl_d:
997 DCHECK(instr->InputAt(2)->IsImmediate());
998 __ Alsl_d(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1),
999 i.InputInt8(2), t7);
1000 break;
1001 case kLoong64Alsl_w:
1002 DCHECK(instr->InputAt(2)->IsImmediate());
1003 __ Alsl_w(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1),
1004 i.InputInt8(2), t7);
1005 break;
1006 case kLoong64And:
1007 case kLoong64And32:
1008 __ And(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
1009 break;
1010 case kLoong64Or:
1011 case kLoong64Or32:
1012 __ Or(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
1013 break;
1014 case kLoong64Nor:
1015 case kLoong64Nor32:
1016 if (instr->InputAt(1)->IsRegister()) {
1017 __ Nor(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
1018 } else {
1019 DCHECK_EQ(0, i.InputOperand(1).immediate());
1020 __ Nor(i.OutputRegister(), i.InputRegister(0), zero_reg);
1021 }
1022 break;
1023 case kLoong64Xor:
1024 case kLoong64Xor32:
1025 __ Xor(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
1026 break;
1027 case kLoong64Clz_w:
1028 __ clz_w(i.OutputRegister(), i.InputRegister(0));
1029 break;
1030 case kLoong64Clz_d:
1031 __ clz_d(i.OutputRegister(), i.InputRegister(0));
1032 break;
1033 case kLoong64Sll_w:
1034 if (instr->InputAt(1)->IsRegister()) {
1035 __ sll_w(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1));
1036 } else {
1037 int64_t imm = i.InputOperand(1).immediate();
1038 __ slli_w(i.OutputRegister(), i.InputRegister(0),
1039 static_cast<uint16_t>(imm));
1040 }
1041 break;
1042 case kLoong64Srl_w:
1043 if (instr->InputAt(1)->IsRegister()) {
1044 __ srl_w(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1));
1045 } else {
1046 int64_t imm = i.InputOperand(1).immediate();
1047 __ srli_w(i.OutputRegister(), i.InputRegister(0),
1048 static_cast<uint16_t>(imm));
1049 }
1050 break;
1051 case kLoong64Sra_w:
1052 if (instr->InputAt(1)->IsRegister()) {
1053 __ sra_w(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1));
1054 } else {
1055 int64_t imm = i.InputOperand(1).immediate();
1056 __ srai_w(i.OutputRegister(), i.InputRegister(0),
1057 static_cast<uint16_t>(imm));
1058 }
1059 break;
1060 case kLoong64Bstrpick_w:
1061 __ bstrpick_w(i.OutputRegister(), i.InputRegister(0),
1062 i.InputInt8(1) + i.InputInt8(2) - 1, i.InputInt8(1));
1063 break;
1064 case kLoong64Bstrins_w:
1065 if (instr->InputAt(1)->IsImmediate() && i.InputInt8(1) == 0) {
1066 __ bstrins_w(i.OutputRegister(), zero_reg,
1067 i.InputInt8(1) + i.InputInt8(2) - 1, i.InputInt8(1));
1068 } else {
1069 __ bstrins_w(i.OutputRegister(), i.InputRegister(0),
1070 i.InputInt8(1) + i.InputInt8(2) - 1, i.InputInt8(1));
1071 }
1072 break;
1073 case kLoong64Bstrpick_d: {
1074 __ bstrpick_d(i.OutputRegister(), i.InputRegister(0),
1075 i.InputInt8(1) + i.InputInt8(2) - 1, i.InputInt8(1));
1076 break;
1077 }
1078 case kLoong64Bstrins_d:
1079 if (instr->InputAt(1)->IsImmediate() && i.InputInt8(1) == 0) {
1080 __ bstrins_d(i.OutputRegister(), zero_reg,
1081 i.InputInt8(1) + i.InputInt8(2) - 1, i.InputInt8(1));
1082 } else {
1083 __ bstrins_d(i.OutputRegister(), i.InputRegister(0),
1084 i.InputInt8(1) + i.InputInt8(2) - 1, i.InputInt8(1));
1085 }
1086 break;
1087 case kLoong64Sll_d:
1088 if (instr->InputAt(1)->IsRegister()) {
1089 __ sll_d(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1));
1090 } else {
1091 int64_t imm = i.InputOperand(1).immediate();
1092 __ slli_d(i.OutputRegister(), i.InputRegister(0),
1093 static_cast<uint16_t>(imm));
1094 }
1095 break;
1096 case kLoong64Srl_d:
1097 if (instr->InputAt(1)->IsRegister()) {
1098 __ srl_d(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1));
1099 } else {
1100 int64_t imm = i.InputOperand(1).immediate();
1101 __ srli_d(i.OutputRegister(), i.InputRegister(0),
1102 static_cast<uint16_t>(imm));
1103 }
1104 break;
1105 case kLoong64Sra_d:
1106 if (instr->InputAt(1)->IsRegister()) {
1107 __ sra_d(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1));
1108 } else {
1109 int64_t imm = i.InputOperand(1).immediate();
1110 __ srai_d(i.OutputRegister(), i.InputRegister(0), imm);
1111 }
1112 break;
1113 case kLoong64Rotr_w:
1114 __ Rotr_w(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
1115 break;
1116 case kLoong64Rotr_d:
1117 __ Rotr_d(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
1118 break;
1119 case kLoong64Tst:
1120 __ And(t8, i.InputRegister(0), i.InputOperand(1));
1121 // Pseudo-instruction used for cmp/branch. No opcode emitted here.
1122 break;
1123 case kLoong64Cmp:
1124 // Pseudo-instruction used for cmp/branch. No opcode emitted here.
1125 break;
1126 case kLoong64Mov:
1127 // TODO(LOONG_dev): Should we combine mov/li, or use separate instr?
1128 // - Also see x64 ASSEMBLE_BINOP & RegisterOrOperandType
1129 if (HasRegisterInput(instr, 0)) {
1130 __ mov(i.OutputRegister(), i.InputRegister(0));
1131 } else {
1132 __ li(i.OutputRegister(), i.InputOperand(0));
1133 }
1134 break;
1135
1136 case kLoong64Float32Cmp: {
1137 FPURegister left = i.InputOrZeroSingleRegister(0);
1138 FPURegister right = i.InputOrZeroSingleRegister(1);
1139 bool predicate;
1140 FPUCondition cc =
1141 FlagsConditionToConditionCmpFPU(&predicate, instr->flags_condition());
1142
1143 if ((left == kDoubleRegZero || right == kDoubleRegZero) &&
1144 !__ IsDoubleZeroRegSet()) {
1145 __ Move(kDoubleRegZero, 0.0);
1146 }
1147
1148 __ CompareF32(left, right, cc);
1149 } break;
1150 case kLoong64Float32Add:
1151 __ fadd_s(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
1152 i.InputDoubleRegister(1));
1153 break;
1154 case kLoong64Float32Sub:
1155 __ fsub_s(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
1156 i.InputDoubleRegister(1));
1157 break;
1158 case kLoong64Float32Mul:
1159 __ fmul_s(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
1160 i.InputDoubleRegister(1));
1161 break;
1162 case kLoong64Float32Div:
1163 __ fdiv_s(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
1164 i.InputDoubleRegister(1));
1165 break;
1166 case kLoong64Float32Abs:
1167 __ fabs_s(i.OutputSingleRegister(), i.InputSingleRegister(0));
1168 break;
1169 case kLoong64Float32Neg:
1170 __ Neg_s(i.OutputSingleRegister(), i.InputSingleRegister(0));
1171 break;
1172 case kLoong64Float32Sqrt: {
1173 __ fsqrt_s(i.OutputDoubleRegister(), i.InputDoubleRegister(0));
1174 break;
1175 }
1176 case kLoong64Float32Min: {
1177 FPURegister dst = i.OutputSingleRegister();
1178 FPURegister src1 = i.InputSingleRegister(0);
1179 FPURegister src2 = i.InputSingleRegister(1);
1180 auto ool = zone()->New<OutOfLineFloat32Min>(this, dst, src1, src2);
1181 __ Float32Min(dst, src1, src2, ool->entry());
1182 __ bind(ool->exit());
1183 break;
1184 }
1185 case kLoong64Float32Max: {
1186 FPURegister dst = i.OutputSingleRegister();
1187 FPURegister src1 = i.InputSingleRegister(0);
1188 FPURegister src2 = i.InputSingleRegister(1);
1189 auto ool = zone()->New<OutOfLineFloat32Max>(this, dst, src1, src2);
1190 __ Float32Max(dst, src1, src2, ool->entry());
1191 __ bind(ool->exit());
1192 break;
1193 }
1194 case kLoong64Float64Cmp: {
1195 FPURegister left = i.InputOrZeroDoubleRegister(0);
1196 FPURegister right = i.InputOrZeroDoubleRegister(1);
1197 bool predicate;
1198 FPUCondition cc =
1199 FlagsConditionToConditionCmpFPU(&predicate, instr->flags_condition());
1200 if ((left == kDoubleRegZero || right == kDoubleRegZero) &&
1201 !__ IsDoubleZeroRegSet()) {
1202 __ Move(kDoubleRegZero, 0.0);
1203 }
1204
1205 __ CompareF64(left, right, cc);
1206 } break;
1207 case kLoong64Float64Add:
1208 __ fadd_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
1209 i.InputDoubleRegister(1));
1210 break;
1211 case kLoong64Float64Sub:
1212 __ fsub_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
1213 i.InputDoubleRegister(1));
1214 break;
1215 case kLoong64Float64Mul:
1216 // TODO(LOONG_dev): LOONG64 add special case: right op is -1.0, see arm
1217 // port.
1218 __ fmul_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
1219 i.InputDoubleRegister(1));
1220 break;
1221 case kLoong64Float64Div:
1222 __ fdiv_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
1223 i.InputDoubleRegister(1));
1224 break;
1225 case kLoong64Float64Mod: {
1226 // TODO(turbofan): implement directly.
1227 FrameScope scope(tasm(), StackFrame::MANUAL);
1228 UseScratchRegisterScope temps(tasm());
1229 Register scratch = temps.Acquire();
1230 __ PrepareCallCFunction(0, 2, scratch);
1231 __ CallCFunction(ExternalReference::mod_two_doubles_operation(), 0, 2);
1232 break;
1233 }
1234 case kLoong64Float64Abs:
1235 __ fabs_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0));
1236 break;
1237 case kLoong64Float64Neg:
1238 __ Neg_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0));
1239 break;
1240 case kLoong64Float64Sqrt: {
1241 __ fsqrt_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0));
1242 break;
1243 }
1244 case kLoong64Float64Min: {
1245 FPURegister dst = i.OutputDoubleRegister();
1246 FPURegister src1 = i.InputDoubleRegister(0);
1247 FPURegister src2 = i.InputDoubleRegister(1);
1248 auto ool = zone()->New<OutOfLineFloat64Min>(this, dst, src1, src2);
1249 __ Float64Min(dst, src1, src2, ool->entry());
1250 __ bind(ool->exit());
1251 break;
1252 }
1253 case kLoong64Float64Max: {
1254 FPURegister dst = i.OutputDoubleRegister();
1255 FPURegister src1 = i.InputDoubleRegister(0);
1256 FPURegister src2 = i.InputDoubleRegister(1);
1257 auto ool = zone()->New<OutOfLineFloat64Max>(this, dst, src1, src2);
1258 __ Float64Max(dst, src1, src2, ool->entry());
1259 __ bind(ool->exit());
1260 break;
1261 }
1262 case kLoong64Float64RoundDown: {
1263 __ Floor_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0));
1264 break;
1265 }
1266 case kLoong64Float32RoundDown: {
1267 __ Floor_s(i.OutputSingleRegister(), i.InputSingleRegister(0));
1268 break;
1269 }
1270 case kLoong64Float64RoundTruncate: {
1271 __ Trunc_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0));
1272 break;
1273 }
1274 case kLoong64Float32RoundTruncate: {
1275 __ Trunc_s(i.OutputSingleRegister(), i.InputSingleRegister(0));
1276 break;
1277 }
1278 case kLoong64Float64RoundUp: {
1279 __ Ceil_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0));
1280 break;
1281 }
1282 case kLoong64Float32RoundUp: {
1283 __ Ceil_s(i.OutputSingleRegister(), i.InputSingleRegister(0));
1284 break;
1285 }
1286 case kLoong64Float64RoundTiesEven: {
1287 __ Round_d(i.OutputDoubleRegister(), i.InputDoubleRegister(0));
1288 break;
1289 }
1290 case kLoong64Float32RoundTiesEven: {
1291 __ Round_s(i.OutputSingleRegister(), i.InputSingleRegister(0));
1292 break;
1293 }
1294 case kLoong64Float64SilenceNaN:
1295 __ FPUCanonicalizeNaN(i.OutputDoubleRegister(), i.InputDoubleRegister(0));
1296 break;
1297 case kLoong64Float64ToFloat32:
1298 __ fcvt_s_d(i.OutputSingleRegister(), i.InputDoubleRegister(0));
1299 break;
1300 case kLoong64Float32ToFloat64:
1301 __ fcvt_d_s(i.OutputDoubleRegister(), i.InputSingleRegister(0));
1302 break;
1303 case kLoong64Int32ToFloat64: {
1304 FPURegister scratch = kScratchDoubleReg;
1305 __ movgr2fr_w(scratch, i.InputRegister(0));
1306 __ ffint_d_w(i.OutputDoubleRegister(), scratch);
1307 break;
1308 }
1309 case kLoong64Int32ToFloat32: {
1310 FPURegister scratch = kScratchDoubleReg;
1311 __ movgr2fr_w(scratch, i.InputRegister(0));
1312 __ ffint_s_w(i.OutputDoubleRegister(), scratch);
1313 break;
1314 }
1315 case kLoong64Uint32ToFloat32: {
1316 __ Ffint_s_uw(i.OutputDoubleRegister(), i.InputRegister(0));
1317 break;
1318 }
1319 case kLoong64Int64ToFloat32: {
1320 FPURegister scratch = kScratchDoubleReg;
1321 __ movgr2fr_d(scratch, i.InputRegister(0));
1322 __ ffint_s_l(i.OutputDoubleRegister(), scratch);
1323 break;
1324 }
1325 case kLoong64Int64ToFloat64: {
1326 FPURegister scratch = kScratchDoubleReg;
1327 __ movgr2fr_d(scratch, i.InputRegister(0));
1328 __ ffint_d_l(i.OutputDoubleRegister(), scratch);
1329 break;
1330 }
1331 case kLoong64Uint32ToFloat64: {
1332 __ Ffint_d_uw(i.OutputDoubleRegister(), i.InputRegister(0));
1333 break;
1334 }
1335 case kLoong64Uint64ToFloat64: {
1336 __ Ffint_d_ul(i.OutputDoubleRegister(), i.InputRegister(0));
1337 break;
1338 }
1339 case kLoong64Uint64ToFloat32: {
1340 __ Ffint_s_ul(i.OutputDoubleRegister(), i.InputRegister(0));
1341 break;
1342 }
1343 case kLoong64Float64ToInt32: {
1344 FPURegister scratch = kScratchDoubleReg;
1345 __ ftintrz_w_d(scratch, i.InputDoubleRegister(0));
1346 __ movfr2gr_s(i.OutputRegister(), scratch);
1347 break;
1348 }
1349 case kLoong64Float32ToInt32: {
1350 FPURegister scratch_d = kScratchDoubleReg;
1351 bool set_overflow_to_min_i32 = MiscField::decode(instr->opcode());
1352 __ ftintrz_w_s(scratch_d, i.InputDoubleRegister(0));
1353 __ movfr2gr_s(i.OutputRegister(), scratch_d);
1354 if (set_overflow_to_min_i32) {
1355 UseScratchRegisterScope temps(tasm());
1356 Register scratch = temps.Acquire();
1357 // Avoid INT32_MAX as an overflow indicator and use INT32_MIN instead,
1358 // because INT32_MIN allows easier out-of-bounds detection.
1359 __ addi_w(scratch, i.OutputRegister(), 1);
1360 __ slt(scratch, scratch, i.OutputRegister());
1361 __ add_w(i.OutputRegister(), i.OutputRegister(), scratch);
1362 }
1363 break;
1364 }
1365 case kLoong64Float32ToInt64: {
1366 FPURegister scratch_d = kScratchDoubleReg;
1367
1368 bool load_status = instr->OutputCount() > 1;
1369 // Other arches use round to zero here, so we follow.
1370 __ ftintrz_l_s(scratch_d, i.InputDoubleRegister(0));
1371 __ movfr2gr_d(i.OutputRegister(), scratch_d);
1372 if (load_status) {
1373 Register output2 = i.OutputRegister(1);
1374 __ movfcsr2gr(output2, FCSR2);
1375 // Check for overflow and NaNs.
1376 __ And(output2, output2,
1377 kFCSROverflowCauseMask | kFCSRInvalidOpCauseMask);
1378 __ Slt(output2, zero_reg, output2);
1379 __ xori(output2, output2, 1);
1380 }
1381 break;
1382 }
1383 case kLoong64Float64ToInt64: {
1384 UseScratchRegisterScope temps(tasm());
1385 Register scratch = temps.Acquire();
1386 FPURegister scratch_d = kScratchDoubleReg;
1387
1388 bool set_overflow_to_min_i64 = MiscField::decode(instr->opcode());
1389 bool load_status = instr->OutputCount() > 1;
1390 // Other arches use round to zero here, so we follow.
1391 __ ftintrz_l_d(scratch_d, i.InputDoubleRegister(0));
1392 __ movfr2gr_d(i.OutputRegister(0), scratch_d);
1393 if (load_status) {
1394 Register output2 = i.OutputRegister(1);
1395 __ movfcsr2gr(output2, FCSR2);
1396 // Check for overflow and NaNs.
1397 __ And(output2, output2,
1398 kFCSROverflowCauseMask | kFCSRInvalidOpCauseMask);
1399 __ Slt(output2, zero_reg, output2);
1400 __ xori(output2, output2, 1);
1401 }
1402 if (set_overflow_to_min_i64) {
1403 // Avoid INT64_MAX as an overflow indicator and use INT64_MIN instead,
1404 // because INT64_MIN allows easier out-of-bounds detection.
1405 __ addi_d(scratch, i.OutputRegister(), 1);
1406 __ slt(scratch, scratch, i.OutputRegister());
1407 __ add_d(i.OutputRegister(), i.OutputRegister(), scratch);
1408 }
1409 break;
1410 }
1411 case kLoong64Float64ToUint32: {
1412 FPURegister scratch = kScratchDoubleReg;
1413 __ Ftintrz_uw_d(i.OutputRegister(), i.InputDoubleRegister(0), scratch);
1414 break;
1415 }
1416 case kLoong64Float32ToUint32: {
1417 FPURegister scratch = kScratchDoubleReg;
1418 bool set_overflow_to_min_i32 = MiscField::decode(instr->opcode());
1419 __ Ftintrz_uw_s(i.OutputRegister(), i.InputDoubleRegister(0), scratch);
1420 if (set_overflow_to_min_i32) {
1421 UseScratchRegisterScope temps(tasm());
1422 Register scratch = temps.Acquire();
1423 // Avoid UINT32_MAX as an overflow indicator and use 0 instead,
1424 // because 0 allows easier out-of-bounds detection.
1425 __ addi_w(scratch, i.OutputRegister(), 1);
1426 __ Movz(i.OutputRegister(), zero_reg, scratch);
1427 }
1428 break;
1429 }
1430 case kLoong64Float32ToUint64: {
1431 FPURegister scratch = kScratchDoubleReg;
1432 Register result = instr->OutputCount() > 1 ? i.OutputRegister(1) : no_reg;
1433 __ Ftintrz_ul_s(i.OutputRegister(), i.InputDoubleRegister(0), scratch,
1434 result);
1435 break;
1436 }
1437 case kLoong64Float64ToUint64: {
1438 FPURegister scratch = kScratchDoubleReg;
1439 Register result = instr->OutputCount() > 1 ? i.OutputRegister(1) : no_reg;
1440 __ Ftintrz_ul_d(i.OutputRegister(0), i.InputDoubleRegister(0), scratch,
1441 result);
1442 break;
1443 }
1444 case kLoong64BitcastDL:
1445 __ movfr2gr_d(i.OutputRegister(), i.InputDoubleRegister(0));
1446 break;
1447 case kLoong64BitcastLD:
1448 __ movgr2fr_d(i.OutputDoubleRegister(), i.InputRegister(0));
1449 break;
1450 case kLoong64Float64ExtractLowWord32:
1451 __ FmoveLow(i.OutputRegister(), i.InputDoubleRegister(0));
1452 break;
1453 case kLoong64Float64ExtractHighWord32:
1454 __ movfrh2gr_s(i.OutputRegister(), i.InputDoubleRegister(0));
1455 break;
1456 case kLoong64Float64InsertLowWord32:
1457 __ FmoveLow(i.OutputDoubleRegister(), i.InputRegister(1));
1458 break;
1459 case kLoong64Float64InsertHighWord32:
1460 __ movgr2frh_w(i.OutputDoubleRegister(), i.InputRegister(1));
1461 break;
1462 // ... more basic instructions ...
1463
1464 case kLoong64Ext_w_b:
1465 __ ext_w_b(i.OutputRegister(), i.InputRegister(0));
1466 break;
1467 case kLoong64Ext_w_h:
1468 __ ext_w_h(i.OutputRegister(), i.InputRegister(0));
1469 break;
1470 case kLoong64Ld_bu:
1471 __ Ld_bu(i.OutputRegister(), i.MemoryOperand());
1472 break;
1473 case kLoong64Ld_b:
1474 __ Ld_b(i.OutputRegister(), i.MemoryOperand());
1475 break;
1476 case kLoong64St_b:
1477 __ St_b(i.InputOrZeroRegister(2), i.MemoryOperand());
1478 break;
1479 case kLoong64Ld_hu:
1480 __ Ld_hu(i.OutputRegister(), i.MemoryOperand());
1481 break;
1482 case kLoong64Ld_h:
1483 __ Ld_h(i.OutputRegister(), i.MemoryOperand());
1484 break;
1485 case kLoong64St_h:
1486 __ St_h(i.InputOrZeroRegister(2), i.MemoryOperand());
1487 break;
1488 case kLoong64Ld_w:
1489 __ Ld_w(i.OutputRegister(), i.MemoryOperand());
1490 break;
1491 case kLoong64Ld_wu:
1492 __ Ld_wu(i.OutputRegister(), i.MemoryOperand());
1493 break;
1494 case kLoong64Ld_d:
1495 __ Ld_d(i.OutputRegister(), i.MemoryOperand());
1496 break;
1497 case kLoong64St_w:
1498 __ St_w(i.InputOrZeroRegister(2), i.MemoryOperand());
1499 break;
1500 case kLoong64St_d:
1501 __ St_d(i.InputOrZeroRegister(2), i.MemoryOperand());
1502 break;
1503 case kLoong64Fld_s: {
1504 __ Fld_s(i.OutputSingleRegister(), i.MemoryOperand());
1505 break;
1506 }
1507 case kLoong64Fst_s: {
1508 size_t index = 0;
1509 MemOperand operand = i.MemoryOperand(&index);
1510 FPURegister ft = i.InputOrZeroSingleRegister(index);
1511 if (ft == kDoubleRegZero && !__ IsDoubleZeroRegSet()) {
1512 __ Move(kDoubleRegZero, 0.0);
1513 }
1514
1515 __ Fst_s(ft, operand);
1516 break;
1517 }
1518 case kLoong64Fld_d:
1519 __ Fld_d(i.OutputDoubleRegister(), i.MemoryOperand());
1520 break;
1521 case kLoong64Fst_d: {
1522 FPURegister ft = i.InputOrZeroDoubleRegister(2);
1523 if (ft == kDoubleRegZero && !__ IsDoubleZeroRegSet()) {
1524 __ Move(kDoubleRegZero, 0.0);
1525 }
1526
1527 __ Fst_d(ft, i.MemoryOperand());
1528 break;
1529 }
1530 case kLoong64Dbar: {
1531 __ dbar(0);
1532 break;
1533 }
1534 case kLoong64Push:
1535 if (instr->InputAt(0)->IsFPRegister()) {
1536 __ Fst_d(i.InputDoubleRegister(0), MemOperand(sp, -kDoubleSize));
1537 __ Sub_d(sp, sp, Operand(kDoubleSize));
1538 frame_access_state()->IncreaseSPDelta(kDoubleSize / kSystemPointerSize);
1539 } else {
1540 __ Push(i.InputRegister(0));
1541 frame_access_state()->IncreaseSPDelta(1);
1542 }
1543 break;
1544 case kLoong64Peek: {
1545 int reverse_slot = i.InputInt32(0);
1546 int offset =
1547 FrameSlotToFPOffset(frame()->GetTotalFrameSlotCount() - reverse_slot);
1548 if (instr->OutputAt(0)->IsFPRegister()) {
1549 LocationOperand* op = LocationOperand::cast(instr->OutputAt(0));
1550 if (op->representation() == MachineRepresentation::kFloat64) {
1551 __ Fld_d(i.OutputDoubleRegister(), MemOperand(fp, offset));
1552 } else if (op->representation() == MachineRepresentation::kFloat32) {
1553 __ Fld_s(i.OutputSingleRegister(0), MemOperand(fp, offset));
1554 } else {
1555 DCHECK_EQ(MachineRepresentation::kSimd128, op->representation());
1556 abort();
1557 }
1558 } else {
1559 __ Ld_d(i.OutputRegister(0), MemOperand(fp, offset));
1560 }
1561 break;
1562 }
1563 case kLoong64StackClaim: {
1564 __ Sub_d(sp, sp, Operand(i.InputInt32(0)));
1565 frame_access_state()->IncreaseSPDelta(i.InputInt32(0) /
1566 kSystemPointerSize);
1567 break;
1568 }
1569 case kLoong64Poke: {
1570 if (instr->InputAt(0)->IsFPRegister()) {
1571 __ Fst_d(i.InputDoubleRegister(0), MemOperand(sp, i.InputInt32(1)));
1572 } else {
1573 __ St_d(i.InputRegister(0), MemOperand(sp, i.InputInt32(1)));
1574 }
1575 break;
1576 }
1577 case kLoong64ByteSwap64: {
1578 __ ByteSwapSigned(i.OutputRegister(0), i.InputRegister(0), 8);
1579 break;
1580 }
1581 case kLoong64ByteSwap32: {
1582 __ ByteSwapSigned(i.OutputRegister(0), i.InputRegister(0), 4);
1583 break;
1584 }
1585 case kAtomicLoadInt8:
1586 DCHECK_EQ(AtomicWidthField::decode(opcode), AtomicWidth::kWord32);
1587 ASSEMBLE_ATOMIC_LOAD_INTEGER(Ld_b);
1588 break;
1589 case kAtomicLoadUint8:
1590 ASSEMBLE_ATOMIC_LOAD_INTEGER(Ld_bu);
1591 break;
1592 case kAtomicLoadInt16:
1593 DCHECK_EQ(AtomicWidthField::decode(opcode), AtomicWidth::kWord32);
1594 ASSEMBLE_ATOMIC_LOAD_INTEGER(Ld_h);
1595 break;
1596 case kAtomicLoadUint16:
1597 ASSEMBLE_ATOMIC_LOAD_INTEGER(Ld_hu);
1598 break;
1599 case kAtomicLoadWord32:
1600 ASSEMBLE_ATOMIC_LOAD_INTEGER(Ld_w);
1601 break;
1602 case kLoong64Word64AtomicLoadUint32:
1603 ASSEMBLE_ATOMIC_LOAD_INTEGER(Ld_wu);
1604 break;
1605 case kLoong64Word64AtomicLoadUint64:
1606 ASSEMBLE_ATOMIC_LOAD_INTEGER(Ld_d);
1607 break;
1608 case kAtomicStoreWord8:
1609 ASSEMBLE_ATOMIC_STORE_INTEGER(St_b);
1610 break;
1611 case kAtomicStoreWord16:
1612 ASSEMBLE_ATOMIC_STORE_INTEGER(St_h);
1613 break;
1614 case kAtomicStoreWord32:
1615 ASSEMBLE_ATOMIC_STORE_INTEGER(St_w);
1616 break;
1617 case kLoong64StoreCompressTagged:
1618 case kLoong64Word64AtomicStoreWord64:
1619 ASSEMBLE_ATOMIC_STORE_INTEGER(St_d);
1620 break;
1621 case kAtomicExchangeInt8:
1622 DCHECK_EQ(AtomicWidthField::decode(opcode), AtomicWidth::kWord32);
1623 ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT(Ll_w, Sc_w, true, 8, 32);
1624 break;
1625 case kAtomicExchangeUint8:
1626 switch (AtomicWidthField::decode(opcode)) {
1627 case AtomicWidth::kWord32:
1628 ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT(Ll_w, Sc_w, false, 8, 32);
1629 break;
1630 case AtomicWidth::kWord64:
1631 ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT(Ll_d, Sc_d, false, 8, 64);
1632 break;
1633 }
1634 break;
1635 case kAtomicExchangeInt16:
1636 DCHECK_EQ(AtomicWidthField::decode(opcode), AtomicWidth::kWord32);
1637 ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT(Ll_w, Sc_w, true, 16, 32);
1638 break;
1639 case kAtomicExchangeUint16:
1640 switch (AtomicWidthField::decode(opcode)) {
1641 case AtomicWidth::kWord32:
1642 ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT(Ll_w, Sc_w, false, 16, 32);
1643 break;
1644 case AtomicWidth::kWord64:
1645 ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT(Ll_d, Sc_d, false, 16, 64);
1646 break;
1647 }
1648 break;
1649 case kAtomicExchangeWord32:
1650 switch (AtomicWidthField::decode(opcode)) {
1651 case AtomicWidth::kWord32:
1652 __ add_d(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1));
1653 __ amswap_db_w(i.OutputRegister(0), i.InputRegister(2),
1654 i.TempRegister(0));
1655 break;
1656 case AtomicWidth::kWord64:
1657 ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT(Ll_d, Sc_d, false, 32, 64);
1658 break;
1659 }
1660 break;
1661 case kLoong64Word64AtomicExchangeUint64:
1662 __ add_d(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1));
1663 __ amswap_db_d(i.OutputRegister(0), i.InputRegister(2),
1664 i.TempRegister(0));
1665 break;
1666 case kAtomicCompareExchangeInt8:
1667 DCHECK_EQ(AtomicWidthField::decode(opcode), AtomicWidth::kWord32);
1668 ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER_EXT(Ll_w, Sc_w, true, 8, 32);
1669 break;
1670 case kAtomicCompareExchangeUint8:
1671 switch (AtomicWidthField::decode(opcode)) {
1672 case AtomicWidth::kWord32:
1673 ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER_EXT(Ll_w, Sc_w, false, 8,
1674 32);
1675 break;
1676 case AtomicWidth::kWord64:
1677 ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER_EXT(Ll_d, Sc_d, false, 8,
1678 64);
1679 break;
1680 }
1681 break;
1682 case kAtomicCompareExchangeInt16:
1683 DCHECK_EQ(AtomicWidthField::decode(opcode), AtomicWidth::kWord32);
1684 ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER_EXT(Ll_w, Sc_w, true, 16, 32);
1685 break;
1686 case kAtomicCompareExchangeUint16:
1687 switch (AtomicWidthField::decode(opcode)) {
1688 case AtomicWidth::kWord32:
1689 ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER_EXT(Ll_w, Sc_w, false, 16,
1690 32);
1691 break;
1692 case AtomicWidth::kWord64:
1693 ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER_EXT(Ll_d, Sc_d, false, 16,
1694 64);
1695 break;
1696 }
1697 break;
1698 case kAtomicCompareExchangeWord32:
1699 switch (AtomicWidthField::decode(opcode)) {
1700 case AtomicWidth::kWord32:
1701 __ slli_w(i.InputRegister(2), i.InputRegister(2), 0);
1702 ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER(Ll_w, Sc_w);
1703 break;
1704 case AtomicWidth::kWord64:
1705 ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER_EXT(Ll_d, Sc_d, false, 32,
1706 64);
1707 break;
1708 }
1709 break;
1710 case kLoong64Word64AtomicCompareExchangeUint64:
1711 ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER(Ll_d, Sc_d);
1712 break;
1713 case kAtomicAddWord32:
1714 switch (AtomicWidthField::decode(opcode)) {
1715 case AtomicWidth::kWord32:
1716 __ Add_d(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1));
1717 __ amadd_db_w(i.OutputRegister(0), i.InputRegister(2),
1718 i.TempRegister(0));
1719 break;
1720 case AtomicWidth::kWord64:
1721 ASSEMBLE_ATOMIC_BINOP_EXT(Ll_d, Sc_d, false, 32, Add_d, 64);
1722 break;
1723 }
1724 break;
1725 case kAtomicSubWord32:
1726 switch (AtomicWidthField::decode(opcode)) {
1727 case AtomicWidth::kWord32:
1728 ASSEMBLE_ATOMIC_BINOP(Ll_w, Sc_w, Sub_w);
1729 break;
1730 case AtomicWidth::kWord64:
1731 ASSEMBLE_ATOMIC_BINOP_EXT(Ll_d, Sc_d, false, 32, Sub_d, 64);
1732 break;
1733 }
1734 break;
1735 case kAtomicAndWord32:
1736 switch (AtomicWidthField::decode(opcode)) {
1737 case AtomicWidth::kWord32:
1738 __ Add_d(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1));
1739 __ amand_db_w(i.OutputRegister(0), i.InputRegister(2),
1740 i.TempRegister(0));
1741 break;
1742 case AtomicWidth::kWord64:
1743 ASSEMBLE_ATOMIC_BINOP_EXT(Ll_d, Sc_d, false, 32, And, 64);
1744 break;
1745 }
1746 break;
1747 case kAtomicOrWord32:
1748 switch (AtomicWidthField::decode(opcode)) {
1749 case AtomicWidth::kWord32:
1750 __ Add_d(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1));
1751 __ amor_db_w(i.OutputRegister(0), i.InputRegister(2),
1752 i.TempRegister(0));
1753 break;
1754 case AtomicWidth::kWord64:
1755 ASSEMBLE_ATOMIC_BINOP_EXT(Ll_d, Sc_d, false, 32, Or, 64);
1756 break;
1757 }
1758 break;
1759 case kAtomicXorWord32:
1760 switch (AtomicWidthField::decode(opcode)) {
1761 case AtomicWidth::kWord32:
1762 __ Add_d(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1));
1763 __ amxor_db_w(i.OutputRegister(0), i.InputRegister(2),
1764 i.TempRegister(0));
1765 break;
1766 case AtomicWidth::kWord64:
1767 ASSEMBLE_ATOMIC_BINOP_EXT(Ll_d, Sc_d, false, 32, Xor, 64);
1768 break;
1769 }
1770 break;
1771 #define ATOMIC_BINOP_CASE(op, inst32, inst64) \
1772 case kAtomic##op##Int8: \
1773 DCHECK_EQ(AtomicWidthField::decode(opcode), AtomicWidth::kWord32); \
1774 ASSEMBLE_ATOMIC_BINOP_EXT(Ll_w, Sc_w, true, 8, inst32, 32); \
1775 break; \
1776 case kAtomic##op##Uint8: \
1777 switch (AtomicWidthField::decode(opcode)) { \
1778 case AtomicWidth::kWord32: \
1779 ASSEMBLE_ATOMIC_BINOP_EXT(Ll_w, Sc_w, false, 8, inst32, 32); \
1780 break; \
1781 case AtomicWidth::kWord64: \
1782 ASSEMBLE_ATOMIC_BINOP_EXT(Ll_d, Sc_d, false, 8, inst64, 64); \
1783 break; \
1784 } \
1785 break; \
1786 case kAtomic##op##Int16: \
1787 DCHECK_EQ(AtomicWidthField::decode(opcode), AtomicWidth::kWord32); \
1788 ASSEMBLE_ATOMIC_BINOP_EXT(Ll_w, Sc_w, true, 16, inst32, 32); \
1789 break; \
1790 case kAtomic##op##Uint16: \
1791 switch (AtomicWidthField::decode(opcode)) { \
1792 case AtomicWidth::kWord32: \
1793 ASSEMBLE_ATOMIC_BINOP_EXT(Ll_w, Sc_w, false, 16, inst32, 32); \
1794 break; \
1795 case AtomicWidth::kWord64: \
1796 ASSEMBLE_ATOMIC_BINOP_EXT(Ll_d, Sc_d, false, 16, inst64, 64); \
1797 break; \
1798 } \
1799 break;
1800 ATOMIC_BINOP_CASE(Add, Add_w, Add_d)
1801 ATOMIC_BINOP_CASE(Sub, Sub_w, Sub_d)
1802 ATOMIC_BINOP_CASE(And, And, And)
1803 ATOMIC_BINOP_CASE(Or, Or, Or)
1804 ATOMIC_BINOP_CASE(Xor, Xor, Xor)
1805 #undef ATOMIC_BINOP_CASE
1806
1807 case kLoong64Word64AtomicAddUint64:
1808 __ Add_d(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1));
1809 __ amadd_db_d(i.OutputRegister(0), i.InputRegister(2), i.TempRegister(0));
1810 break;
1811 case kLoong64Word64AtomicSubUint64:
1812 ASSEMBLE_ATOMIC_BINOP(Ll_d, Sc_d, Sub_d);
1813 break;
1814 case kLoong64Word64AtomicAndUint64:
1815 __ Add_d(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1));
1816 __ amand_db_d(i.OutputRegister(0), i.InputRegister(2), i.TempRegister(0));
1817 break;
1818 case kLoong64Word64AtomicOrUint64:
1819 __ Add_d(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1));
1820 __ amor_db_d(i.OutputRegister(0), i.InputRegister(2), i.TempRegister(0));
1821 break;
1822 case kLoong64Word64AtomicXorUint64:
1823 __ Add_d(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1));
1824 __ amxor_db_d(i.OutputRegister(0), i.InputRegister(2), i.TempRegister(0));
1825 break;
1826 #undef ATOMIC_BINOP_CASE
1827 case kLoong64S128Const:
1828 case kLoong64S128Zero:
1829 case kLoong64I32x4Splat:
1830 case kLoong64I32x4ExtractLane:
1831 case kLoong64I32x4Add:
1832 case kLoong64I32x4ReplaceLane:
1833 case kLoong64I32x4Sub:
1834 case kLoong64F64x2Abs:
1835 default:
1836 break;
1837 }
1838 return kSuccess;
1839 }
1840
1841 #define UNSUPPORTED_COND(opcode, condition) \
1842 StdoutStream{} << "Unsupported " << #opcode << " condition: \"" << condition \
1843 << "\""; \
1844 UNIMPLEMENTED();
1845
AssembleBranchToLabels(CodeGenerator * gen,TurboAssembler * tasm,Instruction * instr,FlagsCondition condition,Label * tlabel,Label * flabel,bool fallthru)1846 void AssembleBranchToLabels(CodeGenerator* gen, TurboAssembler* tasm,
1847 Instruction* instr, FlagsCondition condition,
1848 Label* tlabel, Label* flabel, bool fallthru) {
1849 #undef __
1850 #define __ tasm->
1851 Loong64OperandConverter i(gen, instr);
1852
1853 Condition cc = kNoCondition;
1854 // LOONG64 does not have condition code flags, so compare and branch are
1855 // implemented differently than on the other arch's. The compare operations
1856 // emit loong64 pseudo-instructions, which are handled here by branch
1857 // instructions that do the actual comparison. Essential that the input
1858 // registers to compare pseudo-op are not modified before this branch op, as
1859 // they are tested here.
1860
1861 if (instr->arch_opcode() == kLoong64Tst) {
1862 cc = FlagsConditionToConditionTst(condition);
1863 __ Branch(tlabel, cc, t8, Operand(zero_reg));
1864 } else if (instr->arch_opcode() == kLoong64Add_d ||
1865 instr->arch_opcode() == kLoong64Sub_d) {
1866 UseScratchRegisterScope temps(tasm);
1867 Register scratch = temps.Acquire();
1868 Register scratch2 = temps.Acquire();
1869 cc = FlagsConditionToConditionOvf(condition);
1870 __ srai_d(scratch, i.OutputRegister(), 32);
1871 __ srai_w(scratch2, i.OutputRegister(), 31);
1872 __ Branch(tlabel, cc, scratch2, Operand(scratch));
1873 } else if (instr->arch_opcode() == kLoong64AddOvf_d ||
1874 instr->arch_opcode() == kLoong64SubOvf_d) {
1875 switch (condition) {
1876 // Overflow occurs if overflow register is negative
1877 case kOverflow:
1878 __ Branch(tlabel, lt, t8, Operand(zero_reg));
1879 break;
1880 case kNotOverflow:
1881 __ Branch(tlabel, ge, t8, Operand(zero_reg));
1882 break;
1883 default:
1884 UNSUPPORTED_COND(instr->arch_opcode(), condition);
1885 }
1886 } else if (instr->arch_opcode() == kLoong64MulOvf_w) {
1887 // Overflow occurs if overflow register is not zero
1888 switch (condition) {
1889 case kOverflow:
1890 __ Branch(tlabel, ne, t8, Operand(zero_reg));
1891 break;
1892 case kNotOverflow:
1893 __ Branch(tlabel, eq, t8, Operand(zero_reg));
1894 break;
1895 default:
1896 UNSUPPORTED_COND(kLoong64MulOvf_w, condition);
1897 }
1898 } else if (instr->arch_opcode() == kLoong64Cmp) {
1899 cc = FlagsConditionToConditionCmp(condition);
1900 __ Branch(tlabel, cc, i.InputRegister(0), i.InputOperand(1));
1901 } else if (instr->arch_opcode() == kArchStackPointerGreaterThan) {
1902 cc = FlagsConditionToConditionCmp(condition);
1903 DCHECK((cc == ls) || (cc == hi));
1904 if (cc == ls) {
1905 __ xori(i.TempRegister(0), i.TempRegister(0), 1);
1906 }
1907 __ Branch(tlabel, ne, i.TempRegister(0), Operand(zero_reg));
1908 } else if (instr->arch_opcode() == kLoong64Float32Cmp ||
1909 instr->arch_opcode() == kLoong64Float64Cmp) {
1910 bool predicate;
1911 FlagsConditionToConditionCmpFPU(&predicate, condition);
1912 if (predicate) {
1913 __ BranchTrueF(tlabel);
1914 } else {
1915 __ BranchFalseF(tlabel);
1916 }
1917 } else {
1918 PrintF("AssembleArchBranch Unimplemented arch_opcode: %d\n",
1919 instr->arch_opcode());
1920 UNIMPLEMENTED();
1921 }
1922 if (!fallthru) __ Branch(flabel); // no fallthru to flabel.
1923 #undef __
1924 #define __ tasm()->
1925 }
1926
1927 // Assembles branches after an instruction.
AssembleArchBranch(Instruction * instr,BranchInfo * branch)1928 void CodeGenerator::AssembleArchBranch(Instruction* instr, BranchInfo* branch) {
1929 Label* tlabel = branch->true_label;
1930 Label* flabel = branch->false_label;
1931
1932 AssembleBranchToLabels(this, tasm(), instr, branch->condition, tlabel, flabel,
1933 branch->fallthru);
1934 }
1935
1936 #undef UNSUPPORTED_COND
1937
AssembleArchDeoptBranch(Instruction * instr,BranchInfo * branch)1938 void CodeGenerator::AssembleArchDeoptBranch(Instruction* instr,
1939 BranchInfo* branch) {
1940 AssembleArchBranch(instr, branch);
1941 }
1942
AssembleArchJumpRegardlessOfAssemblyOrder(RpoNumber target)1943 void CodeGenerator::AssembleArchJumpRegardlessOfAssemblyOrder(
1944 RpoNumber target) {
1945 __ Branch(GetLabel(target));
1946 }
1947
1948 #if V8_ENABLE_WEBASSEMBLY
AssembleArchTrap(Instruction * instr,FlagsCondition condition)1949 void CodeGenerator::AssembleArchTrap(Instruction* instr,
1950 FlagsCondition condition) {
1951 class OutOfLineTrap final : public OutOfLineCode {
1952 public:
1953 OutOfLineTrap(CodeGenerator* gen, Instruction* instr)
1954 : OutOfLineCode(gen), instr_(instr), gen_(gen) {}
1955 void Generate() final {
1956 Loong64OperandConverter i(gen_, instr_);
1957 TrapId trap_id =
1958 static_cast<TrapId>(i.InputInt32(instr_->InputCount() - 1));
1959 GenerateCallToTrap(trap_id);
1960 }
1961
1962 private:
1963 void GenerateCallToTrap(TrapId trap_id) {
1964 if (trap_id == TrapId::kInvalid) {
1965 // We cannot test calls to the runtime in cctest/test-run-wasm.
1966 // Therefore we emit a call to C here instead of a call to the runtime.
1967 // We use the context register as the scratch register, because we do
1968 // not have a context here.
1969 __ PrepareCallCFunction(0, 0, cp);
1970 __ CallCFunction(
1971 ExternalReference::wasm_call_trap_callback_for_testing(), 0);
1972 __ LeaveFrame(StackFrame::WASM);
1973 auto call_descriptor = gen_->linkage()->GetIncomingDescriptor();
1974 int pop_count = static_cast<int>(call_descriptor->ParameterSlotCount());
1975 pop_count += (pop_count & 1); // align
1976 __ Drop(pop_count);
1977 __ Ret();
1978 } else {
1979 gen_->AssembleSourcePosition(instr_);
1980 // A direct call to a wasm runtime stub defined in this module.
1981 // Just encode the stub index. This will be patched when the code
1982 // is added to the native module and copied into wasm code space.
1983 __ Call(static_cast<Address>(trap_id), RelocInfo::WASM_STUB_CALL);
1984 ReferenceMap* reference_map =
1985 gen_->zone()->New<ReferenceMap>(gen_->zone());
1986 gen_->RecordSafepoint(reference_map);
1987 if (FLAG_debug_code) {
1988 __ stop();
1989 }
1990 }
1991 }
1992 Instruction* instr_;
1993 CodeGenerator* gen_;
1994 };
1995 auto ool = zone()->New<OutOfLineTrap>(this, instr);
1996 Label* tlabel = ool->entry();
1997 AssembleBranchToLabels(this, tasm(), instr, condition, tlabel, nullptr, true);
1998 }
1999 #endif // V8_ENABLE_WEBASSEMBLY
2000
2001 // Assembles boolean materializations after an instruction.
AssembleArchBoolean(Instruction * instr,FlagsCondition condition)2002 void CodeGenerator::AssembleArchBoolean(Instruction* instr,
2003 FlagsCondition condition) {
2004 Loong64OperandConverter i(this, instr);
2005
2006 // Materialize a full 32-bit 1 or 0 value. The result register is always the
2007 // last output of the instruction.
2008 DCHECK_NE(0u, instr->OutputCount());
2009 Register result = i.OutputRegister(instr->OutputCount() - 1);
2010 Condition cc = kNoCondition;
2011 // Loong64 does not have condition code flags, so compare and branch are
2012 // implemented differently than on the other arch's. The compare operations
2013 // emit loong64 pseudo-instructions, which are checked and handled here.
2014
2015 if (instr->arch_opcode() == kLoong64Tst) {
2016 cc = FlagsConditionToConditionTst(condition);
2017 if (cc == eq) {
2018 __ Sltu(result, t8, 1);
2019 } else {
2020 __ Sltu(result, zero_reg, t8);
2021 }
2022 return;
2023 } else if (instr->arch_opcode() == kLoong64Add_d ||
2024 instr->arch_opcode() == kLoong64Sub_d) {
2025 UseScratchRegisterScope temps(tasm());
2026 Register scratch = temps.Acquire();
2027 cc = FlagsConditionToConditionOvf(condition);
2028 // Check for overflow creates 1 or 0 for result.
2029 __ srli_d(scratch, i.OutputRegister(), 63);
2030 __ srli_w(result, i.OutputRegister(), 31);
2031 __ xor_(result, scratch, result);
2032 if (cc == eq) // Toggle result for not overflow.
2033 __ xori(result, result, 1);
2034 return;
2035 } else if (instr->arch_opcode() == kLoong64AddOvf_d ||
2036 instr->arch_opcode() == kLoong64SubOvf_d) {
2037 // Overflow occurs if overflow register is negative
2038 __ slt(result, t8, zero_reg);
2039 } else if (instr->arch_opcode() == kLoong64MulOvf_w) {
2040 // Overflow occurs if overflow register is not zero
2041 __ Sgtu(result, t8, zero_reg);
2042 } else if (instr->arch_opcode() == kLoong64Cmp) {
2043 cc = FlagsConditionToConditionCmp(condition);
2044 switch (cc) {
2045 case eq:
2046 case ne: {
2047 Register left = i.InputRegister(0);
2048 Operand right = i.InputOperand(1);
2049 if (instr->InputAt(1)->IsImmediate()) {
2050 if (is_int12(-right.immediate())) {
2051 if (right.immediate() == 0) {
2052 if (cc == eq) {
2053 __ Sltu(result, left, 1);
2054 } else {
2055 __ Sltu(result, zero_reg, left);
2056 }
2057 } else {
2058 __ Add_d(result, left, Operand(-right.immediate()));
2059 if (cc == eq) {
2060 __ Sltu(result, result, 1);
2061 } else {
2062 __ Sltu(result, zero_reg, result);
2063 }
2064 }
2065 } else {
2066 __ Xor(result, left, Operand(right));
2067 if (cc == eq) {
2068 __ Sltu(result, result, 1);
2069 } else {
2070 __ Sltu(result, zero_reg, result);
2071 }
2072 }
2073 } else {
2074 __ Xor(result, left, right);
2075 if (cc == eq) {
2076 __ Sltu(result, result, 1);
2077 } else {
2078 __ Sltu(result, zero_reg, result);
2079 }
2080 }
2081 } break;
2082 case lt:
2083 case ge: {
2084 Register left = i.InputRegister(0);
2085 Operand right = i.InputOperand(1);
2086 __ Slt(result, left, right);
2087 if (cc == ge) {
2088 __ xori(result, result, 1);
2089 }
2090 } break;
2091 case gt:
2092 case le: {
2093 Register left = i.InputRegister(1);
2094 Operand right = i.InputOperand(0);
2095 __ Slt(result, left, right);
2096 if (cc == le) {
2097 __ xori(result, result, 1);
2098 }
2099 } break;
2100 case lo:
2101 case hs: {
2102 Register left = i.InputRegister(0);
2103 Operand right = i.InputOperand(1);
2104 __ Sltu(result, left, right);
2105 if (cc == hs) {
2106 __ xori(result, result, 1);
2107 }
2108 } break;
2109 case hi:
2110 case ls: {
2111 Register left = i.InputRegister(1);
2112 Operand right = i.InputOperand(0);
2113 __ Sltu(result, left, right);
2114 if (cc == ls) {
2115 __ xori(result, result, 1);
2116 }
2117 } break;
2118 default:
2119 UNREACHABLE();
2120 }
2121 return;
2122 } else if (instr->arch_opcode() == kLoong64Float64Cmp ||
2123 instr->arch_opcode() == kLoong64Float32Cmp) {
2124 FPURegister left = i.InputOrZeroDoubleRegister(0);
2125 FPURegister right = i.InputOrZeroDoubleRegister(1);
2126 if ((left == kDoubleRegZero || right == kDoubleRegZero) &&
2127 !__ IsDoubleZeroRegSet()) {
2128 __ Move(kDoubleRegZero, 0.0);
2129 }
2130 bool predicate;
2131 FlagsConditionToConditionCmpFPU(&predicate, condition);
2132 {
2133 __ movcf2gr(result, FCC0);
2134 if (!predicate) {
2135 __ xori(result, result, 1);
2136 }
2137 }
2138 return;
2139 } else if (instr->arch_opcode() == kArchStackPointerGreaterThan) {
2140 cc = FlagsConditionToConditionCmp(condition);
2141 DCHECK((cc == ls) || (cc == hi));
2142 if (cc == ls) {
2143 __ xori(i.OutputRegister(), i.TempRegister(0), 1);
2144 }
2145 return;
2146 } else {
2147 PrintF("AssembleArchBranch Unimplemented arch_opcode is : %d\n",
2148 instr->arch_opcode());
2149 TRACE_UNIMPL();
2150 UNIMPLEMENTED();
2151 }
2152 }
2153
AssembleArchBinarySearchSwitch(Instruction * instr)2154 void CodeGenerator::AssembleArchBinarySearchSwitch(Instruction* instr) {
2155 Loong64OperandConverter i(this, instr);
2156 Register input = i.InputRegister(0);
2157 std::vector<std::pair<int32_t, Label*>> cases;
2158 for (size_t index = 2; index < instr->InputCount(); index += 2) {
2159 cases.push_back({i.InputInt32(index + 0), GetLabel(i.InputRpo(index + 1))});
2160 }
2161 AssembleArchBinarySearchSwitchRange(input, i.InputRpo(1), cases.data(),
2162 cases.data() + cases.size());
2163 }
2164
AssembleArchTableSwitch(Instruction * instr)2165 void CodeGenerator::AssembleArchTableSwitch(Instruction* instr) {
2166 Loong64OperandConverter i(this, instr);
2167 Register input = i.InputRegister(0);
2168 size_t const case_count = instr->InputCount() - 2;
2169
2170 __ Branch(GetLabel(i.InputRpo(1)), hs, input, Operand(case_count));
2171 __ GenerateSwitchTable(input, case_count, [&i, this](size_t index) {
2172 return GetLabel(i.InputRpo(index + 2));
2173 });
2174 }
2175
AssembleArchSelect(Instruction * instr,FlagsCondition condition)2176 void CodeGenerator::AssembleArchSelect(Instruction* instr,
2177 FlagsCondition condition) {
2178 UNIMPLEMENTED();
2179 }
2180
FinishFrame(Frame * frame)2181 void CodeGenerator::FinishFrame(Frame* frame) {
2182 auto call_descriptor = linkage()->GetIncomingDescriptor();
2183
2184 const DoubleRegList saves_fpu = call_descriptor->CalleeSavedFPRegisters();
2185 if (!saves_fpu.is_empty()) {
2186 int count = saves_fpu.Count();
2187 DCHECK_EQ(kNumCalleeSavedFPU, count);
2188 frame->AllocateSavedCalleeRegisterSlots(count *
2189 (kDoubleSize / kSystemPointerSize));
2190 }
2191
2192 const RegList saves = call_descriptor->CalleeSavedRegisters();
2193 if (!saves.is_empty()) {
2194 int count = saves.Count();
2195 frame->AllocateSavedCalleeRegisterSlots(count);
2196 }
2197 }
2198
AssembleConstructFrame()2199 void CodeGenerator::AssembleConstructFrame() {
2200 auto call_descriptor = linkage()->GetIncomingDescriptor();
2201
2202 if (frame_access_state()->has_frame()) {
2203 if (call_descriptor->IsCFunctionCall()) {
2204 #if V8_ENABLE_WEBASSEMBLY
2205 if (info()->GetOutputStackFrameType() == StackFrame::C_WASM_ENTRY) {
2206 __ StubPrologue(StackFrame::C_WASM_ENTRY);
2207 // Reserve stack space for saving the c_entry_fp later.
2208 __ Sub_d(sp, sp, Operand(kSystemPointerSize));
2209 #else
2210 // For balance.
2211 if (false) {
2212 #endif // V8_ENABLE_WEBASSEMBLY
2213 } else {
2214 __ Push(ra, fp);
2215 __ mov(fp, sp);
2216 }
2217 } else if (call_descriptor->IsJSFunctionCall()) {
2218 __ Prologue();
2219 } else {
2220 __ StubPrologue(info()->GetOutputStackFrameType());
2221 #if V8_ENABLE_WEBASSEMBLY
2222 if (call_descriptor->IsWasmFunctionCall() ||
2223 call_descriptor->IsWasmImportWrapper() ||
2224 call_descriptor->IsWasmCapiFunction()) {
2225 __ Push(kWasmInstanceRegister);
2226 }
2227 if (call_descriptor->IsWasmCapiFunction()) {
2228 // Reserve space for saving the PC later.
2229 __ Sub_d(sp, sp, Operand(kSystemPointerSize));
2230 }
2231 #endif // V8_ENABLE_WEBASSEMBLY
2232 }
2233 }
2234
2235 int required_slots =
2236 frame()->GetTotalFrameSlotCount() - frame()->GetFixedSlotCount();
2237
2238 if (info()->is_osr()) {
2239 // TurboFan OSR-compiled functions cannot be entered directly.
2240 __ Abort(AbortReason::kShouldNotDirectlyEnterOsrFunction);
2241
2242 // Unoptimized code jumps directly to this entrypoint while the unoptimized
2243 // frame is still on the stack. Optimized code uses OSR values directly from
2244 // the unoptimized frame. Thus, all that needs to be done is to allocate the
2245 // remaining stack slots.
2246 __ RecordComment("-- OSR entrypoint --");
2247 osr_pc_offset_ = __ pc_offset();
2248 required_slots -= osr_helper()->UnoptimizedFrameSlots();
2249 }
2250
2251 const RegList saves = call_descriptor->CalleeSavedRegisters();
2252 const DoubleRegList saves_fpu = call_descriptor->CalleeSavedFPRegisters();
2253
2254 if (required_slots > 0) {
2255 DCHECK(frame_access_state()->has_frame());
2256 #if V8_ENABLE_WEBASSEMBLY
2257 if (info()->IsWasm() && required_slots * kSystemPointerSize > 4 * KB) {
2258 // For WebAssembly functions with big frames we have to do the stack
2259 // overflow check before we construct the frame. Otherwise we may not
2260 // have enough space on the stack to call the runtime for the stack
2261 // overflow.
2262 Label done;
2263
2264 // If the frame is bigger than the stack, we throw the stack overflow
2265 // exception unconditionally. Thereby we can avoid the integer overflow
2266 // check in the condition code.
2267 if (required_slots * kSystemPointerSize < FLAG_stack_size * KB) {
2268 UseScratchRegisterScope temps(tasm());
2269 Register scratch = temps.Acquire();
2270 __ Ld_d(scratch, FieldMemOperand(
2271 kWasmInstanceRegister,
2272 WasmInstanceObject::kRealStackLimitAddressOffset));
2273 __ Ld_d(scratch, MemOperand(scratch, 0));
2274 __ Add_d(scratch, scratch,
2275 Operand(required_slots * kSystemPointerSize));
2276 __ Branch(&done, uge, sp, Operand(scratch));
2277 }
2278
2279 __ Call(wasm::WasmCode::kWasmStackOverflow, RelocInfo::WASM_STUB_CALL);
2280 // The call does not return, hence we can ignore any references and just
2281 // define an empty safepoint.
2282 ReferenceMap* reference_map = zone()->New<ReferenceMap>(zone());
2283 RecordSafepoint(reference_map);
2284 if (FLAG_debug_code) {
2285 __ stop();
2286 }
2287
2288 __ bind(&done);
2289 }
2290 #endif // V8_ENABLE_WEBASSEMBLY
2291 }
2292
2293 const int returns = frame()->GetReturnSlotCount();
2294
2295 // Skip callee-saved and return slots, which are pushed below.
2296 required_slots -= saves.Count();
2297 required_slots -= saves_fpu.Count();
2298 required_slots -= returns;
2299 if (required_slots > 0) {
2300 __ Sub_d(sp, sp, Operand(required_slots * kSystemPointerSize));
2301 }
2302
2303 if (!saves_fpu.is_empty()) {
2304 // Save callee-saved FPU registers.
2305 __ MultiPushFPU(saves_fpu);
2306 DCHECK_EQ(kNumCalleeSavedFPU, saves_fpu.Count());
2307 }
2308
2309 if (!saves.is_empty()) {
2310 // Save callee-saved registers.
2311 __ MultiPush(saves);
2312 }
2313
2314 if (returns != 0) {
2315 // Create space for returns.
2316 __ Sub_d(sp, sp, Operand(returns * kSystemPointerSize));
2317 }
2318 }
2319
2320 void CodeGenerator::AssembleReturn(InstructionOperand* additional_pop_count) {
2321 auto call_descriptor = linkage()->GetIncomingDescriptor();
2322
2323 const int returns = frame()->GetReturnSlotCount();
2324 if (returns != 0) {
2325 __ Add_d(sp, sp, Operand(returns * kSystemPointerSize));
2326 }
2327
2328 // Restore GP registers.
2329 const RegList saves = call_descriptor->CalleeSavedRegisters();
2330 if (!saves.is_empty()) {
2331 __ MultiPop(saves);
2332 }
2333
2334 // Restore FPU registers.
2335 const DoubleRegList saves_fpu = call_descriptor->CalleeSavedFPRegisters();
2336 if (!saves_fpu.is_empty()) {
2337 __ MultiPopFPU(saves_fpu);
2338 }
2339
2340 Loong64OperandConverter g(this, nullptr);
2341
2342 const int parameter_slots =
2343 static_cast<int>(call_descriptor->ParameterSlotCount());
2344
2345 // {aditional_pop_count} is only greater than zero if {parameter_slots = 0}.
2346 // Check RawMachineAssembler::PopAndReturn.
2347 if (parameter_slots != 0) {
2348 if (additional_pop_count->IsImmediate()) {
2349 DCHECK_EQ(g.ToConstant(additional_pop_count).ToInt32(), 0);
2350 } else if (FLAG_debug_code) {
2351 __ Assert(eq, AbortReason::kUnexpectedAdditionalPopValue,
2352 g.ToRegister(additional_pop_count),
2353 Operand(static_cast<int64_t>(0)));
2354 }
2355 }
2356
2357 // Functions with JS linkage have at least one parameter (the receiver).
2358 // If {parameter_slots} == 0, it means it is a builtin with
2359 // kDontAdaptArgumentsSentinel, which takes care of JS arguments popping
2360 // itself.
2361 const bool drop_jsargs = frame_access_state()->has_frame() &&
2362 call_descriptor->IsJSFunctionCall() &&
2363 parameter_slots != 0;
2364
2365 if (call_descriptor->IsCFunctionCall()) {
2366 AssembleDeconstructFrame();
2367 } else if (frame_access_state()->has_frame()) {
2368 // Canonicalize JSFunction return sites for now unless they have an variable
2369 // number of stack slot pops.
2370 if (additional_pop_count->IsImmediate() &&
2371 g.ToConstant(additional_pop_count).ToInt32() == 0) {
2372 if (return_label_.is_bound()) {
2373 __ Branch(&return_label_);
2374 return;
2375 } else {
2376 __ bind(&return_label_);
2377 }
2378 }
2379 if (drop_jsargs) {
2380 // Get the actual argument count
2381 __ Ld_d(t0, MemOperand(fp, StandardFrameConstants::kArgCOffset));
2382 }
2383 AssembleDeconstructFrame();
2384 }
2385 if (drop_jsargs) {
2386 // We must pop all arguments from the stack (including the receiver). This
2387 // number of arguments is given by max(1 + argc_reg, parameter_count).
2388 if (parameter_slots > 1) {
2389 __ li(t1, parameter_slots);
2390 __ slt(t2, t0, t1);
2391 __ Movn(t0, t1, t2);
2392 }
2393 __ Alsl_d(sp, t0, sp, kSystemPointerSizeLog2);
2394 } else if (additional_pop_count->IsImmediate()) {
2395 int additional_count = g.ToConstant(additional_pop_count).ToInt32();
2396 __ Drop(parameter_slots + additional_count);
2397 } else {
2398 Register pop_reg = g.ToRegister(additional_pop_count);
2399 __ Drop(parameter_slots);
2400 __ Alsl_d(sp, pop_reg, sp, kSystemPointerSizeLog2);
2401 }
2402 __ Ret();
2403 }
2404
2405 void CodeGenerator::FinishCode() {}
2406
2407 void CodeGenerator::PrepareForDeoptimizationExits(
2408 ZoneDeque<DeoptimizationExit*>* exits) {}
2409
2410 void CodeGenerator::AssembleMove(InstructionOperand* source,
2411 InstructionOperand* destination) {
2412 Loong64OperandConverter g(this, nullptr);
2413 // Dispatch on the source and destination operand kinds. Not all
2414 // combinations are possible.
2415 if (source->IsRegister()) {
2416 DCHECK(destination->IsRegister() || destination->IsStackSlot());
2417 Register src = g.ToRegister(source);
2418 if (destination->IsRegister()) {
2419 __ mov(g.ToRegister(destination), src);
2420 } else {
2421 __ St_d(src, g.ToMemOperand(destination));
2422 }
2423 } else if (source->IsStackSlot()) {
2424 DCHECK(destination->IsRegister() || destination->IsStackSlot());
2425 MemOperand src = g.ToMemOperand(source);
2426 if (destination->IsRegister()) {
2427 __ Ld_d(g.ToRegister(destination), src);
2428 } else {
2429 UseScratchRegisterScope temps(tasm());
2430 Register scratch = temps.Acquire();
2431 __ Ld_d(scratch, src);
2432 __ St_d(scratch, g.ToMemOperand(destination));
2433 }
2434 } else if (source->IsConstant()) {
2435 Constant src = g.ToConstant(source);
2436 if (destination->IsRegister() || destination->IsStackSlot()) {
2437 UseScratchRegisterScope temps(tasm());
2438 Register scratch = temps.Acquire();
2439 Register dst =
2440 destination->IsRegister() ? g.ToRegister(destination) : scratch;
2441 switch (src.type()) {
2442 case Constant::kInt32:
2443 __ li(dst, Operand(src.ToInt32()));
2444 break;
2445 case Constant::kFloat32:
2446 __ li(dst, Operand::EmbeddedNumber(src.ToFloat32()));
2447 break;
2448 case Constant::kInt64:
2449 #if V8_ENABLE_WEBASSEMBLY
2450 if (RelocInfo::IsWasmReference(src.rmode()))
2451 __ li(dst, Operand(src.ToInt64(), src.rmode()));
2452 else
2453 #endif // V8_ENABLE_WEBASSEMBLY
2454 __ li(dst, Operand(src.ToInt64()));
2455 break;
2456 case Constant::kFloat64:
2457 __ li(dst, Operand::EmbeddedNumber(src.ToFloat64().value()));
2458 break;
2459 case Constant::kExternalReference:
2460 __ li(dst, src.ToExternalReference());
2461 break;
2462 case Constant::kDelayedStringConstant:
2463 __ li(dst, src.ToDelayedStringConstant());
2464 break;
2465 case Constant::kHeapObject: {
2466 Handle<HeapObject> src_object = src.ToHeapObject();
2467 RootIndex index;
2468 if (IsMaterializableFromRoot(src_object, &index)) {
2469 __ LoadRoot(dst, index);
2470 } else {
2471 __ li(dst, src_object);
2472 }
2473 break;
2474 }
2475 case Constant::kCompressedHeapObject:
2476 UNREACHABLE();
2477 case Constant::kRpoNumber:
2478 UNREACHABLE(); // TODO(titzer): loading RPO numbers on LOONG64.
2479 }
2480 if (destination->IsStackSlot()) __ St_d(dst, g.ToMemOperand(destination));
2481 } else if (src.type() == Constant::kFloat32) {
2482 if (destination->IsFPStackSlot()) {
2483 MemOperand dst = g.ToMemOperand(destination);
2484 if (bit_cast<int32_t>(src.ToFloat32()) == 0) {
2485 __ St_d(zero_reg, dst);
2486 } else {
2487 UseScratchRegisterScope temps(tasm());
2488 Register scratch = temps.Acquire();
2489 __ li(scratch, Operand(bit_cast<int32_t>(src.ToFloat32())));
2490 __ St_d(scratch, dst);
2491 }
2492 } else {
2493 DCHECK(destination->IsFPRegister());
2494 FloatRegister dst = g.ToSingleRegister(destination);
2495 __ Move(dst, src.ToFloat32());
2496 }
2497 } else {
2498 DCHECK_EQ(Constant::kFloat64, src.type());
2499 DoubleRegister dst = destination->IsFPRegister()
2500 ? g.ToDoubleRegister(destination)
2501 : kScratchDoubleReg;
2502 __ Move(dst, src.ToFloat64().value());
2503 if (destination->IsFPStackSlot()) {
2504 __ Fst_d(dst, g.ToMemOperand(destination));
2505 }
2506 }
2507 } else if (source->IsFPRegister()) {
2508 FPURegister src = g.ToDoubleRegister(source);
2509 if (destination->IsFPRegister()) {
2510 FPURegister dst = g.ToDoubleRegister(destination);
2511 __ Move(dst, src);
2512 } else {
2513 DCHECK(destination->IsFPStackSlot());
2514 __ Fst_d(src, g.ToMemOperand(destination));
2515 }
2516 } else if (source->IsFPStackSlot()) {
2517 DCHECK(destination->IsFPRegister() || destination->IsFPStackSlot());
2518 MemOperand src = g.ToMemOperand(source);
2519 if (destination->IsFPRegister()) {
2520 __ Fld_d(g.ToDoubleRegister(destination), src);
2521 } else {
2522 DCHECK(destination->IsFPStackSlot());
2523 FPURegister temp = kScratchDoubleReg;
2524 __ Fld_d(temp, src);
2525 __ Fst_d(temp, g.ToMemOperand(destination));
2526 }
2527 } else {
2528 UNREACHABLE();
2529 }
2530 }
2531
2532 void CodeGenerator::AssembleSwap(InstructionOperand* source,
2533 InstructionOperand* destination) {
2534 Loong64OperandConverter g(this, nullptr);
2535 // Dispatch on the source and destination operand kinds. Not all
2536 // combinations are possible.
2537 if (source->IsRegister()) {
2538 UseScratchRegisterScope temps(tasm());
2539 Register scratch = temps.Acquire();
2540 // Register-register.
2541 Register src = g.ToRegister(source);
2542 if (destination->IsRegister()) {
2543 Register dst = g.ToRegister(destination);
2544 __ Move(scratch, src);
2545 __ Move(src, dst);
2546 __ Move(dst, scratch);
2547 } else {
2548 DCHECK(destination->IsStackSlot());
2549 MemOperand dst = g.ToMemOperand(destination);
2550 __ mov(scratch, src);
2551 __ Ld_d(src, dst);
2552 __ St_d(scratch, dst);
2553 }
2554 } else if (source->IsStackSlot()) {
2555 DCHECK(destination->IsStackSlot());
2556 // TODO(LOONG_dev): LOONG64 Optimize scratch registers usage
2557 // Since the Ld instruction may need a scratch reg,
2558 // we should not use both of the two scratch registers in
2559 // UseScratchRegisterScope here.
2560 UseScratchRegisterScope temps(tasm());
2561 Register scratch = temps.Acquire();
2562 FPURegister scratch_d = kScratchDoubleReg;
2563 MemOperand src = g.ToMemOperand(source);
2564 MemOperand dst = g.ToMemOperand(destination);
2565 __ Ld_d(scratch, src);
2566 __ Fld_d(scratch_d, dst);
2567 __ St_d(scratch, dst);
2568 __ Fst_d(scratch_d, src);
2569 } else if (source->IsFPRegister()) {
2570 FPURegister scratch_d = kScratchDoubleReg;
2571 FPURegister src = g.ToDoubleRegister(source);
2572 if (destination->IsFPRegister()) {
2573 FPURegister dst = g.ToDoubleRegister(destination);
2574 __ Move(scratch_d, src);
2575 __ Move(src, dst);
2576 __ Move(dst, scratch_d);
2577 } else {
2578 DCHECK(destination->IsFPStackSlot());
2579 MemOperand dst = g.ToMemOperand(destination);
2580 __ Move(scratch_d, src);
2581 __ Fld_d(src, dst);
2582 __ Fst_d(scratch_d, dst);
2583 }
2584 } else if (source->IsFPStackSlot()) {
2585 DCHECK(destination->IsFPStackSlot());
2586 UseScratchRegisterScope temps(tasm());
2587 Register scratch = temps.Acquire();
2588 MemOperand src0 = g.ToMemOperand(source);
2589 MemOperand src1(src0.base(), src0.offset() + kIntSize);
2590 MemOperand dst0 = g.ToMemOperand(destination);
2591 MemOperand dst1(dst0.base(), dst0.offset() + kIntSize);
2592 FPURegister scratch_d = kScratchDoubleReg;
2593 __ Fld_d(scratch_d, dst0); // Save destination in temp_1.
2594 __ Ld_w(scratch, src0); // Then use scratch to copy source to destination.
2595 __ St_w(scratch, dst0);
2596 __ Ld_w(scratch, src1);
2597 __ St_w(scratch, dst1);
2598 __ Fst_d(scratch_d, src0);
2599 } else {
2600 // No other combinations are possible.
2601 UNREACHABLE();
2602 }
2603 }
2604
2605 void CodeGenerator::AssembleJumpTable(Label** targets, size_t target_count) {
2606 // On 64-bit LOONG64 we emit the jump tables inline.
2607 UNREACHABLE();
2608 }
2609
2610 #undef ASSEMBLE_ATOMIC_LOAD_INTEGER
2611 #undef ASSEMBLE_ATOMIC_STORE_INTEGER
2612 #undef ASSEMBLE_ATOMIC_BINOP
2613 #undef ASSEMBLE_ATOMIC_BINOP_EXT
2614 #undef ASSEMBLE_ATOMIC_EXCHANGE_INTEGER
2615 #undef ASSEMBLE_ATOMIC_EXCHANGE_INTEGER_EXT
2616 #undef ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER
2617 #undef ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER_EXT
2618 #undef ASSEMBLE_IEEE754_BINOP
2619 #undef ASSEMBLE_IEEE754_UNOP
2620
2621 #undef TRACE_MSG
2622 #undef TRACE_UNIMPL
2623 #undef __
2624
2625 } // namespace compiler
2626 } // namespace internal
2627 } // namespace v8
2628