Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 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_ |