// Copyright 2013 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef V8_CRANKSHAFT_ARM64_DELAYED_MASM_ARM64_H_ #define V8_CRANKSHAFT_ARM64_DELAYED_MASM_ARM64_H_ #include "src/crankshaft/lithium.h" namespace v8 { namespace internal { class LCodeGen; // This class delays the generation of some instructions. This way, we have a // chance to merge two instructions in one (with load/store pair). // Each instruction must either: // - merge with the pending instruction and generate just one instruction. // - emit the pending instruction and then generate the instruction (or set the // pending instruction). class DelayedMasm BASE_EMBEDDED { public: DelayedMasm(LCodeGen* owner, MacroAssembler* masm, const Register& scratch_register) : cgen_(owner), masm_(masm), scratch_register_(scratch_register), scratch_register_used_(false), pending_(kNone), saved_value_(0) { #ifdef DEBUG pending_register_ = no_reg; pending_value_ = 0; pending_pc_ = 0; scratch_register_acquired_ = false; #endif } ~DelayedMasm() { DCHECK(!scratch_register_acquired_); DCHECK(!scratch_register_used_); DCHECK(!pending()); } inline void EndDelayedUse(); const Register& ScratchRegister() { scratch_register_used_ = true; return scratch_register_; } bool IsScratchRegister(const CPURegister& reg) { return reg.Is(scratch_register_); } bool scratch_register_used() const { return scratch_register_used_; } void reset_scratch_register_used() { scratch_register_used_ = false; } // Acquire/Release scratch register for use outside this class. void AcquireScratchRegister() { EmitPending(); ResetSavedValue(); #ifdef DEBUG DCHECK(!scratch_register_acquired_); scratch_register_acquired_ = true; #endif } void ReleaseScratchRegister() { #ifdef DEBUG DCHECK(scratch_register_acquired_); scratch_register_acquired_ = false; #endif } bool pending() { return pending_ != kNone; } // Extra layer over the macro-assembler instructions (which emits the // potential pending instruction). inline void Mov(const Register& rd, const Operand& operand, DiscardMoveMode discard_mode = kDontDiscardForSameWReg); inline void Fmov(FPRegister fd, FPRegister fn); inline void Fmov(FPRegister fd, double imm); inline void LoadObject(Register result, Handle<Object> object); // Instructions which try to merge which the pending instructions. void StackSlotMove(LOperand* src, LOperand* dst); // StoreConstant can only be used if the scratch register is not acquired. void StoreConstant(uint64_t value, const MemOperand& operand); void Load(const CPURegister& rd, const MemOperand& operand); void Store(const CPURegister& rd, const MemOperand& operand); // Emit the potential pending instruction. void EmitPending(); // Reset the pending state. void ResetPending() { pending_ = kNone; #ifdef DEBUG pending_register_ = no_reg; MemOperand tmp; pending_address_src_ = tmp; pending_address_dst_ = tmp; pending_value_ = 0; pending_pc_ = 0; #endif } void InitializeRootRegister() { masm_->InitializeRootRegister(); } private: // Set the saved value and load the ScratchRegister with it. void SetSavedValue(uint64_t saved_value) { DCHECK(saved_value != 0); if (saved_value_ != saved_value) { masm_->Mov(ScratchRegister(), saved_value); saved_value_ = saved_value; } } // Reset the saved value (i.e. the value of ScratchRegister is no longer // known). void ResetSavedValue() { saved_value_ = 0; } LCodeGen* cgen_; MacroAssembler* masm_; // Register used to store a constant. Register scratch_register_; bool scratch_register_used_; // Sometimes we store or load two values in two contiguous stack slots. // In this case, we try to use the ldp/stp instructions to reduce code size. // To be able to do that, instead of generating directly the instructions, // we register with the following fields that an instruction needs to be // generated. Then with the next instruction, if the instruction is // consistent with the pending one for stp/ldp we generate ldp/stp. Else, // if they are not consistent, we generate the pending instruction and we // register the new instruction (which becomes pending). // Enumeration of instructions which can be pending. enum Pending { kNone, kStoreConstant, kLoad, kStore, kStackSlotMove }; // The pending instruction. Pending pending_; // For kLoad, kStore: register which must be loaded/stored. CPURegister pending_register_; // For kLoad, kStackSlotMove: address of the load. MemOperand pending_address_src_; // For kStoreConstant, kStore, kStackSlotMove: address of the store. MemOperand pending_address_dst_; // For kStoreConstant: value to be stored. uint64_t pending_value_; // Value held into the ScratchRegister if the saved_value_ is not 0. // For 0, we use xzr. uint64_t saved_value_; #ifdef DEBUG // Address where the pending instruction must be generated. It's only used to // check that nothing else has been generated since we set the pending // instruction. int pending_pc_; // If true, the scratch register has been acquired outside this class. The // scratch register can no longer be used for constants. bool scratch_register_acquired_; #endif }; } // namespace internal } // namespace v8 #endif // V8_CRANKSHAFT_ARM64_DELAYED_MASM_ARM64_H_