1 // Copyright 2013 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 #ifndef V8_CRANKSHAFT_ARM64_DELAYED_MASM_ARM64_H_ 6 #define V8_CRANKSHAFT_ARM64_DELAYED_MASM_ARM64_H_ 7 8 #include "src/crankshaft/lithium.h" 9 10 namespace v8 { 11 namespace internal { 12 13 class LCodeGen; 14 15 // This class delays the generation of some instructions. This way, we have a 16 // chance to merge two instructions in one (with load/store pair). 17 // Each instruction must either: 18 // - merge with the pending instruction and generate just one instruction. 19 // - emit the pending instruction and then generate the instruction (or set the 20 // pending instruction). 21 class DelayedMasm BASE_EMBEDDED { 22 public: 23 DelayedMasm(LCodeGen* owner, 24 MacroAssembler* masm, 25 const Register& scratch_register) 26 : cgen_(owner), masm_(masm), scratch_register_(scratch_register), 27 scratch_register_used_(false), pending_(kNone), saved_value_(0) { 28 #ifdef DEBUG 29 pending_register_ = no_reg; 30 pending_value_ = 0; 31 pending_pc_ = 0; 32 scratch_register_acquired_ = false; 33 #endif 34 } 35 ~DelayedMasm() { 36 DCHECK(!scratch_register_acquired_); 37 DCHECK(!scratch_register_used_); 38 DCHECK(!pending()); 39 } 40 inline void EndDelayedUse(); 41 42 const Register& ScratchRegister() { 43 scratch_register_used_ = true; 44 return scratch_register_; 45 } 46 bool IsScratchRegister(const CPURegister& reg) { 47 return reg.Is(scratch_register_); 48 } 49 bool scratch_register_used() const { return scratch_register_used_; } 50 void reset_scratch_register_used() { scratch_register_used_ = false; } 51 // Acquire/Release scratch register for use outside this class. 52 void AcquireScratchRegister() { 53 EmitPending(); 54 ResetSavedValue(); 55 #ifdef DEBUG 56 DCHECK(!scratch_register_acquired_); 57 scratch_register_acquired_ = true; 58 #endif 59 } 60 void ReleaseScratchRegister() { 61 #ifdef DEBUG 62 DCHECK(scratch_register_acquired_); 63 scratch_register_acquired_ = false; 64 #endif 65 } 66 bool pending() { return pending_ != kNone; } 67 68 // Extra layer over the macro-assembler instructions (which emits the 69 // potential pending instruction). 70 inline void Mov(const Register& rd, 71 const Operand& operand, 72 DiscardMoveMode discard_mode = kDontDiscardForSameWReg); 73 inline void Fmov(FPRegister fd, FPRegister fn); 74 inline void Fmov(FPRegister fd, double imm); 75 inline void LoadObject(Register result, Handle<Object> object); 76 // Instructions which try to merge which the pending instructions. 77 void StackSlotMove(LOperand* src, LOperand* dst); 78 // StoreConstant can only be used if the scratch register is not acquired. 79 void StoreConstant(uint64_t value, const MemOperand& operand); 80 void Load(const CPURegister& rd, const MemOperand& operand); 81 void Store(const CPURegister& rd, const MemOperand& operand); 82 // Emit the potential pending instruction. 83 void EmitPending(); 84 // Reset the pending state. 85 void ResetPending() { 86 pending_ = kNone; 87 #ifdef DEBUG 88 pending_register_ = no_reg; 89 MemOperand tmp; 90 pending_address_src_ = tmp; 91 pending_address_dst_ = tmp; 92 pending_value_ = 0; 93 pending_pc_ = 0; 94 #endif 95 } 96 void InitializeRootRegister() { 97 masm_->InitializeRootRegister(); 98 } 99 100 private: 101 // Set the saved value and load the ScratchRegister with it. 102 void SetSavedValue(uint64_t saved_value) { 103 DCHECK(saved_value != 0); 104 if (saved_value_ != saved_value) { 105 masm_->Mov(ScratchRegister(), saved_value); 106 saved_value_ = saved_value; 107 } 108 } 109 // Reset the saved value (i.e. the value of ScratchRegister is no longer 110 // known). 111 void ResetSavedValue() { 112 saved_value_ = 0; 113 } 114 115 LCodeGen* cgen_; 116 MacroAssembler* masm_; 117 118 // Register used to store a constant. 119 Register scratch_register_; 120 bool scratch_register_used_; 121 122 // Sometimes we store or load two values in two contiguous stack slots. 123 // In this case, we try to use the ldp/stp instructions to reduce code size. 124 // To be able to do that, instead of generating directly the instructions, 125 // we register with the following fields that an instruction needs to be 126 // generated. Then with the next instruction, if the instruction is 127 // consistent with the pending one for stp/ldp we generate ldp/stp. Else, 128 // if they are not consistent, we generate the pending instruction and we 129 // register the new instruction (which becomes pending). 130 131 // Enumeration of instructions which can be pending. 132 enum Pending { 133 kNone, 134 kStoreConstant, 135 kLoad, kStore, 136 kStackSlotMove 137 }; 138 // The pending instruction. 139 Pending pending_; 140 // For kLoad, kStore: register which must be loaded/stored. 141 CPURegister pending_register_; 142 // For kLoad, kStackSlotMove: address of the load. 143 MemOperand pending_address_src_; 144 // For kStoreConstant, kStore, kStackSlotMove: address of the store. 145 MemOperand pending_address_dst_; 146 // For kStoreConstant: value to be stored. 147 uint64_t pending_value_; 148 // Value held into the ScratchRegister if the saved_value_ is not 0. 149 // For 0, we use xzr. 150 uint64_t saved_value_; 151 #ifdef DEBUG 152 // Address where the pending instruction must be generated. It's only used to 153 // check that nothing else has been generated since we set the pending 154 // instruction. 155 int pending_pc_; 156 // If true, the scratch register has been acquired outside this class. The 157 // scratch register can no longer be used for constants. 158 bool scratch_register_acquired_; 159 #endif 160 }; 161 162 } // namespace internal 163 } // namespace v8 164 165 #endif // V8_CRANKSHAFT_ARM64_DELAYED_MASM_ARM64_H_ 166