v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
assembler-riscv.h
Go to the documentation of this file.
1// Copyright (c) 1994-2006 Sun Microsystems Inc.
2// All Rights Reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are
6// met:
7//
8// - Redistributions of source code must retain the above copyright notice,
9// this list of conditions and the following disclaimer.
10//
11// - Redistribution in binary form must reproduce the above copyright
12// notice, this list of conditions and the following disclaimer in the
13// documentation and/or other materials provided with the distribution.
14//
15// - Neither the name of Sun Microsystems or the names of contributors may
16// be used to endorse or promote products derived from this software without
17// specific prior written permission.
18//
19// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
20// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
23// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31// The original source code covered by the above license above has been
32// modified significantly by Google Inc.
33// Copyright 2021 the V8 project authors. All rights reserved.
34
35#ifndef V8_CODEGEN_RISCV_ASSEMBLER_RISCV_H_
36#define V8_CODEGEN_RISCV_ASSEMBLER_RISCV_H_
37
38#include <stdio.h>
39
40#include <memory>
41#include <set>
42
48#include "src/codegen/label.h"
64#include "src/objects/smi.h"
65
66namespace v8 {
67namespace internal {
68
69#define DEBUG_PRINTF(...) \
70 if (v8_flags.riscv_debug) { \
71 printf(__VA_ARGS__); \
72 }
73
74class SafepointTableBuilder;
75
76// -----------------------------------------------------------------------------
77// Machine instruction Operands.
78constexpr int kSmiShift = kSmiTagSize + kSmiShiftSize;
79constexpr uintptr_t kSmiShiftMask = (1UL << kSmiShift) - 1;
80// Class Operand represents a shifter operand in data processing instructions.
81class Operand {
82 public:
83 // Immediate.
89
91 : Operand(static_cast<intptr_t>(value.ptr())) {}
92
94 : rm_(no_reg), rmode_(RelocInfo::EXTERNAL_REFERENCE) {
95 value_.immediate = static_cast<intptr_t>(f.address());
96 }
97
100
101 static Operand EmbeddedNumber(double number); // Smi or HeapNumber.
102
103 // Register.
105
106 // Return true if this is a register operand.
107 V8_INLINE bool is_reg() const { return rm_.is_valid(); }
108
113
114 inline intptr_t immediate() const {
115 DCHECK(!is_reg());
117 return value_.immediate;
118 }
119
120 bool IsImmediate() const { return !rm_.is_valid(); }
121
126
134
135 Register rm() const { return rm_; }
136
137 RelocInfo::Mode rmode() const { return rmode_; }
138
139 private:
141 union Value {
142 Value() {}
143 HeapNumberRequest heap_number_request; // if is_heap_number_request_
144 intptr_t immediate; // otherwise
145 } value_; // valid if rm_ == no_reg
146 bool is_heap_number_request_ = false;
148
149 friend class Assembler;
150 friend class MacroAssembler;
151};
152
153// On RISC-V we have only one addressing mode with base_reg + offset.
154// Class MemOperand represents a memory operand in load and store instructions.
155class V8_EXPORT_PRIVATE MemOperand : public Operand {
156 public:
157 // Immediate value attached to offset.
158 enum OffsetAddend { offset_minus_one = -1, offset_zero = 0 };
159
160 explicit MemOperand(Register rn, int32_t offset = 0);
161 explicit MemOperand(Register rn, int32_t unit, int32_t multiplier,
162 OffsetAddend offset_addend = offset_zero);
163 int32_t offset() const { return offset_; }
164
165 void set_offset(int32_t offset) { offset_ = offset; }
166
167 bool OffsetIsInt12Encodable() const { return is_int12(offset_); }
168
169 private:
170 int32_t offset_;
171
172 friend class Assembler;
173};
174
175class V8_EXPORT_PRIVATE Assembler : public AssemblerBase,
176 public AssemblerRISCVI,
177 public AssemblerRISCVA,
178 public AssemblerRISCVB,
179 public AssemblerRISCVF,
180 public AssemblerRISCVD,
181 public AssemblerRISCVM,
182 public AssemblerRISCVC,
183 public AssemblerRISCVZifencei,
184 public AssemblerRISCVZicsr,
185 public AssemblerRISCVZicond,
186 public AssemblerRISCVV {
187 public:
188 // Create an assembler. Instructions and relocation information are emitted
189 // into a buffer, with the instructions starting from the beginning and the
190 // relocation information starting from the end of the buffer. See CodeDesc
191 // for a detailed comment on the layout (globals.h).
192 //
193 // If the provided buffer is nullptr, the assembler allocates and grows its
194 // own buffer. Otherwise it takes ownership of the provided buffer.
196 std::unique_ptr<AssemblerBuffer> = {});
197 // For compatibility with assemblers that require a zone.
199 std::unique_ptr<AssemblerBuffer> buffer = {})
200 : Assembler(options, std::move(buffer)) {}
201
202 virtual ~Assembler();
203
206
208 // GetCode emits any pending (non-emitted) code and fills the descriptor desc.
209 static constexpr int kNoHandlerTable = 0;
210 static constexpr SafepointTableBuilderBase* kNoSafepointTable = nullptr;
211 void GetCode(LocalIsolate* isolate, CodeDesc* desc,
212 SafepointTableBuilderBase* safepoint_table_builder,
213 int handler_table_offset);
214
215 // Convenience wrapper for allocating with an Isolate.
216 void GetCode(Isolate* isolate, CodeDesc* desc);
217 // Convenience wrapper for code without safepoint or handler tables.
218 void GetCode(LocalIsolate* isolate, CodeDesc* desc) {
219 GetCode(isolate, desc, kNoSafepointTable, kNoHandlerTable);
220 }
221
222 // Unused on this architecture.
224
225 // Label operations & relative jumps (PPUM Appendix D).
226 //
227 // Takes a branch opcode (cc) and a label (L) and generates
228 // either a backward branch or a forward branch and links it
229 // to the label fixup chain. Usage:
230 //
231 // Label L; // unbound label
232 // j(cc, &L); // forward branch to unbound label
233 // bind(&L); // bind label to the current pc
234 // j(cc, &L); // backward branch to bound label
235 // bind(&L); // illegal: a label may be bound only once
236 //
237 // Note: The same Label can be used for forward and backward branches
238 // but it may be bound only once.
239 void bind(Label* L); // Binds an unbound label L to current code position.
240
241 // Determines if Label is bound and near enough so that branch instruction
242 // can be used to reach it, instead of jump instruction.
243 bool is_near(Label* L);
244 bool is_near(Label* L, OffsetSize bits);
246
247 // Get offset from instr.
249 static int BrachlongOffset(Instr auipc, Instr jalr);
250 static int PatchBranchlongOffset(
251 Address pc, Instr auipc, Instr instr_I, int32_t offset,
252 WritableJitAllocation* jit_allocation = nullptr);
253
254 // Returns the branch offset to the given label from the current code
255 // position. Links the label to the current position if it is still unbound.
256 // Manages the jump elimination optimization if the second parameter is true.
257 virtual int32_t branch_offset_helper(Label* L, OffsetSize bits);
258 uintptr_t jump_address(Label* L);
260
261 // Puts a labels target address at the given position.
262 // The high 8 bits are set to zero.
263 void label_at_put(Label* L, int at_offset);
264
265 // During code generation builtin targets in PC-relative call/jump
266 // instructions are temporarily encoded as builtin ID until the generated
267 // code is moved into the code space.
268 static inline Builtin target_builtin_at(Address pc);
269
270 // Read/Modify the code target address in the branch/call instruction at pc.
271 // The isolate argument is unused (and may be nullptr) when skipping flushing.
272 static Address target_constant_address_at(Address pc);
273
274 static Address target_address_at(Address pc, Address constant_pool);
275
277 Address pc, Address constant_pool, Address target,
278 WritableJitAllocation* jit_allocation = nullptr,
279 ICacheFlushMode icache_flush_mode = FLUSH_ICACHE_IF_NEEDED);
280
281 // Read/Modify the code target address in the branch/call instruction at pc.
283 Address constant_pool);
285 Address pc, Address constant_pool, Tagged_t target,
286 WritableJitAllocation* jit_allocation = nullptr,
287 ICacheFlushMode icache_flush_mode = FLUSH_ICACHE_IF_NEEDED);
288
290 Address constant_pool);
292 Address pc, Address constant_pool);
293
294 inline Handle<HeapObject> embedded_object_handle_at(Address pc);
295
296#ifdef V8_TARGET_ARCH_RISCV64
297 inline void set_embedded_object_index_referenced_from(
298 Address p, EmbeddedObjectIndex index);
299#endif
300
301 static bool IsConstantPoolAt(Instruction* instr);
302 static int ConstantPoolSizeAt(Instruction* instr);
303 // See Assembler::CheckConstPool for more info.
304 void EmitPoolGuard();
305
306#if defined(V8_TARGET_ARCH_RISCV64)
307 static void set_target_value_at(
308 Address pc, uint64_t target,
309 WritableJitAllocation* jit_allocation = nullptr,
310 ICacheFlushMode icache_flush_mode = FLUSH_ICACHE_IF_NEEDED);
311#elif defined(V8_TARGET_ARCH_RISCV32)
312 static void set_target_value_at(
313 Address pc, uint32_t target,
314 WritableJitAllocation* jit_allocation = nullptr,
315 ICacheFlushMode icache_flush_mode = FLUSH_ICACHE_IF_NEEDED);
316#endif
317
318 static inline int32_t target_constant32_at(Address pc);
319 static inline void set_target_constant32_at(
320 Address pc, uint32_t target, WritableJitAllocation* jit_allocation,
321 ICacheFlushMode icache_flush_mode);
322
323 static void JumpLabelToJumpRegister(Address pc);
324
325 // This sets the branch destination (which gets loaded at the call address).
326 // This is for calls and branches within generated code. The serializer
327 // has already deserialized the lui/ori instructions etc.
328 inline static void deserialization_set_special_target_at(Address location,
329 Tagged<Code> code,
330 Address target);
331
332 // Get the size of the special target encoded at 'instruction_payload'.
334 Address instruction_payload);
335
336 // This sets the internal reference at the pc.
338 Address pc, Address target, WritableJitAllocation& jit_allocation,
339 RelocInfo::Mode mode = RelocInfo::INTERNAL_REFERENCE);
340
341 // Read/modify the uint32 constant used at pc.
342 static inline uint32_t uint32_constant_at(Address pc, Address constant_pool);
343 static inline void set_uint32_constant_at(
344 Address pc, Address constant_pool, uint32_t new_constant,
345 WritableJitAllocation* jit_allocation,
346 ICacheFlushMode icache_flush_mode = FLUSH_ICACHE_IF_NEEDED);
347
348 // Here we are patching the address in the LUI/ADDI instruction pair.
349 // These values are used in the serialization process and must be zero for
350 // RISC-V platform, as InstructionStream, Embedded Object or
351 // External-reference pointers are split across two consecutive instructions
352 // and don't exist separately in the code, so the serializer should not step
353 // forwards in memory after a target is resolved and written.
354 static constexpr int kSpecialTargetSize = 0;
355
356 // Number of consecutive instructions used to store 32bit/64bit constant.
357 // This constant was used in RelocInfo::target_address_address() function
358 // to tell serializer address of the instruction that follows
359 // LUI/ADDI instruction pair.
360 static constexpr int kInstructionsFor32BitConstant = 2;
361 static constexpr int kInstructionsFor64BitConstant = 8;
362
363 // Difference between address of current opcode and value read from pc
364 // register.
365 static constexpr int kPcLoadDelta = 4;
366
367 // Bits available for offset field in branches
368 static constexpr int kBranchOffsetBits = 13;
369
370 // Bits available for offset field in jump
371 static constexpr int kJumpOffsetBits = 21;
372
373 // Bits available for offset field in compresed jump
374 static constexpr int kCJalOffsetBits = 12;
375
376 // Bits available for offset field in compressed branch
377 static constexpr int kCBranchOffsetBits = 9;
378
379 // Max offset for b instructions with 12-bit offset field (multiple of 2)
380 static constexpr int kMaxBranchOffset = (1 << (13 - 1)) - 1;
381
382 // Max offset for jal instruction with 20-bit offset field (multiple of 2)
383 static constexpr int kMaxJumpOffset = (1 << (21 - 1)) - 1;
384
385 static constexpr int kTrampolineSlotsSize = 2 * kInstrSize;
386
387 RegList* GetScratchRegisterList() { return &scratch_register_list_; }
389 return &scratch_double_register_list_;
390 }
391
392 // ---------------------------------------------------------------------------
393 // InstructionStream generation.
394
395 // Insert the smallest number of nop instructions
396 // possible to align the pc offset to a multiple
397 // of m. m must be a power of 2 (>= 4).
398 void Align(int m);
399 // Insert the smallest number of zero bytes possible to align the pc offset
400 // to a mulitple of m. m must be a power of 2 (>= 2).
401 void DataAlign(int m);
402 // Aligns code to something that's optimal for a jump target for the platform.
404 void LoopHeaderAlign() { CodeTargetAlign(); }
405
406 // Different nop operations are used by the code generator to detect certain
407 // states of the generated code.
409 NON_MARKING_NOP = 0,
410 DEBUG_BREAK_NOP,
411 // IC markers.
412 PROPERTY_ACCESS_INLINED,
413 PROPERTY_ACCESS_INLINED_CONTEXT,
414 PROPERTY_ACCESS_INLINED_CONTEXT_DONT_DELETE,
415 // Helper values.
416 LAST_CODE_MARKER,
417 FIRST_IC_MARKER = PROPERTY_ACCESS_INLINED,
418 };
419
420 void NOP();
421 void EBREAK();
422
423 // Assembler Pseudo Instructions (Tables 25.2, 25.3, RISC-V Unprivileged ISA)
424 void nop();
425#if defined(V8_TARGET_ARCH_RISCV64)
426 void RecursiveLiImpl(Register rd, int64_t imm);
427 void RecursiveLi(Register rd, int64_t imm);
428 static int RecursiveLiCount(int64_t imm);
429 static int RecursiveLiImplCount(int64_t imm);
430 void RV_li(Register rd, int64_t imm);
431 static int RV_li_count(int64_t imm, bool is_get_temp_reg = false);
432 // Returns the number of instructions required to load the immediate
433 void GeneralLi(Register rd, int64_t imm);
434 static int GeneralLiCount(int64_t imm, bool is_get_temp_reg = false);
435 // Loads an immediate, always using 8 instructions, regardless of the value,
436 // so that it can be modified later.
437 void li_constant(Register rd, int64_t imm);
438 void li_constant32(Register rd, int32_t imm);
439 void li_ptr(Register rd, int64_t imm);
440#endif
441#if defined(V8_TARGET_ARCH_RISCV32)
442 void RV_li(Register rd, int32_t imm);
443 static int RV_li_count(int32_t imm, bool is_get_temp_reg = false);
444
445 void li_constant(Register rd, int32_t imm);
446 void li_ptr(Register rd, int32_t imm);
447#endif
448
449 void break_(uint32_t code, bool break_as_stop = false);
450 void stop(uint32_t code = kMaxStopCode);
451
452 // Check the code size generated from label to here.
454 return pc_offset() - label->pos();
455 }
456
457 // Check the number of instructions generated from label to here.
459 return SizeOfCodeGeneratedSince(label) / kInstrSize;
460 }
461
462 using BlockConstPoolScope = ConstantPool::BlockScope;
463 // Class for scoping postponing the trampoline pool generation.
465 public:
466 explicit BlockTrampolinePoolScope(Assembler* assem, int margin = 0)
467 : assem_(assem) {
468 if (margin > 0) {
469 assem_->CheckTrampolinePoolQuick(margin / kInstrSize);
470 }
471 assem_->StartBlockTrampolinePool();
472 }
473
474 explicit BlockTrampolinePoolScope(Assembler* assem, PoolEmissionCheck check)
475 : assem_(assem) {
476 assem_->StartBlockTrampolinePool();
477 }
478 ~BlockTrampolinePoolScope() { assem_->EndBlockTrampolinePool(); }
479
480 private:
481 Assembler* assem_;
483 };
484
486 public:
487 // Block Trampoline Pool and Constant Pool. Emits pools if necessary to
488 // ensure that {margin} more bytes can be emitted without triggering pool
489 // emission.
490 explicit BlockPoolsScope(Assembler* assem, int margin = 0)
491 : block_const_pool_(assem, margin),
492 block_trampoline_pool_(assem, margin) {}
493
494 BlockPoolsScope(Assembler* assem, PoolEmissionCheck check, int margin = 0)
495 : block_const_pool_(assem, check),
496 block_trampoline_pool_(assem, margin) {}
498
499 private:
500 BlockConstPoolScope block_const_pool_;
503 };
504
505 // Class for postponing the assembly buffer growth. Typically used for
506 // sequences of instructions that must be emitted as a unit, before
507 // buffer growth (and relocation) can occur.
508 // This blocking scope is not nestable.
510 public:
511 explicit BlockGrowBufferScope(Assembler* assem) : assem_(assem) {
512 assem_->StartBlockGrowBuffer();
513 }
514 ~BlockGrowBufferScope() { assem_->EndBlockGrowBuffer(); }
515
516 private:
517 Assembler* assem_;
518
520 };
521
522 // Record a deoptimization reason that can be used by a log or cpu profiler.
523 // Use --trace-deopt to enable.
524 void RecordDeoptReason(DeoptimizeReason reason, uint32_t node_id,
525 SourcePosition position, int id);
526
527 static int RelocateInternalReference(RelocInfo::Mode rmode, Address pc,
528 intptr_t pc_delta);
529 static void RelocateRelativeReference(RelocInfo::Mode rmode, Address pc,
530 intptr_t pc_delta);
531
532 // Writes a single byte or word of data in the code stream. Used for
533 // inline tables, e.g., jump-tables.
534 void db(uint8_t data);
535 void dd(uint32_t data);
536 void dq(uint64_t data);
537 void dp(uintptr_t data) { dq(data); }
538 void dd(Label* label);
539
540 Instruction* pc() const { return reinterpret_cast<Instruction*>(pc_); }
541
542 Instruction* InstructionAt(ptrdiff_t offset) const {
543 return reinterpret_cast<Instruction*>(buffer_start_ + offset);
544 }
545
546 // Postpone the generation of the trampoline pool for the specified number of
547 // instructions.
548 void BlockTrampolinePoolFor(int instructions);
549
550 // Check if there is less than kGap bytes available in the buffer.
551 // If this is the case, we need to grow the buffer before emitting
552 // an instruction or relocation information.
553 inline bool overflow() const { return pc_ >= reloc_info_writer.pos() - kGap; }
554
555 // Get the number of bytes available in the buffer.
556 inline intptr_t available_space() const {
557 return reloc_info_writer.pos() - pc_;
558 }
559
560 // Read/patch instructions.
561 static Instr instr_at(Address pc) { return *reinterpret_cast<Instr*>(pc); }
562 static void instr_at_put(Address pc, Instr instr,
563 WritableJitAllocation* jit_allocation = nullptr);
564
566 return *reinterpret_cast<Instr*>(buffer_start_ + pos);
567 }
568 void instr_at_put(int pos, Instr instr,
569 WritableJitAllocation* jit_allocation = nullptr);
570
571 void instr_at_put(int pos, ShortInstr instr,
572 WritableJitAllocation* jit_allocation = nullptr);
573
574 Address toAddress(int pos) {
575 return reinterpret_cast<Address>(buffer_start_ + pos);
576 }
577
579
580 // Get the code target object for a pc-relative call or jump.
582 Address pc_) const;
583
584 inline int UnboundLabelsCount() { return unbound_labels_count_; }
585
586 void RecordConstPool(int size);
587
589 constpool_.Check(Emission::kForced, Jump::kOmitted);
590 }
592 constpool_.Check(Emission::kForced, Jump::kRequired);
593 }
594 // Check if the const pool needs to be emitted while pretending that {margin}
595 // more bytes of instructions have already been emitted.
596 void EmitConstPoolWithJumpIfNeeded(size_t margin = 0) {
597 constpool_.Check(Emission::kIfNeeded, Jump::kRequired, margin);
598 }
599
600 void EmitConstPoolWithoutJumpIfNeeded(size_t margin = 0) {
601 constpool_.Check(Emission::kIfNeeded, Jump::kOmitted, margin);
602 }
603
604 RelocInfoStatus RecordEntry(uint32_t data, RelocInfo::Mode rmode) {
605 return constpool_.RecordEntry(data, rmode);
606 }
607
608 RelocInfoStatus RecordEntry(uint64_t data, RelocInfo::Mode rmode) {
609 return constpool_.RecordEntry(data, rmode);
610 }
611
612 void CheckTrampolinePoolQuick(int extra_instructions = 0) {
613 DEBUG_PRINTF("\tCheckTrampolinePoolQuick pc_offset:%d %d\n", pc_offset(),
614 next_buffer_check_ - extra_instructions * kInstrSize);
615 if (pc_offset() >= next_buffer_check_ - extra_instructions * kInstrSize) {
616 CheckTrampolinePool();
617 }
618 }
619
620 friend class VectorUnit;
622 public:
623 inline int32_t sew() const { return 2 ^ (sew_ + 3); }
624
625 inline int32_t vlmax() const {
626 if ((lmul_ & 0b100) != 0) {
627 return (kRvvVLEN / sew()) >> (lmul_ & 0b11);
628 } else {
629 return ((kRvvVLEN << lmul_) / sew());
630 }
631 }
632
633 explicit VectorUnit(Assembler* assm) : assm_(assm) {}
634
635 void set(Register rd, VSew sew, Vlmul lmul) {
636 if (sew != sew_ || lmul != lmul_ || vl != vlmax()) {
637 sew_ = sew;
638 lmul_ = lmul;
639 vl = vlmax();
640 assm_->vsetvlmax(rd, sew_, lmul_);
641 }
642 }
643
644 void set(Register rd, int8_t sew, int8_t lmul) {
645 DCHECK_GE(sew, E8);
646 DCHECK_LE(sew, E64);
647 DCHECK_GE(lmul, m1);
648 DCHECK_LE(lmul, mf2);
649 set(rd, VSew(sew), Vlmul(lmul));
650 }
651
652 void set(FPURoundingMode mode) {
653 if (mode_ != mode) {
654 assm_->addi(kScratchReg, zero_reg, mode << kFcsrFrmShift);
655 assm_->fscsr(kScratchReg);
656 mode_ = mode;
657 }
658 }
659 void set(Register rd, Register rs1, VSew sew, Vlmul lmul) {
660 if (sew != sew_ || lmul != lmul_) {
661 sew_ = sew;
662 lmul_ = lmul;
663 vl = 0;
664 assm_->vsetvli(rd, rs1, sew_, lmul_);
665 }
666 }
667
668 void set(VSew sew, Vlmul lmul) {
669 if (sew != sew_ || lmul != lmul_) {
670 sew_ = sew;
671 lmul_ = lmul;
672 assm_->vsetvl(sew_, lmul_);
673 }
674 }
675
676 void clear() {
677 sew_ = kVsInvalid;
678 lmul_ = kVlInvalid;
679 }
680
681 private:
684 int32_t vl = 0;
687 };
688
690
691 void ClearVectorunit() { VU.clear(); }
692
693 protected:
694 // Readable constants for base and offset adjustment helper, these indicate if
695 // aside from offset, another value like offset + 4 should fit into int16.
696 enum class OffsetAccessType : bool {
697 SINGLE_ACCESS = false,
698 TWO_ACCESSES = true
699 };
700
701 // Determine whether need to adjust base and offset of memroy load/store
702 bool NeedAdjustBaseAndOffset(
703 const MemOperand& src, OffsetAccessType = OffsetAccessType::SINGLE_ACCESS,
704 int second_Access_add_to_offset = 4);
705
706 // Helper function for memory load/store using base register and offset.
707 void AdjustBaseAndOffset(
708 MemOperand* src, Register scratch,
709 OffsetAccessType access_type = OffsetAccessType::SINGLE_ACCESS,
710 int second_access_add_to_offset = 4);
711
713 Address target);
714
715 intptr_t buffer_space() const { return reloc_info_writer.pos() - pc_; }
716
717 // Decode branch instruction at pos and return branch target pos.
718 int target_at(int pos, bool is_internal);
719
720 // Patch branch instruction at pos to branch to given branch target pos.
721 void target_at_put(int pos, int target_pos, bool is_internal);
722
723 // Say if we need to relocate with this mode.
725
726 // Record reloc info for current pc_.
727 void RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data = 0);
728
729 // Block the emission of the trampoline pool before pc_offset.
731 if (no_trampoline_pool_before_ < pc_offset)
732 no_trampoline_pool_before_ = pc_offset;
733 }
734
736 DEBUG_PRINTF("\tStartBlockTrampolinePool\n");
737 trampoline_pool_blocked_nesting_++;
738 }
739
741 trampoline_pool_blocked_nesting_--;
742 DEBUG_PRINTF("\ttrampoline_pool_blocked_nesting:%d\n",
743 trampoline_pool_blocked_nesting_);
744 if (trampoline_pool_blocked_nesting_ == 0) {
745 CheckTrampolinePoolQuick(1);
746 }
747 }
748
750 return trampoline_pool_blocked_nesting_ > 0;
751 }
752
753 bool has_exception() const { return internal_trampoline_exception_; }
754
755 bool is_trampoline_emitted() const { return trampoline_emitted_; }
756
757 // Temporarily block automatic assembly buffer growth.
759 DCHECK(!block_buffer_growth_);
760 block_buffer_growth_ = true;
761 }
762
764 DCHECK(block_buffer_growth_);
765 block_buffer_growth_ = false;
766 }
767
768 bool is_buffer_growth_blocked() const { return block_buffer_growth_; }
769
770 private:
771 // Avoid overflows for displacements etc.
772 static const int kMaximalBufferSize = 512 * MB;
773
774 // Buffer size and constant pool distance are checked together at regular
775 // intervals of kBufferCheckInterval emitted bytes.
776 static constexpr int kBufferCheckInterval = 1 * KB / 2;
777
778 // InstructionStream generation.
779 // The relocation writer's position is at least kGap bytes below the end of
780 // the generated instructions. This is so that multi-instruction sequences do
781 // not have to check for overflow. The same is true for writes of large
782 // relocation info entries.
783 static constexpr int kGap = 64;
784 static_assert(AssemblerBase::kMinimalBufferSize >= 2 * kGap);
785
786 // Repeated checking whether the trampoline pool should be emitted is rather
787 // expensive. By default we only check again once a number of instructions
788 // has been generated.
789 static constexpr int kCheckConstIntervalInst = 32;
790 static constexpr int kCheckConstInterval =
791 kCheckConstIntervalInst * kInstrSize;
792
793 int next_buffer_check_; // pc offset of next buffer check.
794
795 // Emission of the trampoline pool may be blocked in some code sequences.
796 int trampoline_pool_blocked_nesting_; // Block emission if this is not zero.
797 int no_trampoline_pool_before_; // Block emission before this pc offset.
798
799 // Keep track of the last emitted pool to guarantee a maximal distance.
800 int last_trampoline_pool_end_; // pc offset of the end of the last pool.
801
802 // Automatic growth of the assembly buffer may be blocked for some sequences.
803 bool block_buffer_growth_; // Block growth when true.
804
805 // Relocation information generation.
806 // Each relocation is encoded as a variable size value.
807 static constexpr int kMaxRelocSize = RelocInfoWriter::kMaxSize;
808 RelocInfoWriter reloc_info_writer;
809
810 // The bound position, before this we cannot do instruction elimination.
811 int last_bound_pos_;
812
813 // InstructionStream emission.
814 inline void CheckBuffer();
816 void emit(Instr x);
817 void emit(ShortInstr x);
818 void emit(uint64_t x);
819 template <typename T>
820 inline void EmitHelper(T x);
821
822 static void disassembleInstr(uint8_t* pc);
823
824 // Labels.
825 void print(const Label* L);
826 void bind_to(Label* L, int pos);
827 void next(Label* L, bool is_internal);
828
829 // One trampoline consists of:
830 // - space for trampoline slots,
831 // - space for labels.
832 //
833 // Space for trampoline slots is equal to slot_count * 2 * kInstrSize.
834 // Space for trampoline slots precedes space for labels. Each label is of one
835 // instruction size, so total amount for labels is equal to
836 // label_count * kInstrSize.
838 public:
840 start_ = 0;
841 next_slot_ = 0;
842 free_slot_count_ = 0;
843 end_ = 0;
844 }
845 Trampoline(int start, int slot_count) {
846 start_ = start;
847 next_slot_ = start;
848 free_slot_count_ = slot_count;
849 end_ = start + slot_count * kTrampolineSlotsSize;
850 }
851 int start() { return start_; }
852 int end() { return end_; }
853 int take_slot() {
854 int trampoline_slot = kInvalidSlotPos;
855 if (free_slot_count_ <= 0) {
856 // We have run out of space on trampolines.
857 // Make sure we fail in debug mode, so we become aware of each case
858 // when this happens.
859 DCHECK(0);
860 // Internal exception will be caught.
861 } else {
862 trampoline_slot = next_slot_;
863 free_slot_count_--;
864 next_slot_ += kTrampolineSlotsSize;
865 DEBUG_PRINTF("\ttrampoline slot %d next %d free %d\n", trampoline_slot,
866 next_slot_, free_slot_count_)
867 }
868 return trampoline_slot;
869 }
870
871 private:
872 int start_;
873 int end_;
874 int next_slot_;
875 int free_slot_count_;
876 };
877
878 int32_t get_trampoline_entry(int32_t pos);
879 int unbound_labels_count_;
880 // After trampoline is emitted, long branches are used in generated code for
881 // the forward branches whose target offsets could be beyond reach of branch
882 // instruction. We use this information to trigger different mode of
883 // branch instruction generation, where we use jump instructions rather
884 // than regular branch instructions.
885 bool trampoline_emitted_ = false;
886 static constexpr int kInvalidSlotPos = -1;
887
888 // Internal reference positions, required for unbounded internal reference
889 // labels.
892 return internal_reference_positions_.find(L->pos()) !=
893 internal_reference_positions_.end();
894 }
895
896 Trampoline trampoline_;
897 bool internal_trampoline_exception_;
898
899 RegList scratch_register_list_;
901
902 private:
903 ConstantPool constpool_;
904
906
908
910 friend class RelocInfo;
911 friend class BlockTrampolinePoolScope;
912 friend class EnsureSpace;
913 friend class ConstantPool;
914};
915
916class EnsureSpace {
917 public:
918 explicit inline EnsureSpace(Assembler* assembler);
919};
920
921// This scope utility allows scratch registers to be managed safely. The
922// Assembler's {GetScratchRegisterList()}/{GetScratchDoubleRegisterList()}
923// are used as pools of general-purpose/double scratch registers.
924// These registers can be allocated on demand, and will be returned
925// at the end of the scope.
926//
927// When the scope ends, the Assembler's lists will be restored to their original
928// states, even if the lists are modified by some other means. Note that this
929// scope can be nested but the destructors need to run in the opposite order as
930// the constructors. We do not have assertions for this.
932 public:
934 : assembler_(assembler),
935 old_available_(*assembler->GetScratchRegisterList()),
936 old_available_double_(*assembler->GetScratchDoubleRegisterList()) {}
937
939 RegList* available = assembler_->GetScratchRegisterList();
940 DoubleRegList* available_double =
941 assembler_->GetScratchDoubleRegisterList();
942 *available = old_available_;
943 *available_double = old_available_double_;
944 }
945
947 RegList* available = assembler_->GetScratchRegisterList();
948 return available->PopFirst();
949 }
950
952 DoubleRegList* available_double =
953 assembler_->GetScratchDoubleRegisterList();
954 return available_double->PopFirst();
955 }
956
957 // Check if we have registers available to acquire.
958 bool CanAcquire() const {
959 RegList* available = assembler_->GetScratchRegisterList();
960 return !available->is_empty();
961 }
962
963 void Include(const Register& reg1, const Register& reg2) {
964 Include(reg1);
965 Include(reg2);
966 }
967 void Include(const Register& reg) {
969 RegList* available = assembler_->GetScratchRegisterList();
970 DCHECK_NOT_NULL(available);
971 DCHECK(!available->has(reg));
972 available->set(reg);
973 }
974 void Include(RegList list) {
975 RegList* available = assembler_->GetScratchRegisterList();
976 DCHECK_NOT_NULL(available);
977 *available = *available | list;
978 }
979 void Exclude(const RegList& list) {
980 RegList* available = assembler_->GetScratchRegisterList();
981 DCHECK_NOT_NULL(available);
982 available->clear(list);
983 }
984 void Exclude(const Register& reg1, const Register& reg2) {
985 Exclude(reg1);
986 Exclude(reg2);
987 }
988 void Exclude(const Register& reg) {
990 RegList list({reg});
991 Exclude(list);
992 }
993
995 DoubleRegList* available_double =
996 assembler_->GetScratchDoubleRegisterList();
997 DCHECK_NOT_NULL(available_double);
998 DCHECK_EQ((*available_double & list).bits(), 0x0);
999 *available_double = *available_double | list;
1000 }
1001
1002 RegList Available() { return *assembler_->GetScratchRegisterList(); }
1003 void SetAvailable(RegList available) {
1004 *assembler_->GetScratchRegisterList() = available;
1005 }
1007 return *assembler_->GetScratchDoubleRegisterList();
1008 }
1009 void SetAvailableDouble(DoubleRegList available_double) {
1010 *assembler_->GetScratchDoubleRegisterList() = available_double;
1011 }
1012
1013 private:
1014 friend class Assembler;
1015 friend class MacroAssembler;
1016
1018 RegList old_available_;
1020};
1021
1022[[nodiscard]] static inline Instr SetHi20Offset(int32_t hi29, Instr instr);
1023[[nodiscard]] static inline Instr SetLo12Offset(int32_t lo12, Instr instr);
1024
1025} // namespace internal
1026} // namespace v8
1027
1028#endif // V8_CODEGEN_RISCV_ASSEMBLER_RISCV_H_
#define DEBUG_PRINTF(...)
SourcePosition pos
DISALLOW_IMPLICIT_CONSTRUCTORS(BlockGrowBufferScope)
BlockTrampolinePoolScope block_trampoline_pool_
BlockPoolsScope(Assembler *assem, int margin=0)
BlockPoolsScope(Assembler *assem, PoolEmissionCheck check, int margin=0)
BlockTrampolinePoolScope(Assembler *assem, int margin=0)
DISALLOW_IMPLICIT_CONSTRUCTORS(BlockTrampolinePoolScope)
BlockTrampolinePoolScope(Assembler *assem, PoolEmissionCheck check)
Trampoline(int start, int slot_count)
void set(Register rd, VSew sew, Vlmul lmul)
void set(Register rd, int8_t sew, int8_t lmul)
void set(FPURoundingMode mode)
void set(VSew sew, Vlmul lmul)
void set(Register rd, Register rs1, VSew sew, Vlmul lmul)
void bind_to(Label *L, int pos)
void stop(uint32_t code=kMaxStopCode)
void GetCode(LocalIsolate *isolate, CodeDesc *desc)
bool is_near(Label *L, OffsetSize bits)
bool is_buffer_growth_blocked() const
bool is_internal_reference(Label *L)
intptr_t buffer_space() const
Address toAddress(int pos)
void break_(uint32_t code, bool break_as_stop=false)
void label_at_put(Label *L, int at_offset)
void EmitConstPoolWithoutJumpIfNeeded(size_t margin=0)
bool is_near(Label *L)
bool is_trampoline_emitted() const
std::set< intptr_t > internal_reference_positions_
void target_at_put(int pos, int target_pos, bool is_internal)
Assembler(const MaybeAssemblerZone &, const AssemblerOptions &options, std::unique_ptr< AssemblerBuffer > buffer={})
static int deserialization_special_target_size(Address instruction_payload)
void AllocateAndInstallRequestedHeapNumbers(LocalIsolate *isolate)
void next(Label *L, bool is_internal)
static void deserialization_set_target_internal_reference_at(Address pc, Address target, WritableJitAllocation &jit_allocation, RelocInfo::Mode mode=RelocInfo::INTERNAL_REFERENCE)
static Address target_constant_address_at(Address pc)
bool is_trampoline_pool_blocked() const
DoubleRegList * GetScratchDoubleRegisterList()
int InstructionsGeneratedSince(Label *label)
DoubleRegList scratch_double_register_list_
void db(uint8_t data)
static void set_target_address_at(Address pc, Address constant_pool, Address target, WritableJitAllocation *jit_allocation=nullptr, ICacheFlushMode icache_flush_mode=FLUSH_ICACHE_IF_NEEDED)
Instruction * InstructionAt(ptrdiff_t offset) const
intptr_t available_space() const
static void set_uint32_constant_at(Address pc, Address constant_pool, uint32_t new_constant, WritableJitAllocation *jit_allocation, ICacheFlushMode icache_flush_mode=FLUSH_ICACHE_IF_NEEDED)
virtual int32_t branch_offset_helper(Label *L, OffsetSize bits)
uintptr_t jump_address(Label *L)
static void set_target_compressed_address_at(Address pc, Address constant_pool, Tagged_t target, WritableJitAllocation *jit_allocation=nullptr, ICacheFlushMode icache_flush_mode=FLUSH_ICACHE_IF_NEEDED)
void RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data=0)
RelocInfoStatus RecordEntry(uint32_t data, RelocInfo::Mode rmode)
void EmitConstPoolWithJumpIfNeeded(size_t margin=0)
static uint32_t uint32_constant_at(Address pc, Address constant_pool)
void print(const Label *L)
bool MustUseReg(RelocInfo::Mode rmode)
static Tagged_t target_compressed_address_at(Address pc, Address constant_pool)
static Address target_address_at(Address pc, Address constant_pool)
Handle< Object > code_target_object_handle_at(Address pc, Address constant_pool)
void emit(uint64_t x)
void BlockTrampolinePoolFor(int instructions)
void GetCode(Isolate *isolate, CodeDesc *desc)
void dd(Label *label)
Handle< HeapObject > compressed_embedded_object_handle_at(Address pc, Address constant_pool)
void dq(uint64_t data)
static Instr instr_at(Address pc)
V8_INLINE Handle< Code > relative_code_target_object_handle_at(Address pc_) const
int32_t get_trampoline_entry(int32_t pos)
static RegList DefaultTmpList()
void GetCode(LocalIsolate *isolate, CodeDesc *desc, SafepointTableBuilderBase *safepoint_table_builder, int handler_table_offset)
bool is_near_branch(Label *L)
RelocInfoStatus RecordEntry(uint64_t data, RelocInfo::Mode rmode)
void dp(uintptr_t data)
int32_t branch_long_offset(Label *L)
static void JumpLabelToJumpRegister(Address pc)
int BranchOffset(Instr instr)
void dd(uint32_t data)
Assembler(const AssemblerOptions &, std::unique_ptr< AssemblerBuffer >={})
void CheckTrampolinePoolQuick(int extra_instructions=0)
int SizeOfCodeGeneratedSince(Label *label)
static void set_target_internal_reference_encoded_at(Address pc, Address target)
Instruction * pc() const
void RecordDeoptReason(DeoptimizeReason reason, uint32_t node_id, SourcePosition position, int id)
void BlockTrampolinePoolBefore(int pc_offset)
int target_at(int pos, bool is_internal)
static DoubleRegList DefaultFPTmpList()
static Builtin target_builtin_at(Address pc)
EnsureSpace(Assembler *assembler)
V8_EXPORT_PRIVATE Address address() const
void set_offset(int32_t offset)
bool OffsetIsInt12Encodable() const
MemOperand(Register rn, int32_t unit, int32_t multiplier, OffsetAddend offset_addend=offset_zero)
MemOperand(Register rn, int32_t offset=0)
intptr_t immediate() const
V8_INLINE Operand(Tagged< Smi > value)
RelocInfo::Mode rmode() const
V8_INLINE Operand(const ExternalReference &f)
union v8::internal::Operand::Value value_
intptr_t immediate_for_heap_number_request() const
RelocInfo::Mode rmode()
HeapNumberRequest heap_number_request() const
RelocInfo::Mode rmode_
bool IsHeapNumberRequest() const
V8_INLINE bool is_reg() const
V8_INLINE bool is_reg() const
static Operand EmbeddedNumber(double number)
Register rm() const
V8_INLINE Operand(intptr_t immediate, RelocInfo::Mode rmode=RelocInfo::NO_INFO)
V8_INLINE Operand(int32_t immediate, RelocInfo::Mode rmode=RelocInfo::NO_INFO)
int32_t immediate() const
V8_INLINE Operand(Register rm)
constexpr bool is_empty() const
constexpr RegisterT PopFirst()
constexpr bool is_valid() const
UseScratchRegisterScope(Assembler *assembler)
void Include(const Register &reg1, const Register &reg2)
void SetAvailableDouble(DoubleRegList available_double)
void Exclude(const Register &reg1, const Register &reg2)
Operand const offset_
RecordWriteMode const mode_
#define kScratchReg
uint8_t *const start_
Definition assembler.cc:131
const v8::base::TimeTicks end_
Definition sweeper.cc:54
int start
Label label
BytecodeAssembler & assembler_
too high values may cause the compiler to set high thresholds for inlining to as much as possible avoid inlined allocation of objects that cannot escape trace load stores from virtual maglev objects use TurboFan fast string builder analyze liveness of environment slots and zap dead values trace TurboFan load elimination emit data about basic block usage in builtins to this enable builtin reordering when run mksnapshot flag for emit warnings when applying builtin profile data verify register allocation in TurboFan randomly schedule instructions to stress dependency tracking enable store store elimination in TurboFan rewrite far to near simulate GC compiler thread race related to allow float parameters to be passed in simulator mode JS Wasm Run additional turbo_optimize_inlined_js_wasm_wrappers enable experimental feedback collection in generic lowering enable Turboshaft s WasmLoadElimination enable Turboshaft s low level load elimination for JS enable Turboshaft s escape analysis for string concatenation use enable Turbolev features that we want to ship in the not too far future trace individual Turboshaft reduction steps trace intermediate Turboshaft reduction steps invocation count threshold for early optimization Enables optimizations which favor memory size over execution speed Enables sampling allocation profiler with X as a sample interval min size of a semi the new space consists of two semi spaces max size of the Collect garbage after Collect garbage after keeps maps alive for< n > old space garbage collections print one detailed trace line in allocation gc speed threshold for starting incremental marking via a task in percent of available threshold for starting incremental marking immediately in percent of available Use a single schedule for determining a marking schedule between JS and C objects schedules the minor GC task with kUserVisible priority max worker number of concurrent for NumberOfWorkerThreads start background threads that allocate memory concurrent_array_buffer_sweeping use parallel threads to clear weak refs in the atomic pause trace progress of the incremental marking trace object counts and memory usage * MB
too high values may cause the compiler to set high thresholds for inlining to as much as possible avoid inlined allocation of objects that cannot escape trace load stores from virtual maglev objects use TurboFan fast string builder analyze liveness of environment slots and zap dead values trace TurboFan load elimination emit data about basic block usage in builtins to this enable builtin reordering when run mksnapshot flag for emit warnings when applying builtin profile data verify register allocation in TurboFan randomly schedule instructions to stress dependency tracking enable store store elimination in TurboFan rewrite far to near simulate GC compiler thread race related to allow float parameters to be passed in simulator mode JS Wasm Run additional turbo_optimize_inlined_js_wasm_wrappers enable experimental feedback collection in generic lowering enable Turboshaft s WasmLoadElimination enable Turboshaft s low level load elimination for JS enable Turboshaft s escape analysis for string concatenation use enable Turbolev features that we want to ship in the not too far future trace individual Turboshaft reduction steps trace intermediate Turboshaft reduction steps invocation count threshold for early optimization Enables optimizations which favor memory size over execution speed Enables sampling allocation profiler with X as a sample interval min size of a semi the new space consists of two semi spaces max size of the Collect garbage after Collect garbage after keeps maps alive for< n > old space garbage collections print one detailed trace line in allocation gc speed threshold for starting incremental marking via a task in percent of available threshold for starting incremental marking immediately in percent of available Use a single schedule for determining a marking schedule between JS and C objects schedules the minor GC task with kUserVisible priority max worker number of concurrent for NumberOfWorkerThreads start background threads that allocate memory concurrent_array_buffer_sweeping use parallel threads to clear weak refs in the atomic pause trace progress of the incremental marking trace object counts and memory usage report a tick only when allocated zone memory changes by this amount TracingFlags::gc_stats TracingFlags::gc_stats track native contexts that are expected to be garbage collected verify heap pointers before and after GC memory reducer runs GC with ReduceMemoryFootprint flag Maximum number of memory reducer GCs scheduled Old gen GC speed is computed directly from gc tracer counters Perform compaction on full GCs based on V8 s default heuristics Perform compaction on every full GC Perform code space compaction when finalizing a full GC with stack Stress GC compaction to flush out bugs with moving objects flush of baseline code when it has not been executed recently Use time base code flushing instead of age Use a progress bar to scan large objects in increments when incremental marking is active force incremental marking for small heaps and run it more often force marking at random points between and force scavenge at random points between and reclaim otherwise unreachable unmodified wrapper objects when possible less compaction in non memory reducing mode use high priority threads for concurrent Marking Test mode only flag It allows an unit test to select evacuation candidates use incremental marking for CppHeap cppheap_concurrent_marking c value for membalancer A special constant to balance between memory and space tradeoff The smaller the more memory it uses enable use of SSE4 instructions if available enable use of AVX VNNI instructions if available enable use of POPCNT instruction if available force all emitted branches to be in long mode(MIPS/PPC only)") DEFINE_BOOL(partial_constant_pool
int32_t offset
Instruction * instr
LiftoffRegister reg
int pc_offset
int x
int position
Definition liveedit.cc:290
int m
Definition mul-fft.cc:294
STL namespace.
constexpr Register no_reg
V8_INLINE IndirectHandle< T > handle(Tagged< T > object, Isolate *isolate)
Definition handles-inl.h:72
constexpr uint8_t kPcLoadDelta
std::variant< Zone *, AccountingAllocator * > MaybeAssemblerZone
Definition assembler.h:262
const int kSmiTagSize
Definition v8-internal.h:87
constexpr uint64_t kSmiShiftMask
Address Tagged_t
Definition globals.h:547
constexpr int L
constexpr int kSmiShift
static Instr SetHi20Offset(int32_t hi20, Instr instr)
const int kSmiShiftSize
constexpr uint8_t kInstrSize
constexpr int kRvvVLEN
static Instr SetLo12Offset(int32_t lo12, Instr instr)
#define NOP(...)
#define DCHECK_LE(v1, v2)
Definition logging.h:490
#define DCHECK_NOT_NULL(val)
Definition logging.h:492
#define DCHECK_IMPLIES(v1, v2)
Definition logging.h:493
#define DCHECK_NE(v1, v2)
Definition logging.h:486
#define DCHECK_GE(v1, v2)
Definition logging.h:488
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
#define V8_EXPORT_PRIVATE
Definition macros.h:460
HeapNumberRequest heap_number_request
#define V8_INLINE
Definition v8config.h:500
#define V8_NODISCARD
Definition v8config.h:693