// Copyright (c) 1994-2006 Sun Microsystems Inc. // All Rights Reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions // are met: // // - Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // // - Redistribution in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the // distribution. // // - Neither the name of Sun Microsystems or the names of contributors may // be used to endorse or promote products derived from this software without // specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE // COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED // OF THE POSSIBILITY OF SUCH DAMAGE. // The original source code covered by the above license above has been // modified significantly by Google Inc. // Copyright 2014 the V8 project authors. All rights reserved. // A light-weight S390 Assembler // Generates user mode instructions for z/Architecture #ifndef V8_S390_ASSEMBLER_S390_H_ #define V8_S390_ASSEMBLER_S390_H_ #include <stdio.h> #if V8_HOST_ARCH_S390 // elf.h include is required for auxv check for STFLE facility used // for hardware detection, which is sensible only on s390 hosts. #include <elf.h> #endif #include <fcntl.h> #include <unistd.h> #include "src/assembler.h" #include "src/s390/constants-s390.h" #define ABI_USES_FUNCTION_DESCRIPTORS 0 #define ABI_PASSES_HANDLES_IN_REGS 1 // ObjectPair is defined under runtime/runtime-util.h. // On 31-bit, ObjectPair == uint64_t. ABI dictates long long // be returned with the lower addressed half in r2 // and the higher addressed half in r3. (Returns in Regs) // On 64-bit, ObjectPair is a Struct. ABI dictaes Structs be // returned in a storage buffer allocated by the caller, // with the address of this buffer passed as a hidden // argument in r2. (Does NOT return in Regs) // For x86 linux, ObjectPair is returned in registers. #if V8_TARGET_ARCH_S390X #define ABI_RETURNS_OBJECTPAIR_IN_REGS 0 #else #define ABI_RETURNS_OBJECTPAIR_IN_REGS 1 #endif #define ABI_CALL_VIA_IP 1 #define INSTR_AND_DATA_CACHE_COHERENCY LWSYNC namespace v8 { namespace internal { // clang-format off #define GENERAL_REGISTERS(V) \ V(r0) V(r1) V(r2) V(r3) V(r4) V(r5) V(r6) V(r7) \ V(r8) V(r9) V(r10) V(fp) V(ip) V(r13) V(r14) V(sp) #define ALLOCATABLE_GENERAL_REGISTERS(V) \ V(r2) V(r3) V(r4) V(r5) V(r6) V(r7) \ V(r8) V(r9) V(r13) #define DOUBLE_REGISTERS(V) \ V(d0) V(d1) V(d2) V(d3) V(d4) V(d5) V(d6) V(d7) \ V(d8) V(d9) V(d10) V(d11) V(d12) V(d13) V(d14) V(d15) #define FLOAT_REGISTERS DOUBLE_REGISTERS #define SIMD128_REGISTERS DOUBLE_REGISTERS #define ALLOCATABLE_DOUBLE_REGISTERS(V) \ V(d1) V(d2) V(d3) V(d4) V(d5) V(d6) V(d7) \ V(d8) V(d9) V(d10) V(d11) V(d12) V(d15) V(d0) // clang-format on // CPU Registers. // // 1) We would prefer to use an enum, but enum values are assignment- // compatible with int, which has caused code-generation bugs. // // 2) We would prefer to use a class instead of a struct but we don't like // the register initialization to depend on the particular initialization // order (which appears to be different on OS X, Linux, and Windows for the // installed versions of C++ we tried). Using a struct permits C-style // "initialization". Also, the Register objects cannot be const as this // forces initialization stubs in MSVC, making us dependent on initialization // order. // // 3) By not using an enum, we are possibly preventing the compiler from // doing certain constant folds, which may significantly reduce the // code generated for some assembly instructions (because they boil down // to a few constants). If this is a problem, we could change the code // such that we use an enum in optimized mode, and the struct in debug // mode. This way we get the compile-time error checking in debug mode // and best performance in optimized code. struct Register { enum Code { #define REGISTER_CODE(R) kCode_##R, GENERAL_REGISTERS(REGISTER_CODE) #undef REGISTER_CODE kAfterLast, kCode_no_reg = -1 }; static const int kNumRegisters = Code::kAfterLast; #define REGISTER_COUNT(R) 1 + static const int kNumAllocatable = ALLOCATABLE_GENERAL_REGISTERS(REGISTER_COUNT) 0; #undef REGISTER_COUNT #define REGISTER_BIT(R) 1 << kCode_##R | static const RegList kAllocatable = ALLOCATABLE_GENERAL_REGISTERS(REGISTER_BIT) 0; #undef REGISTER_BIT static Register from_code(int code) { DCHECK(code >= 0); DCHECK(code < kNumRegisters); Register r = {code}; return r; } bool is_valid() const { return 0 <= reg_code && reg_code < kNumRegisters; } bool is(Register reg) const { return reg_code == reg.reg_code; } int code() const { DCHECK(is_valid()); return reg_code; } int bit() const { DCHECK(is_valid()); return 1 << reg_code; } void set_code(int code) { reg_code = code; DCHECK(is_valid()); } #if V8_TARGET_LITTLE_ENDIAN static const int kMantissaOffset = 0; static const int kExponentOffset = 4; #else static const int kMantissaOffset = 4; static const int kExponentOffset = 0; #endif // Unfortunately we can't make this private in a struct. int reg_code; }; typedef struct Register Register; #define DECLARE_REGISTER(R) const Register R = {Register::kCode_##R}; GENERAL_REGISTERS(DECLARE_REGISTER) #undef DECLARE_REGISTER const Register no_reg = {Register::kCode_no_reg}; // Register aliases const Register kLithiumScratch = r1; // lithium scratch. const Register kRootRegister = r10; // Roots array pointer. const Register cp = r13; // JavaScript context pointer. static const bool kSimpleFPAliasing = true; static const bool kSimdMaskRegisters = false; // Double word FP register. struct DoubleRegister { enum Code { #define REGISTER_CODE(R) kCode_##R, DOUBLE_REGISTERS(REGISTER_CODE) #undef REGISTER_CODE kAfterLast, kCode_no_reg = -1 }; static const int kNumRegisters = Code::kAfterLast; static const int kMaxNumRegisters = kNumRegisters; bool is_valid() const { return 0 <= reg_code && reg_code < kNumRegisters; } bool is(DoubleRegister reg) const { return reg_code == reg.reg_code; } int code() const { DCHECK(is_valid()); return reg_code; } int bit() const { DCHECK(is_valid()); return 1 << reg_code; } static DoubleRegister from_code(int code) { DoubleRegister r = {code}; return r; } int reg_code; }; typedef DoubleRegister FloatRegister; // TODO(john.yan) Define SIMD registers. typedef DoubleRegister Simd128Register; #define DECLARE_REGISTER(R) \ const DoubleRegister R = {DoubleRegister::kCode_##R}; DOUBLE_REGISTERS(DECLARE_REGISTER) #undef DECLARE_REGISTER const Register no_dreg = {Register::kCode_no_reg}; // Aliases for double registers. Defined using #define instead of // "static const DoubleRegister&" because Clang complains otherwise when a // compilation unit that includes this header doesn't use the variables. #define kDoubleRegZero d14 #define kScratchDoubleReg d13 Register ToRegister(int num); // Coprocessor register struct CRegister { bool is_valid() const { return 0 <= reg_code && reg_code < 8; } bool is(CRegister creg) const { return reg_code == creg.reg_code; } int code() const { DCHECK(is_valid()); return reg_code; } int bit() const { DCHECK(is_valid()); return 1 << reg_code; } // Unfortunately we can't make this private in a struct. int reg_code; }; const CRegister no_creg = {-1}; const CRegister cr0 = {0}; const CRegister cr1 = {1}; const CRegister cr2 = {2}; const CRegister cr3 = {3}; const CRegister cr4 = {4}; const CRegister cr5 = {5}; const CRegister cr6 = {6}; const CRegister cr7 = {7}; // ----------------------------------------------------------------------------- // Machine instruction Operands #if V8_TARGET_ARCH_S390X const RelocInfo::Mode kRelocInfo_NONEPTR = RelocInfo::NONE64; #else const RelocInfo::Mode kRelocInfo_NONEPTR = RelocInfo::NONE32; #endif // Class Operand represents a shifter operand in data processing instructions // defining immediate numbers and masks typedef uint8_t Length; struct Mask { uint8_t mask; uint8_t value() { return mask; } static Mask from_value(uint8_t input) { DCHECK(input <= 0x0F); Mask m = {input}; return m; } }; class Operand BASE_EMBEDDED { public: // immediate INLINE(explicit Operand(intptr_t immediate, RelocInfo::Mode rmode = kRelocInfo_NONEPTR)); INLINE(static Operand Zero()) { return Operand(static_cast<intptr_t>(0)); } INLINE(explicit Operand(const ExternalReference& f)); explicit Operand(Handle<Object> handle); INLINE(explicit Operand(Smi* value)); // rm INLINE(explicit Operand(Register rm)); // Return true if this is a register operand. INLINE(bool is_reg() const); bool must_output_reloc_info(const Assembler* assembler) const; inline intptr_t immediate() const { DCHECK(!rm_.is_valid()); return imm_; } inline void setBits(int n) { imm_ = (static_cast<uint32_t>(imm_) << (32 - n)) >> (32 - n); } Register rm() const { return rm_; } private: Register rm_; intptr_t imm_; // valid if rm_ == no_reg RelocInfo::Mode rmode_; friend class Assembler; friend class MacroAssembler; }; typedef int32_t Disp; // Class MemOperand represents a memory operand in load and store instructions // On S390, we have various flavours of memory operands: // 1) a base register + 16 bit unsigned displacement // 2) a base register + index register + 16 bit unsigned displacement // 3) a base register + index register + 20 bit signed displacement class MemOperand BASE_EMBEDDED { public: explicit MemOperand(Register rx, Disp offset = 0); explicit MemOperand(Register rx, Register rb, Disp offset = 0); int32_t offset() const { return offset_; } uint32_t getDisplacement() const { return offset(); } // Base register Register rb() const { DCHECK(!baseRegister.is(no_reg)); return baseRegister; } Register getBaseRegister() const { return rb(); } // Index Register Register rx() const { DCHECK(!indexRegister.is(no_reg)); return indexRegister; } Register getIndexRegister() const { return rx(); } private: Register baseRegister; // base Register indexRegister; // index int32_t offset_; // offset friend class Assembler; }; class DeferredRelocInfo { public: DeferredRelocInfo() {} DeferredRelocInfo(int position, RelocInfo::Mode rmode, intptr_t data) : position_(position), rmode_(rmode), data_(data) {} int position() const { return position_; } RelocInfo::Mode rmode() const { return rmode_; } intptr_t data() const { return data_; } private: int position_; RelocInfo::Mode rmode_; intptr_t data_; }; class Assembler : public AssemblerBase { public: // Create an assembler. Instructions and relocation information are emitted // into a buffer, with the instructions starting from the beginning and the // relocation information starting from the end of the buffer. See CodeDesc // for a detailed comment on the layout (globals.h). // // If the provided buffer is NULL, the assembler allocates and grows its own // buffer, and buffer_size determines the initial buffer size. The buffer is // owned by the assembler and deallocated upon destruction of the assembler. // // If the provided buffer is not NULL, the assembler uses the provided buffer // for code generation and assumes its size to be buffer_size. If the buffer // is too small, a fatal error occurs. No deallocation of the buffer is done // upon destruction of the assembler. Assembler(Isolate* isolate, void* buffer, int buffer_size); virtual ~Assembler() {} // GetCode emits any pending (non-emitted) code and fills the descriptor // desc. GetCode() is idempotent; it returns the same result if no other // Assembler functions are invoked in between GetCode() calls. void GetCode(CodeDesc* desc); // Label operations & relative jumps (PPUM Appendix D) // // Takes a branch opcode (cc) and a label (L) and generates // either a backward branch or a forward branch and links it // to the label fixup chain. Usage: // // Label L; // unbound label // j(cc, &L); // forward branch to unbound label // bind(&L); // bind label to the current pc // j(cc, &L); // backward branch to bound label // bind(&L); // illegal: a label may be bound only once // // Note: The same Label can be used for forward and backward branches // but it may be bound only once. void bind(Label* L); // binds an unbound label L to the current code position // Links a label at the current pc_offset(). If already bound, returns the // bound position. If already linked, returns the position of the prior link. // Otherwise, returns the current pc_offset(). int link(Label* L); // Determines if Label is bound and near enough so that a single // branch instruction can be used to reach it. bool is_near(Label* L, Condition cond); // Returns the branch offset to the given label from the current code position // Links the label to the current position if it is still unbound int branch_offset(Label* L) { return link(L) - pc_offset(); } // Puts a labels target address at the given position. // The high 8 bits are set to zero. void label_at_put(Label* L, int at_offset); void load_label_offset(Register r1, Label* L); // Read/Modify the code target address in the branch/call instruction at pc. INLINE(static Address target_address_at(Address pc, Address constant_pool)); INLINE(static void set_target_address_at( Isolate* isolate, Address pc, Address constant_pool, Address target, ICacheFlushMode icache_flush_mode = FLUSH_ICACHE_IF_NEEDED)); INLINE(static Address target_address_at(Address pc, Code* code)); INLINE(static void set_target_address_at( Isolate* isolate, Address pc, Code* code, Address target, ICacheFlushMode icache_flush_mode = FLUSH_ICACHE_IF_NEEDED)); // Return the code target address at a call site from the return address // of that call in the instruction stream. inline static Address target_address_from_return_address(Address pc); // Given the address of the beginning of a call, return the address // in the instruction stream that the call will return to. INLINE(static Address return_address_from_call_start(Address pc)); inline Handle<Object> code_target_object_handle_at(Address pc); // This sets the branch destination. // This is for calls and branches within generated code. inline static void deserialization_set_special_target_at( Isolate* isolate, Address instruction_payload, Code* code, Address target); // This sets the internal reference at the pc. inline static void deserialization_set_target_internal_reference_at( Isolate* isolate, Address pc, Address target, RelocInfo::Mode mode = RelocInfo::INTERNAL_REFERENCE); // Here we are patching the address in the IIHF/IILF instruction pair. // These values are used in the serialization process and must be zero for // S390 platform, as Code, Embedded Object or External-reference pointers // are split across two consecutive instructions and don't exist separately // in the code, so the serializer should not step forwards in memory after // a target is resolved and written. static const int kSpecialTargetSize = 0; // Number of bytes for instructions used to store pointer sized constant. #if V8_TARGET_ARCH_S390X static const int kBytesForPtrConstant = 12; // IIHF + IILF #else static const int kBytesForPtrConstant = 6; // IILF #endif // Distance between the instruction referring to the address of the call // target and the return address. // Offset between call target address and return address // for BRASL calls // Patch will be appiled to other FIXED_SEQUENCE call static const int kCallTargetAddressOffset = 6; // The length of FIXED_SEQUENCE call // iihf r8, <address_hi> // <64-bit only> // iilf r8, <address_lo> // basr r14, r8 #if V8_TARGET_ARCH_S390X static const int kCallSequenceLength = 14; #else static const int kCallSequenceLength = 8; #endif // This is the length of the BreakLocationIterator::SetDebugBreakAtReturn() // code patch FIXED_SEQUENCE in bytes! // JS Return Sequence = Call Sequence + BKPT // static const int kJSReturnSequenceLength = kCallSequenceLength + 2; // This is the length of the code sequence from SetDebugBreakAtSlot() // FIXED_SEQUENCE in bytes! static const int kDebugBreakSlotLength = kCallSequenceLength; static const int kPatchDebugBreakSlotReturnOffset = kCallTargetAddressOffset; // Length to patch between the start of the JS return sequence // from SetDebugBreakAtReturn and the address from // break_address_from_return_address. // // frame->pc() in Debug::SetAfterBreakTarget will point to BKPT in // JS return sequence, so the length to patch will not include BKPT // instruction length. // static const int kPatchReturnSequenceAddressOffset = // kCallSequenceLength - kPatchDebugBreakSlotReturnOffset; // Length to patch between the start of the FIXED call sequence from // SetDebugBreakAtSlot() and the the address from // break_address_from_return_address. static const int kPatchDebugBreakSlotAddressOffset = kDebugBreakSlotLength - kPatchDebugBreakSlotReturnOffset; static inline int encode_crbit(const CRegister& cr, enum CRBit crbit) { return ((cr.code() * CRWIDTH) + crbit); } // --------------------------------------------------------------------------- // Code generation template <class T, int size, int lo, int hi> inline T getfield(T value) { DCHECK(lo < hi); DCHECK(size > 0); int mask = hi - lo; int shift = size * 8 - hi; uint32_t mask_value = (mask == 32) ? 0xffffffff : (1 << mask) - 1; return (value & mask_value) << shift; } // Declare generic instruction formats by fields inline void e_format(Opcode opcode) { emit2bytes(getfield<uint16_t, 2, 0, 16>(opcode)); } inline void i_format(Opcode opcode, int f1) { emit2bytes(getfield<uint16_t, 2, 0, 8>(opcode) | getfield<uint16_t, 2, 8, 16>(f1)); } inline void ie_format(Opcode opcode, int f1, int f2) { emit4bytes(getfield<uint32_t, 4, 0, 16>(opcode) | getfield<uint32_t, 4, 24, 28>(f1) | getfield<uint32_t, 4, 28, 32>(f2)); } inline void mii_format(Opcode opcode, int f1, int f2, int f3) { emit6bytes( getfield<uint64_t, 6, 0, 8>(opcode) | getfield<uint64_t, 6, 8, 12>(f1) | getfield<uint64_t, 6, 12, 24>(f2) | getfield<uint64_t, 6, 24, 48>(f3)); } inline void ri_format(Opcode opcode, int f1, int f2) { uint32_t op1 = opcode >> 4; uint32_t op2 = opcode & 0xf; emit4bytes( getfield<uint32_t, 4, 0, 8>(op1) | getfield<uint32_t, 4, 8, 12>(f1) | getfield<uint32_t, 4, 12, 16>(op2) | getfield<uint32_t, 4, 16, 32>(f2)); } inline void rie_1_format(Opcode opcode, int f1, int f2, int f3, int f4) { uint32_t op1 = opcode >> 8; uint32_t op2 = opcode & 0xff; emit6bytes( getfield<uint64_t, 6, 0, 8>(op1) | getfield<uint64_t, 6, 8, 12>(f1) | getfield<uint64_t, 6, 12, 16>(f2) | getfield<uint64_t, 6, 16, 32>(f3) | getfield<uint64_t, 6, 32, 36>(f4) | getfield<uint64_t, 6, 40, 48>(op2)); } inline void rie_2_format(Opcode opcode, int f1, int f2, int f3, int f4) { uint32_t op1 = opcode >> 8; uint32_t op2 = opcode & 0xff; emit6bytes( getfield<uint64_t, 6, 0, 8>(op1) | getfield<uint64_t, 6, 8, 12>(f1) | getfield<uint64_t, 6, 12, 16>(f2) | getfield<uint64_t, 6, 16, 32>(f3) | getfield<uint64_t, 6, 32, 40>(f4) | getfield<uint64_t, 6, 40, 48>(op2)); } inline void rie_3_format(Opcode opcode, int f1, int f2, int f3, int f4, int f5) { uint32_t op1 = opcode >> 8; uint32_t op2 = opcode & 0xff; emit6bytes( getfield<uint64_t, 6, 0, 8>(op1) | getfield<uint64_t, 6, 8, 12>(f1) | getfield<uint64_t, 6, 12, 16>(f2) | getfield<uint64_t, 6, 16, 24>(f3) | getfield<uint64_t, 6, 24, 32>(f4) | getfield<uint64_t, 6, 32, 40>(f5) | getfield<uint64_t, 6, 40, 48>(op2)); } #define DECLARE_S390_RIL_AB_INSTRUCTIONS(name, op_name, op_value) \ template <class R1> \ inline void name(R1 r1, const Operand& i2) { \ ril_format(op_name, r1.code(), i2.immediate()); \ } #define DECLARE_S390_RIL_C_INSTRUCTIONS(name, op_name, op_value) \ inline void name(Condition m1, const Operand& i2) { \ ril_format(op_name, m1, i2.immediate()); \ } inline void ril_format(Opcode opcode, int f1, int f2) { uint32_t op1 = opcode >> 4; uint32_t op2 = opcode & 0xf; emit6bytes( getfield<uint64_t, 6, 0, 8>(op1) | getfield<uint64_t, 6, 8, 12>(f1) | getfield<uint64_t, 6, 12, 16>(op2) | getfield<uint64_t, 6, 16, 48>(f2)); } S390_RIL_A_OPCODE_LIST(DECLARE_S390_RIL_AB_INSTRUCTIONS) S390_RIL_B_OPCODE_LIST(DECLARE_S390_RIL_AB_INSTRUCTIONS) S390_RIL_C_OPCODE_LIST(DECLARE_S390_RIL_C_INSTRUCTIONS) #undef DECLARE_S390_RIL_AB_INSTRUCTIONS #undef DECLARE_S390_RIL_C_INSTRUCTIONS inline void ris_format(Opcode opcode, int f1, int f2, int f3, int f4, int f5) { uint32_t op1 = opcode >> 8; uint32_t op2 = opcode & 0xff; emit6bytes( getfield<uint64_t, 6, 0, 8>(op1) | getfield<uint64_t, 6, 8, 12>(f1) | getfield<uint64_t, 6, 12, 16>(f2) | getfield<uint64_t, 6, 16, 20>(f3) | getfield<uint64_t, 6, 20, 32>(f4) | getfield<uint64_t, 6, 32, 40>(f5) | getfield<uint64_t, 6, 40, 48>(op2)); } #define DECLARE_S390_RR_INSTRUCTIONS(name, op_name, op_value) \ inline void name(Register r1, Register r2) { \ rr_format(op_name, r1.code(), r2.code()); \ } \ inline void name(DoubleRegister r1, DoubleRegister r2) { \ rr_format(op_name, r1.code(), r2.code()); \ } \ inline void name(Condition m1, Register r2) { \ rr_format(op_name, m1, r2.code()); \ } inline void rr_format(Opcode opcode, int f1, int f2) { emit2bytes(getfield<uint16_t, 2, 0, 8>(opcode) | getfield<uint16_t, 2, 8, 12>(f1) | getfield<uint16_t, 2, 12, 16>(f2)); } S390_RR_OPCODE_LIST(DECLARE_S390_RR_INSTRUCTIONS) #undef DECLARE_S390_RR_INSTRUCTIONS #define DECLARE_S390_RRD_INSTRUCTIONS(name, op_name, op_value) \ template <class R1, class R2, class R3> \ inline void name(R1 r1, R3 r3, R2 r2) { \ rrd_format(op_name, r1.code(), r3.code(), r2.code()); \ } inline void rrd_format(Opcode opcode, int f1, int f2, int f3) { emit4bytes(getfield<uint32_t, 4, 0, 16>(opcode) | getfield<uint32_t, 4, 16, 20>(f1) | getfield<uint32_t, 4, 24, 28>(f2) | getfield<uint32_t, 4, 28, 32>(f3)); } S390_RRD_OPCODE_LIST(DECLARE_S390_RRD_INSTRUCTIONS) #undef DECLARE_S390_RRD_INSTRUCTIONS #define DECLARE_S390_RRE_INSTRUCTIONS(name, op_name, op_value) \ template <class R1, class R2> \ inline void name(R1 r1, R2 r2) { \ rre_format(op_name, r1.code(), r2.code()); \ } inline void rre_format(Opcode opcode, int f1, int f2) { emit4bytes(getfield<uint32_t, 4, 0, 16>(opcode) | getfield<uint32_t, 4, 24, 28>(f1) | getfield<uint32_t, 4, 28, 32>(f2)); } S390_RRE_OPCODE_LIST(DECLARE_S390_RRE_INSTRUCTIONS) // Special format void lzdr(DoubleRegister r1) { rre_format(LZDR, r1.code(), 0); } #undef DECLARE_S390_RRE_INSTRUCTIONS inline void rrf_format(Opcode opcode, int f1, int f2, int f3, int f4) { emit4bytes( getfield<uint32_t, 4, 0, 16>(opcode) | getfield<uint32_t, 4, 16, 20>(f1) | getfield<uint32_t, 4, 20, 24>(f2) | getfield<uint32_t, 4, 24, 28>(f3) | getfield<uint32_t, 4, 28, 32>(f4)); } #define DECLARE_S390_RX_INSTRUCTIONS(name, op_name, op_value) \ template <class R1> \ inline void name(R1 r1, Register x2, Register b2, Disp d2) { \ rx_format(op_name, r1.code(), x2.code(), b2.code(), d2); \ } \ template <class R1> \ inline void name(R1 r1, const MemOperand& opnd) { \ name(r1, opnd.getIndexRegister(), \ opnd.getBaseRegister(), opnd.getDisplacement()); \ } inline void rx_format(Opcode opcode, int f1, int f2, int f3, int f4) { DCHECK(is_uint8(opcode)); DCHECK(is_uint12(f4)); emit4bytes(getfield<uint32_t, 4, 0, 8>(opcode) | getfield<uint32_t, 4, 8, 12>(f1) | getfield<uint32_t, 4, 12, 16>(f2) | getfield<uint32_t, 4, 16, 20>(f3) | getfield<uint32_t, 4, 20, 32>(f4)); } S390_RX_A_OPCODE_LIST(DECLARE_S390_RX_INSTRUCTIONS) void bc(Condition cond, const MemOperand& opnd) { bc(cond, opnd.getIndexRegister(), opnd.getBaseRegister(), opnd.getDisplacement()); } void bc(Condition cond, Register x2, Register b2, Disp d2) { rx_format(BC, cond, x2.code(), b2.code(), d2); } #undef DECLARE_S390_RX_INSTRUCTIONS #define DECLARE_S390_RXY_INSTRUCTIONS(name, op_name, op_value) \ template <class R1, class R2> \ inline void name(R1 r1, R2 r2, Register b2, Disp d2) { \ rxy_format(op_name, r1.code(), r2.code(), b2.code(), d2); \ } \ template <class R1> \ inline void name(R1 r1, const MemOperand& opnd) { \ name(r1, opnd.getIndexRegister(), \ opnd.getBaseRegister(), opnd.getDisplacement()); \ } inline void rxy_format(Opcode opcode, int f1, int f2, int f3, int f4) { DCHECK(is_uint16(opcode)); DCHECK(is_int20(f4)); emit6bytes(getfield<uint64_t, 6, 0, 8>(opcode >> 8) | getfield<uint64_t, 6, 8, 12>(f1) | getfield<uint64_t, 6, 12, 16>(f2) | getfield<uint64_t, 6, 16, 20>(f3) | getfield<uint64_t, 6, 20, 32>(f4 & 0x0fff) | getfield<uint64_t, 6, 32, 40>(f4 >> 12) | getfield<uint64_t, 6, 40, 48>(opcode & 0x00ff)); } S390_RXY_A_OPCODE_LIST(DECLARE_S390_RXY_INSTRUCTIONS) void pfd(Condition cond, const MemOperand& opnd) { pfd(cond, opnd.getIndexRegister(), opnd.getBaseRegister(), opnd.getDisplacement()); } void pfd(Condition cond, Register x2, Register b2, Disp d2) { rxy_format(PFD, cond, x2.code(), b2.code(), d2); } #undef DECLARE_S390_RXY_INSTRUCTIONS // Helper for unconditional branch to Label with update to save register void b(Register r, Label* l) { int32_t halfwords = branch_offset(l) / 2; brasl(r, Operand(halfwords)); } // Conditional Branch Instruction - Generates either BRC / BRCL void branchOnCond(Condition c, int branch_offset, bool is_bound = false); // Helpers for conditional branch to Label void b(Condition cond, Label* l, Label::Distance dist = Label::kFar) { branchOnCond(cond, branch_offset(l), l->is_bound() || (dist == Label::kNear)); } void bc_short(Condition cond, Label* l, Label::Distance dist = Label::kFar) { b(cond, l, Label::kNear); } // Helpers for conditional branch to Label void beq(Label* l, Label::Distance dist = Label::kFar) { b(eq, l, dist); } void bne(Label* l, Label::Distance dist = Label::kFar) { b(ne, l, dist); } void blt(Label* l, Label::Distance dist = Label::kFar) { b(lt, l, dist); } void ble(Label* l, Label::Distance dist = Label::kFar) { b(le, l, dist); } void bgt(Label* l, Label::Distance dist = Label::kFar) { b(gt, l, dist); } void bge(Label* l, Label::Distance dist = Label::kFar) { b(ge, l, dist); } void b(Label* l, Label::Distance dist = Label::kFar) { b(al, l, dist); } void jmp(Label* l, Label::Distance dist = Label::kFar) { b(al, l, dist); } void bunordered(Label* l, Label::Distance dist = Label::kFar) { b(unordered, l, dist); } void bordered(Label* l, Label::Distance dist = Label::kFar) { b(ordered, l, dist); } // Helpers for conditional indirect branch off register void b(Condition cond, Register r) { bcr(cond, r); } void beq(Register r) { b(eq, r); } void bne(Register r) { b(ne, r); } void blt(Register r) { b(lt, r); } void ble(Register r) { b(le, r); } void bgt(Register r) { b(gt, r); } void bge(Register r) { b(ge, r); } void b(Register r) { b(al, r); } void jmp(Register r) { b(al, r); } void bunordered(Register r) { b(unordered, r); } void bordered(Register r) { b(ordered, r); } // --------------------------------------------------------------------------- // Code generation // Insert the smallest number of nop instructions // possible to align the pc offset to a multiple // of m. m must be a power of 2 (>= 4). void Align(int m); // Insert the smallest number of zero bytes possible to align the pc offset // to a mulitple of m. m must be a power of 2 (>= 2). void DataAlign(int m); // Aligns code to something that's optimal for a jump target for the platform. void CodeTargetAlign(); void breakpoint(bool do_print) { if (do_print) { PrintF("DebugBreak is inserted to %p\n", static_cast<void*>(pc_)); } #if V8_HOST_ARCH_64_BIT int64_t value = reinterpret_cast<uint64_t>(&v8::base::OS::DebugBreak); int32_t hi_32 = static_cast<int64_t>(value) >> 32; int32_t lo_32 = static_cast<int32_t>(value); iihf(r1, Operand(hi_32)); iilf(r1, Operand(lo_32)); #else iilf(r1, Operand(reinterpret_cast<uint32_t>(&v8::base::OS::DebugBreak))); #endif basr(r14, r1); } void call(Handle<Code> target, RelocInfo::Mode rmode, TypeFeedbackId ast_id = TypeFeedbackId::None()); void jump(Handle<Code> target, RelocInfo::Mode rmode, Condition cond); // S390 instruction generation #define I_FORM(name) void name(const Operand& i) #define RR_FORM(name) void name(Register r1, Register r2) #define RR2_FORM(name) void name(Condition m1, Register r2) #define RI1_FORM(name) void name(Register r, const Operand& i) #define RI2_FORM(name) void name(Condition m, const Operand& i) #define RIE_FORM(name) void name(Register r1, Register R3, const Operand& i) #define RIE_F_FORM(name) \ void name(Register r1, Register r2, const Operand& i3, const Operand& i4, \ const Operand& i5) #define RXE_FORM(name) \ void name(Register r1, const MemOperand& opnd); \ void name(Register r1, Register b2, Register x2, Disp d2) #define RXF_FORM(name) \ void name(Register r1, Register r3, const MemOperand& opnd); \ void name(Register r1, Register r3, Register b2, Register x2, Disp d2) #define RSI_FORM(name) void name(Register r1, Register r3, const Operand& i) #define RIS_FORM(name) \ void name(Register r1, Condition m3, Register b4, Disp d4, \ const Operand& i2); \ void name(Register r1, const Operand& i2, Condition m3, \ const MemOperand& opnd) #define SI_FORM(name) \ void name(const MemOperand& opnd, const Operand& i); \ void name(const Operand& i2, Register b1, Disp d1) #define SIL_FORM(name) \ void name(Register b1, Disp d1, const Operand& i2); \ void name(const MemOperand& opnd, const Operand& i2) #define RRF1_FORM(name) void name(Register r1, Register r2, Register r3) #define RRF2_FORM(name) void name(Condition m1, Register r1, Register r2) #define RRF3_FORM(name) \ void name(Register r3, Condition m4, Register r1, Register r2) #define RS1_FORM(name) \ void name(Register r1, Register r3, const MemOperand& opnd); \ void name(Register r1, Register r3, Register b2, Disp d2) #define RS2_FORM(name) \ void name(Register r1, Condition m3, const MemOperand& opnd); \ void name(Register r1, Condition m3, Register b2, Disp d2) #define RSE_FORM(name) \ void name(Register r1, Register r3, const MemOperand& opnd); \ void name(Register r1, Register r3, Register b2, Disp d2) #define RSL_FORM(name) \ void name(Length l, Register b2, Disp d2); \ void name(const MemOperand& opnd) #define RSY1_FORM(name) \ void name(Register r1, Register r3, Register b2, Disp d2); \ void name(Register r1, Register r3, const MemOperand& opnd) #define RSY2_FORM(name) \ void name(Register r1, Condition m3, Register b2, Disp d2); \ void name(Register r1, Condition m3, const MemOperand& opnd) #define RRS_FORM(name) \ void name(Register r1, Register r2, Register b4, Disp d4, Condition m3); \ void name(Register r1, Register r2, Condition m3, const MemOperand& opnd) #define S_FORM(name) \ void name(Register b2, Disp d2); \ void name(const MemOperand& opnd) #define SIY_FORM(name) \ void name(const Operand& i2, Register b1, Disp d1); \ void name(const MemOperand& opnd, const Operand& i) #define SS1_FORM(name) \ void name(Register b1, Disp d1, Register b3, Disp d2, Length length); \ void name(const MemOperand& opnd1, const MemOperand& opnd2, Length length) #define SS2_FORM(name) \ void name(const MemOperand& opnd1, const MemOperand& opnd2, Length length1, \ Length length2); \ void name(Register b1, Disp d1, Register b2, Disp d2, Length l1, Length l2) #define SS3_FORM(name) \ void name(const MemOperand& opnd1, const MemOperand& opnd2, Length length); \ void name(const Operand& i3, Register b1, Disp d1, Register b2, Disp d2, \ Length l1) #define SS4_FORM(name) \ void name(const MemOperand& opnd1, const MemOperand& opnd2); \ void name(Register r1, Register r3, Register b1, Disp d1, Register b2, \ Disp d2) #define SS5_FORM(name) \ void name(const MemOperand& opnd1, const MemOperand& opnd2); \ void name(Register r1, Register r3, Register b3, Disp d2, Register b4, \ Disp d4) #define SSE_FORM(name) \ void name(Register b1, Disp d1, Register b2, Disp d2); \ void name(const MemOperand& opnd1, const MemOperand& opnd2) #define SSF_FORM(name) \ void name(Register r3, Register b1, Disp d1, Register b2, Disp d2); \ void name(Register r3, const MemOperand& opnd1, const MemOperand& opnd2) #define DECLARE_VRR_A_INSTRUCTIONS(name, opcode_name, opcode_value) \ void name(DoubleRegister v1, DoubleRegister v2, Condition m5, Condition m4, \ Condition m3) { \ uint64_t code = (static_cast<uint64_t>(opcode_value & 0xFF00)) * B32 | \ (static_cast<uint64_t>(v1.code())) * B36 | \ (static_cast<uint64_t>(v2.code())) * B32 | \ (static_cast<uint64_t>(m5 & 0xF)) * B20 | \ (static_cast<uint64_t>(m4 & 0xF)) * B16 | \ (static_cast<uint64_t>(m3 & 0xF)) * B12 | \ (static_cast<uint64_t>(opcode_value & 0x00FF)); \ emit6bytes(code); \ } S390_VRR_A_OPCODE_LIST(DECLARE_VRR_A_INSTRUCTIONS) #undef DECLARE_VRR_A_INSTRUCTIONS #define DECLARE_VRR_C_INSTRUCTIONS(name, opcode_name, opcode_value) \ void name(DoubleRegister v1, DoubleRegister v2, DoubleRegister v3, \ Condition m6, Condition m5, Condition m4) { \ uint64_t code = (static_cast<uint64_t>(opcode_value & 0xFF00)) * B32 | \ (static_cast<uint64_t>(v1.code())) * B36 | \ (static_cast<uint64_t>(v2.code())) * B32 | \ (static_cast<uint64_t>(v3.code())) * B28 | \ (static_cast<uint64_t>(m6 & 0xF)) * B20 | \ (static_cast<uint64_t>(m5 & 0xF)) * B16 | \ (static_cast<uint64_t>(m4 & 0xF)) * B12 | \ (static_cast<uint64_t>(opcode_value & 0x00FF)); \ emit6bytes(code); \ } S390_VRR_C_OPCODE_LIST(DECLARE_VRR_C_INSTRUCTIONS) #undef DECLARE_VRR_C_INSTRUCTIONS // Single Element format void vfa(DoubleRegister v1, DoubleRegister v2, DoubleRegister v3) { vfa(v1, v2, v3, static_cast<Condition>(0), static_cast<Condition>(8), static_cast<Condition>(3)); } void vfs(DoubleRegister v1, DoubleRegister v2, DoubleRegister v3) { vfs(v1, v2, v3, static_cast<Condition>(0), static_cast<Condition>(8), static_cast<Condition>(3)); } void vfm(DoubleRegister v1, DoubleRegister v2, DoubleRegister v3) { vfm(v1, v2, v3, static_cast<Condition>(0), static_cast<Condition>(8), static_cast<Condition>(3)); } void vfd(DoubleRegister v1, DoubleRegister v2, DoubleRegister v3) { vfd(v1, v2, v3, static_cast<Condition>(0), static_cast<Condition>(8), static_cast<Condition>(3)); } // S390 instruction sets RXE_FORM(ddb); SS1_FORM(ed); RRF2_FORM(fidbr); RI1_FORM(iihh); RI1_FORM(iihl); RI1_FORM(iilh); RI1_FORM(iill); RSY1_FORM(loc); RXE_FORM(mdb); SS4_FORM(mvck); SSF_FORM(mvcos); SS4_FORM(mvcs); SS1_FORM(mvn); SS1_FORM(nc); SI_FORM(ni); RI1_FORM(nilh); RI1_FORM(nill); RI1_FORM(oill); RXE_FORM(sdb); RS1_FORM(srdl); RI1_FORM(tmll); SS1_FORM(tr); S_FORM(ts); // Load Address Instructions void larl(Register r, Label* l); // Load Instructions void lhi(Register r, const Operand& imm); void lghi(Register r, const Operand& imm); // Load Multiple Instructions void lm(Register r1, Register r2, const MemOperand& src); void lmy(Register r1, Register r2, const MemOperand& src); void lmg(Register r1, Register r2, const MemOperand& src); // Load On Condition Instructions void locr(Condition m3, Register r1, Register r2); void locgr(Condition m3, Register r1, Register r2); void loc(Condition m3, Register r1, const MemOperand& src); void locg(Condition m3, Register r1, const MemOperand& src); // Store Instructions // Store Multiple Instructions void stm(Register r1, Register r2, const MemOperand& src); void stmy(Register r1, Register r2, const MemOperand& src); void stmg(Register r1, Register r2, const MemOperand& src); // Compare Instructions void chi(Register r, const Operand& opnd); void cghi(Register r, const Operand& opnd); // Compare Logical Instructions void cli(const MemOperand& mem, const Operand& imm); void cliy(const MemOperand& mem, const Operand& imm); void clc(const MemOperand& opnd1, const MemOperand& opnd2, Length length); // Test Under Mask Instructions void tm(const MemOperand& mem, const Operand& imm); void tmy(const MemOperand& mem, const Operand& imm); // Rotate Instructions void rll(Register r1, Register r3, Register opnd); void rll(Register r1, Register r3, const Operand& opnd); void rll(Register r1, Register r3, Register r2, const Operand& opnd); void rllg(Register r1, Register r3, const Operand& opnd); void rllg(Register r1, Register r3, const Register opnd); void rllg(Register r1, Register r3, Register r2, const Operand& opnd); // Shift Instructions (32) void sll(Register r1, Register opnd); void sll(Register r1, const Operand& opnd); void sllk(Register r1, Register r3, Register opnd); void sllk(Register r1, Register r3, const Operand& opnd); void srl(Register r1, Register opnd); void srl(Register r1, const Operand& opnd); void srlk(Register r1, Register r3, Register opnd); void srlk(Register r1, Register r3, const Operand& opnd); void sra(Register r1, Register opnd); void sra(Register r1, const Operand& opnd); void srak(Register r1, Register r3, Register opnd); void srak(Register r1, Register r3, const Operand& opnd); void sla(Register r1, Register opnd); void sla(Register r1, const Operand& opnd); void slak(Register r1, Register r3, Register opnd); void slak(Register r1, Register r3, const Operand& opnd); // Shift Instructions (64) void sllg(Register r1, Register r3, const Operand& opnd); void sllg(Register r1, Register r3, const Register opnd); void srlg(Register r1, Register r3, const Operand& opnd); void srlg(Register r1, Register r3, const Register opnd); void srag(Register r1, Register r3, const Operand& opnd); void srag(Register r1, Register r3, const Register opnd); void srda(Register r1, const Operand& opnd); void srdl(Register r1, const Operand& opnd); void slag(Register r1, Register r3, const Operand& opnd); void slag(Register r1, Register r3, const Register opnd); void sldl(Register r1, Register b2, const Operand& opnd); void srdl(Register r1, Register b2, const Operand& opnd); void srda(Register r1, Register b2, const Operand& opnd); // Rotate and Insert Selected Bits void risbg(Register dst, Register src, const Operand& startBit, const Operand& endBit, const Operand& shiftAmt, bool zeroBits = true); void risbgn(Register dst, Register src, const Operand& startBit, const Operand& endBit, const Operand& shiftAmt, bool zeroBits = true); // Move Character (Mem to Mem) void mvc(const MemOperand& opnd1, const MemOperand& opnd2, uint32_t length); // Branch Instructions void bras(Register r, const Operand& opnd); void brc(Condition c, const Operand& opnd); void brct(Register r1, const Operand& opnd); void brctg(Register r1, const Operand& opnd); // 32-bit Add Instructions void ahi(Register r1, const Operand& opnd); void ahik(Register r1, Register r3, const Operand& opnd); void ark(Register r1, Register r2, Register r3); void asi(const MemOperand&, const Operand&); // 64-bit Add Instructions void aghi(Register r1, const Operand& opnd); void aghik(Register r1, Register r3, const Operand& opnd); void agrk(Register r1, Register r2, Register r3); void agsi(const MemOperand&, const Operand&); // 32-bit Add Logical Instructions void alrk(Register r1, Register r2, Register r3); // 64-bit Add Logical Instructions void algrk(Register r1, Register r2, Register r3); // 32-bit Subtract Instructions void srk(Register r1, Register r2, Register r3); // 64-bit Subtract Instructions void sgrk(Register r1, Register r2, Register r3); // 32-bit Subtract Logical Instructions void slrk(Register r1, Register r2, Register r3); // 64-bit Subtract Logical Instructions void slgrk(Register r1, Register r2, Register r3); // 32-bit Multiply Instructions void mhi(Register r1, const Operand& opnd); void msrkc(Register r1, Register r2, Register r3); void msgrkc(Register r1, Register r2, Register r3); // 64-bit Multiply Instructions void mghi(Register r1, const Operand& opnd); // Bitwise Instructions (AND / OR / XOR) void nrk(Register r1, Register r2, Register r3); void ngrk(Register r1, Register r2, Register r3); void ork(Register r1, Register r2, Register r3); void ogrk(Register r1, Register r2, Register r3); void xrk(Register r1, Register r2, Register r3); void xgrk(Register r1, Register r2, Register r3); void xc(const MemOperand& opnd1, const MemOperand& opnd2, Length length); // Floating <-> Fixed Point Conversion Instructions void cdlfbr(Condition m3, Condition m4, DoubleRegister fltReg, Register fixReg); void cdlgbr(Condition m3, Condition m4, DoubleRegister fltReg, Register fixReg); void celgbr(Condition m3, Condition m4, DoubleRegister fltReg, Register fixReg); void celfbr(Condition m3, Condition m4, DoubleRegister fltReg, Register fixReg); void clfdbr(Condition m3, Condition m4, Register fixReg, DoubleRegister fltReg); void clfebr(Condition m3, Condition m4, Register fixReg, DoubleRegister fltReg); void clgdbr(Condition m3, Condition m4, Register fixReg, DoubleRegister fltReg); void clgebr(Condition m3, Condition m4, Register fixReg, DoubleRegister fltReg); void cfdbr(Condition m, Register fixReg, DoubleRegister fltReg); void cgebr(Condition m, Register fixReg, DoubleRegister fltReg); void cgdbr(Condition m, Register fixReg, DoubleRegister fltReg); void cfebr(Condition m3, Register fixReg, DoubleRegister fltReg); void cefbr(Condition m3, DoubleRegister fltReg, Register fixReg); // Floating Point Compare Instructions void cdb(DoubleRegister r1, const MemOperand& opnd); void ceb(DoubleRegister r1, const MemOperand& opnd); // Floating Point Arithmetic Instructions void adb(DoubleRegister r1, const MemOperand& opnd); void sdb(DoubleRegister r1, const MemOperand& opnd); void mdb(DoubleRegister r1, const MemOperand& opnd); void ddb(DoubleRegister r1, const MemOperand& opnd); void sqdb(DoubleRegister r1, const MemOperand& opnd); void ldeb(DoubleRegister r1, const MemOperand& opnd); enum FIDBRA_MASK3 { FIDBRA_CURRENT_ROUNDING_MODE = 0, FIDBRA_ROUND_TO_NEAREST_AWAY_FROM_0 = 1, // ... FIDBRA_ROUND_TOWARD_0 = 5, FIDBRA_ROUND_TOWARD_POS_INF = 6, FIDBRA_ROUND_TOWARD_NEG_INF = 7 }; void fiebra(DoubleRegister d1, DoubleRegister d2, FIDBRA_MASK3 m3); void fidbra(DoubleRegister d1, DoubleRegister d2, FIDBRA_MASK3 m3); // Move integer void mvhi(const MemOperand& opnd1, const Operand& i2); void mvghi(const MemOperand& opnd1, const Operand& i2); // Exception-generating instructions and debugging support void stop(const char* msg, Condition cond = al, int32_t code = kDefaultStopCode, CRegister cr = cr7); void bkpt(uint32_t imm16); // v5 and above // Different nop operations are used by the code generator to detect certain // states of the generated code. enum NopMarkerTypes { NON_MARKING_NOP = 0, GROUP_ENDING_NOP, DEBUG_BREAK_NOP, // IC markers. PROPERTY_ACCESS_INLINED, PROPERTY_ACCESS_INLINED_CONTEXT, PROPERTY_ACCESS_INLINED_CONTEXT_DONT_DELETE, // Helper values. LAST_CODE_MARKER, FIRST_IC_MARKER = PROPERTY_ACCESS_INLINED }; void nop(int type = 0); // 0 is the default non-marking type. void dumy(int r1, int x2, int b2, int d2); // Check the code size generated from label to here. int SizeOfCodeGeneratedSince(Label* label) { return pc_offset() - label->pos(); } // Debugging // Mark address of a debug break slot. void RecordDebugBreakSlot(RelocInfo::Mode mode); // Record the AST id of the CallIC being compiled, so that it can be placed // in the relocation information. void SetRecordedAstId(TypeFeedbackId ast_id) { recorded_ast_id_ = ast_id; } TypeFeedbackId RecordedAstId() { // roohack - another issue??? DCHECK(!recorded_ast_id_.IsNone()); return recorded_ast_id_; } void ClearRecordedAstId() { recorded_ast_id_ = TypeFeedbackId::None(); } // Record a comment relocation entry that can be used by a disassembler. // Use --code-comments to enable. void RecordComment(const char* msg); // Record a deoptimization reason that can be used by a log or cpu profiler. // Use --trace-deopt to enable. void RecordDeoptReason(DeoptimizeReason reason, SourcePosition position, int id); // Writes a single byte or word of data in the code stream. Used // for inline tables, e.g., jump-tables. void db(uint8_t data); void dd(uint32_t data); void dq(uint64_t data); void dp(uintptr_t data); void PatchConstantPoolAccessInstruction(int pc_offset, int offset, ConstantPoolEntry::Access access, ConstantPoolEntry::Type type) { // No embedded constant pool support. UNREACHABLE(); } // Read/patch instructions SixByteInstr instr_at(int pos) { return Instruction::InstructionBits(buffer_ + pos); } template <typename T> void instr_at_put(int pos, T instr) { Instruction::SetInstructionBits<T>(buffer_ + pos, instr); } // Decodes instruction at pos, and returns its length int32_t instr_length_at(int pos) { return Instruction::InstructionLength(buffer_ + pos); } static SixByteInstr instr_at(byte* pc) { return Instruction::InstructionBits(pc); } static Condition GetCondition(Instr instr); static bool IsBranch(Instr instr); #if V8_TARGET_ARCH_S390X static bool Is64BitLoadIntoIP(SixByteInstr instr1, SixByteInstr instr2); #else static bool Is32BitLoadIntoIP(SixByteInstr instr); #endif static bool IsCmpRegister(Instr instr); static bool IsCmpImmediate(Instr instr); static bool IsNop(SixByteInstr instr, int type = NON_MARKING_NOP); // The code currently calls CheckBuffer() too often. This has the side // effect of randomly growing the buffer in the middle of multi-instruction // sequences. // // This function allows outside callers to check and grow the buffer void EnsureSpaceFor(int space_needed); void EmitRelocations(); void emit_label_addr(Label* label); public: byte* buffer_pos() const { return buffer_; } protected: // Relocation for a type-recording IC has the AST id added to it. This // member variable is a way to pass the information from the call site to // the relocation info. TypeFeedbackId recorded_ast_id_; int buffer_space() const { return reloc_info_writer.pos() - pc_; } // Decode instruction(s) at pos and return backchain to previous // label reference or kEndOfChain. int target_at(int pos); // Patch instruction(s) at pos to target target_pos (e.g. branch) void target_at_put(int pos, int target_pos, bool* is_branch = nullptr); // Record reloc info for current pc_ void RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data = 0); private: // Code generation // The relocation writer's position is at least kGap bytes below the end of // the generated instructions. This is so that multi-instruction sequences do // not have to check for overflow. The same is true for writes of large // relocation info entries. static const int kGap = 32; // Relocation info generation // Each relocation is encoded as a variable size value static const int kMaxRelocSize = RelocInfoWriter::kMaxSize; RelocInfoWriter reloc_info_writer; std::vector<DeferredRelocInfo> relocations_; // The bound position, before this we cannot do instruction elimination. int last_bound_pos_; // Code emission inline void CheckBuffer(); void GrowBuffer(int needed = 0); inline void TrackBranch(); inline void UntrackBranch(); inline int32_t emit_code_target( Handle<Code> target, RelocInfo::Mode rmode, TypeFeedbackId ast_id = TypeFeedbackId::None()); // Helpers to emit binary encoding of 2/4/6 byte instructions. inline void emit2bytes(uint16_t x); inline void emit4bytes(uint32_t x); inline void emit6bytes(uint64_t x); // Helpers to emit binary encoding for various instruction formats. inline void rr2_form(uint8_t op, Condition m1, Register r2); inline void ri_form(Opcode op, Register r1, const Operand& i2); inline void ri_form(Opcode op, Condition m1, const Operand& i2); inline void rie_form(Opcode op, Register r1, Register r3, const Operand& i2); inline void rie_f_form(Opcode op, Register r1, Register r2, const Operand& i3, const Operand& i4, const Operand& i5); inline void ris_form(Opcode op, Register r1, Condition m3, Register b4, Disp d4, const Operand& i2); inline void rrf1_form(Opcode op, Register r1, Register r2, Register r3); inline void rrf1_form(uint32_t x); inline void rrf2_form(uint32_t x); inline void rrf3_form(uint32_t x); inline void rrfe_form(Opcode op, Condition m3, Condition m4, Register r1, Register r2); inline void rrs_form(Opcode op, Register r1, Register r2, Register b4, Disp d4, Condition m3); inline void rs_form(Opcode op, Register r1, Condition m3, Register b2, const Disp d2); inline void rs_form(Opcode op, Register r1, Register r3, Register b2, const Disp d2); inline void rsi_form(Opcode op, Register r1, Register r3, const Operand& i2); inline void rsl_form(Opcode op, Length l1, Register b2, Disp d2); inline void rsy_form(Opcode op, Register r1, Register r3, Register b2, const Disp d2); inline void rsy_form(Opcode op, Register r1, Condition m3, Register b2, const Disp d2); inline void rxe_form(Opcode op, Register r1, Register x2, Register b2, Disp d2); inline void rxf_form(Opcode op, Register r1, Register r3, Register b2, Register x2, Disp d2); inline void s_form(Opcode op, Register b1, Disp d2); inline void si_form(Opcode op, const Operand& i2, Register b1, Disp d1); inline void siy_form(Opcode op, const Operand& i2, Register b1, Disp d1); inline void sil_form(Opcode op, Register b1, Disp d1, const Operand& i2); inline void ss_form(Opcode op, Length l, Register b1, Disp d1, Register b2, Disp d2); inline void ss_form(Opcode op, Length l1, Length l2, Register b1, Disp d1, Register b2, Disp d2); inline void ss_form(Opcode op, Length l1, const Operand& i3, Register b1, Disp d1, Register b2, Disp d2); inline void ss_form(Opcode op, Register r1, Register r2, Register b1, Disp d1, Register b2, Disp d2); inline void sse_form(Opcode op, Register b1, Disp d1, Register b2, Disp d2); inline void ssf_form(Opcode op, Register r3, Register b1, Disp d1, Register b2, Disp d2); // Labels void print(Label* L); int max_reach_from(int pos); void bind_to(Label* L, int pos); void next(Label* L); friend class RegExpMacroAssemblerS390; friend class RelocInfo; friend class CodePatcher; List<Handle<Code> > code_targets_; friend class EnsureSpace; }; class EnsureSpace BASE_EMBEDDED { public: explicit EnsureSpace(Assembler* assembler) { assembler->CheckBuffer(); } }; } // namespace internal } // namespace v8 #endif // V8_S390_ASSEMBLER_S390_H_