blob: ee77c6ed3cd35346f6710a96cf024807ed3a6531 [file] [log] [blame]
// 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_CODEGEN_S390_ASSEMBLER_S390_H_
#define V8_CODEGEN_S390_ASSEMBLER_S390_H_
#include <stdio.h>
#include <memory>
#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/codegen/assembler.h"
#include "src/codegen/external-reference.h"
#include "src/codegen/label.h"
#include "src/codegen/s390/constants-s390.h"
#include "src/codegen/s390/register-s390.h"
#include "src/objects/smi.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
namespace v8 {
namespace internal {
class SafepointTableBuilder;
// -----------------------------------------------------------------------------
// Machine instruction Operands
// Class Operand represents a shifter operand in data processing instructions
// defining immediate numbers and masks
class V8_EXPORT_PRIVATE Operand {
public:
// immediate
V8_INLINE explicit Operand(intptr_t immediate,
RelocInfo::Mode rmode = RelocInfo::NO_INFO)
: rmode_(rmode) {
value_.immediate = immediate;
}
V8_INLINE static Operand Zero() { return Operand(static_cast<intptr_t>(0)); }
V8_INLINE explicit Operand(const ExternalReference& f)
: rmode_(RelocInfo::EXTERNAL_REFERENCE) {
value_.immediate = static_cast<intptr_t>(f.address());
}
explicit Operand(Handle<HeapObject> handle);
V8_INLINE explicit Operand(Smi value) : rmode_(RelocInfo::NO_INFO) {
value_.immediate = static_cast<intptr_t>(value.ptr());
}
// rm
V8_INLINE explicit Operand(Register rm);
static Operand EmbeddedNumber(double value); // Smi or HeapNumber
static Operand EmbeddedStringConstant(const StringConstantBase* str);
// Return true if this is a register operand.
V8_INLINE bool is_reg() const { return rm_.is_valid(); }
bool must_output_reloc_info(const Assembler* assembler) const;
inline intptr_t immediate() const {
DCHECK(!rm_.is_valid());
DCHECK(!is_heap_object_request());
return value_.immediate;
}
HeapObjectRequest heap_object_request() const {
DCHECK(is_heap_object_request());
return value_.heap_object_request;
}
inline void setBits(int n) {
value_.immediate =
(static_cast<uint32_t>(value_.immediate) << (32 - n)) >> (32 - n);
}
Register rm() const { return rm_; }
bool is_heap_object_request() const {
DCHECK_IMPLIES(is_heap_object_request_, !rm_.is_valid());
DCHECK_IMPLIES(is_heap_object_request_,
rmode_ == RelocInfo::FULL_EMBEDDED_OBJECT ||
rmode_ == RelocInfo::CODE_TARGET);
return is_heap_object_request_;
}
RelocInfo::Mode rmode() const { return rmode_; }
private:
Register rm_ = no_reg;
union Value {
Value() {}
HeapObjectRequest heap_object_request; // if is_heap_object_request_
intptr_t immediate; // otherwise
} value_; // valid if rm_ == no_reg
bool is_heap_object_request_ = false;
RelocInfo::Mode rmode_;
friend class Assembler;
friend class MacroAssembler;
};
using Disp = int32_t;
// 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 V8_EXPORT_PRIVATE MemOperand {
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 != no_reg);
return baseRegister;
}
Register getBaseRegister() const { return rb(); }
// Index Register
Register rx() const {
DCHECK(indexRegister != 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 V8_EXPORT_PRIVATE 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 nullptr, the assembler allocates and grows its
// own buffer. Otherwise it takes ownership of the provided buffer.
explicit Assembler(const AssemblerOptions&,
std::unique_ptr<AssemblerBuffer> = {});
virtual ~Assembler() {}
// GetCode emits any pending (non-emitted) code and fills the descriptor desc.
static constexpr int kNoHandlerTable = 0;
static constexpr SafepointTableBuilder* kNoSafepointTable = nullptr;
void GetCode(Isolate* isolate, CodeDesc* desc,
SafepointTableBuilder* safepoint_table_builder,
int handler_table_offset);
// Convenience wrapper for code without safepoint or handler tables.
void GetCode(Isolate* isolate, CodeDesc* desc) {
GetCode(isolate, desc, kNoSafepointTable, kNoHandlerTable);
}
// Unused on this architecture.
void MaybeEmitOutOfLineConstantPool() {}
// 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);
// 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(); }
void load_label_offset(Register r1, Label* L);
// Read/Modify the code target address in the branch/call instruction at pc.
// The isolate argument is unused (and may be nullptr) when skipping flushing.
V8_INLINE static Address target_address_at(Address pc, Address constant_pool);
// Read/Modify the code target address in the branch/call instruction at pc.
inline static Tagged_t target_compressed_address_at(Address pc,
Address constant_pool);
V8_INLINE static void set_target_address_at(
Address pc, Address constant_pool, Address target,
ICacheFlushMode icache_flush_mode = FLUSH_ICACHE_IF_NEEDED);
inline static void set_target_compressed_address_at(
Address pc, Address constant_pool, Tagged_t target,
ICacheFlushMode icache_flush_mode = FLUSH_ICACHE_IF_NEEDED);
inline Handle<Object> code_target_object_handle_at(Address pc);
inline Handle<HeapObject> compressed_embedded_object_handle_at(
Address pc, Address constant_pool);
// This sets the branch destination.
// This is for calls and branches within generated code.
inline static void deserialization_set_special_target_at(
Address instruction_payload, Code code, Address target);
// Get the size of the special target encoded at 'instruction_payload'.
inline static int deserialization_special_target_size(
Address instruction_payload);
// This sets the internal reference at the pc.
inline static void deserialization_set_target_internal_reference_at(
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 constexpr int kSpecialTargetSize = 0;
// Number of bytes for instructions used to store pointer sized constant.
#if V8_TARGET_ARCH_S390X
static constexpr int kBytesForPtrConstant = 12; // IIHF + IILF
#else
static constexpr int kBytesForPtrConstant = 6; // IILF
#endif
RegList* GetScratchRegisterList() { return &scratch_register_list_; }
// ---------------------------------------------------------------------------
// Code generation
template <class T, int size, int lo, int hi>
inline T getfield(T value) {
DCHECK(lo < hi);
DCHECK_GT(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;
}
#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
#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); }
void lzer(DoubleRegister r1) { rre_format(LZER, r1.code(), 0); }
#undef DECLARE_S390_RRE_INSTRUCTIONS
#define DECLARE_S390_RX_INSTRUCTIONS(name, op_name, op_value) \
template <class R1> \
inline void name(R1 r1, Register x2, Register b2, const Operand& d2) { \
rx_format(op_name, r1.code(), x2.code(), b2.code(), d2.immediate()); \
} \
template <class R1> \
inline void name(R1 r1, const MemOperand& opnd) { \
name(r1, opnd.getIndexRegister(), opnd.getBaseRegister(), \
Operand(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(),
Operand(opnd.getDisplacement()));
}
void bc(Condition cond, Register x2, Register b2, const Operand& d2) {
rx_format(BC, cond, x2.code(), b2.code(), d2.immediate());
}
#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, const Operand& d2) { \
rxy_format(op_name, r1.code(), r2.code(), b2.code(), d2.immediate()); \
} \
template <class R1> \
inline void name(R1 r1, const MemOperand& opnd) { \
name(r1, opnd.getIndexRegister(), opnd.getBaseRegister(), \
Operand(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(),
Operand(opnd.getDisplacement()));
}
void pfd(Condition cond, Register x2, Register b2, const Operand& d2) {
rxy_format(PFD, cond, x2.code(), b2.code(), d2.immediate());
}
#undef DECLARE_S390_RXY_INSTRUCTIONS
inline void rsy_format(Opcode op, int f1, int f2, int f3, int f4) {
DCHECK(is_int20(f4));
DCHECK(is_uint16(op));
uint64_t code =
(getfield<uint64_t, 6, 0, 8>(op >> 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>(op & 0xff));
emit6bytes(code);
}
#define DECLARE_S390_RSY_A_INSTRUCTIONS(name, op_name, op_value) \
void name(Register r1, Register r3, Register b2, \
const Operand& d2 = Operand::Zero()) { \
rsy_format(op_name, r1.code(), r3.code(), b2.code(), d2.immediate()); \
} \
void name(Register r1, Register r3, Operand d2) { name(r1, r3, r0, d2); } \
void name(Register r1, Register r3, const MemOperand& opnd) { \
name(r1, r3, opnd.getBaseRegister(), Operand(opnd.getDisplacement())); \
}
S390_RSY_A_OPCODE_LIST(DECLARE_S390_RSY_A_INSTRUCTIONS)
#undef DECLARE_S390_RSY_A_INSTRUCTIONS
#define DECLARE_S390_RSY_B_INSTRUCTIONS(name, op_name, op_value) \
void name(Register r1, Condition m3, Register b2, const Operand& d2) { \
rsy_format(op_name, r1.code(), m3, b2.code(), d2.immediate()); \
} \
void name(Register r1, Condition m3, const MemOperand& opnd) { \
name(r1, m3, opnd.getBaseRegister(), Operand(opnd.getDisplacement())); \
}
S390_RSY_B_OPCODE_LIST(DECLARE_S390_RSY_B_INSTRUCTIONS)
#undef DECLARE_S390_RSY_B_INSTRUCTIONS
inline void rs_format(Opcode op, int f1, int f2, int f3, const int f4) {
uint32_t code =
getfield<uint32_t, 4, 0, 8>(op) | 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);
emit4bytes(code);
}
#define DECLARE_S390_RS_A_INSTRUCTIONS(name, op_name, op_value) \
void name(Register r1, Register r3, Register b2, const Operand& d2) { \
rs_format(op_name, r1.code(), r3.code(), b2.code(), d2.immediate()); \
} \
void name(Register r1, Register r3, const MemOperand& opnd) { \
name(r1, r3, opnd.getBaseRegister(), Operand(opnd.getDisplacement())); \
}
S390_RS_A_OPCODE_LIST(DECLARE_S390_RS_A_INSTRUCTIONS)
#undef DECLARE_S390_RS_A_INSTRUCTIONS
#define DECLARE_S390_RS_B_INSTRUCTIONS(name, op_name, op_value) \
void name(Register r1, Condition m3, Register b2, const Operand& d2) { \
rs_format(op_name, r1.code(), m3, b2.code(), d2.immediate()); \
} \
void name(Register r1, Condition m3, const MemOperand& opnd) { \
name(r1, m3, opnd.getBaseRegister(), Operand(opnd.getDisplacement())); \
}
S390_RS_B_OPCODE_LIST(DECLARE_S390_RS_B_INSTRUCTIONS)
#undef DECLARE_S390_RS_B_INSTRUCTIONS
#define DECLARE_S390_RS_SHIFT_FORMAT(name, opcode) \
void name(Register r1, Register r2, const Operand& opnd = Operand::Zero()) { \
rs_format(opcode, r1.code(), r0.code(), r2.code(), opnd.immediate()); \
} \
void name(Register r1, const Operand& opnd) { \
rs_format(opcode, r1.code(), r0.code(), r0.code(), opnd.immediate()); \
}
DECLARE_S390_RS_SHIFT_FORMAT(sll, SLL)
DECLARE_S390_RS_SHIFT_FORMAT(srl, SRL)
DECLARE_S390_RS_SHIFT_FORMAT(sla, SLA)
DECLARE_S390_RS_SHIFT_FORMAT(sra, SRA)
DECLARE_S390_RS_SHIFT_FORMAT(sldl, SLDL)
DECLARE_S390_RS_SHIFT_FORMAT(srda, SRDA)
DECLARE_S390_RS_SHIFT_FORMAT(srdl, SRDL)
#undef DECLARE_S390_RS_SHIFT_FORMAT
inline void rxe_format(Opcode op, int f1, int f2, int f3, int f4,
int f5 = 0) {
DCHECK(is_uint12(f4));
DCHECK(is_uint16(op));
uint64_t code =
(getfield<uint64_t, 6, 0, 8>(op >> 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, 36>(f5) |
getfield<uint64_t, 6, 40, 48>(op & 0xff));
emit6bytes(code);
}
#define DECLARE_S390_RXE_INSTRUCTIONS(name, op_name, op_value) \
void name(Register r1, Register x2, Register b2, const Operand& d2, \
Condition m3 = static_cast<Condition>(0)) { \
rxe_format(op_name, r1.code(), x2.code(), b2.code(), d2.immediate(), m3); \
} \
template <class _R1Type> \
void name(_R1Type r1, const MemOperand& opnd) { \
name(Register::from_code(r1.code()), opnd.rx(), opnd.rb(), \
Operand(opnd.offset())); \
}
S390_RXE_OPCODE_LIST(DECLARE_S390_RXE_INSTRUCTIONS)
#undef DECLARE_S390_RXE_INSTRUCTIONS
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));
}
#define DECLARE_S390_RI_A_INSTRUCTIONS(name, op_name, op_value) \
void name(Register r, const Operand& i2) { \
DCHECK(is_uint12(op_name)); \
DCHECK(is_uint16(i2.immediate()) || is_int16(i2.immediate())); \
ri_format(op_name, r.code(), i2.immediate()); \
}
S390_RI_A_OPCODE_LIST(DECLARE_S390_RI_A_INSTRUCTIONS)
#undef DECLARE_S390_RI_A_INSTRUCTIONS
#define DECLARE_S390_RI_B_INSTRUCTIONS(name, op_name, op_value) \
void name(Register r1, const Operand& imm) { \
/* 2nd argument encodes # of halfwords, so divide by 2. */ \
int16_t numHalfwords = static_cast<int16_t>(imm.immediate()) / 2; \
Operand halfwordOp = Operand(numHalfwords); \
halfwordOp.setBits(16); \
ri_format(op_name, r1.code(), halfwordOp.immediate()); \
}
S390_RI_B_OPCODE_LIST(DECLARE_S390_RI_B_INSTRUCTIONS)
#undef DECLARE_S390_RI_B_INSTRUCTIONS
#define DECLARE_S390_RI_C_INSTRUCTIONS(name, op_name, op_value) \
void name(Condition m, const Operand& i2) { \
DCHECK(is_uint12(op_name)); \
DCHECK(is_uint4(m)); \
DCHECK(op_name == BRC ? is_int16(i2.immediate()) \
: is_uint16(i2.immediate())); \
ri_format(op_name, m, i2.immediate()); \
}
S390_RI_C_OPCODE_LIST(DECLARE_S390_RI_C_INSTRUCTIONS)
#undef DECLARE_S390_RI_C_INSTRUCTIONS
inline void rrf_format(Opcode op, int f1, int f2, int f3, int f4) {
uint32_t code =
getfield<uint32_t, 4, 0, 16>(op) | 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);
emit4bytes(code);
}
#define DECLARE_S390_RRF_A_INSTRUCTIONS(name, op_name, op_value) \
void name(Register r1, Condition m4, Register r2, Register r3) { \
rrf_format(op_name, r3.code(), m4, r1.code(), r2.code()); \
} \
void name(Register r1, Register r2, Register r3) { \
name(r1, Condition(0), r2, r3); \
}
S390_RRF_A_OPCODE_LIST(DECLARE_S390_RRF_A_INSTRUCTIONS)
#undef DECLARE_S390_RRF_A_INSTRUCTIONS
#define DECLARE_S390_RRF_B_INSTRUCTIONS(name, op_name, op_value) \
void name(Register r1, Condition m4, Register r2, Register r3) { \
rrf_format(op_name, r3.code(), m4, r1.code(), r2.code()); \
} \
void name(Register r1, Register r2, Register r3) { \
name(r1, Condition(0), r2, r3); \
}
S390_RRF_B_OPCODE_LIST(DECLARE_S390_RRF_B_INSTRUCTIONS)
#undef DECLARE_S390_RRF_B_INSTRUCTIONS
#define DECLARE_S390_RRF_C_INSTRUCTIONS(name, op_name, op_value) \
template <class R1, class R2> \
void name(Condition m3, Condition m4, R1 r1, R2 r2) { \
rrf_format(op_name, m3, m4, r1.code(), r2.code()); \
} \
template <class R1, class R2> \
void name(Condition m3, R1 r1, R2 r2) { \
name(m3, Condition(0), r1, r2); \
}
S390_RRF_C_OPCODE_LIST(DECLARE_S390_RRF_C_INSTRUCTIONS)
#undef DECLARE_S390_RRF_C_INSTRUCTIONS
#define DECLARE_S390_RRF_D_INSTRUCTIONS(name, op_name, op_value) \
template <class R1, class R2> \
void name(Condition m3, Condition m4, R1 r1, R2 r2) { \
rrf_format(op_name, m3, m4, r1.code(), r2.code()); \
} \
template <class R1, class R2> \
void name(Condition m3, R1 r1, R2 r2) { \
name(m3, Condition(0), r1, r2); \
}
S390_RRF_D_OPCODE_LIST(DECLARE_S390_RRF_D_INSTRUCTIONS)
#undef DECLARE_S390_RRF_D_INSTRUCTIONS
#define DECLARE_S390_RRF_E_INSTRUCTIONS(name, op_name, op_value) \
template <class M3, class M4, class R1, class R2> \
void name(M3 m3, M4 m4, R1 r1, R2 r2) { \
rrf_format(op_name, m3, m4, r1.code(), r2.code()); \
} \
template <class M3, class R1, class R2> \
void name(M3 m3, R1 r1, R2 r2) { \
name(m3, Condition(0), r1, r2); \
}
S390_RRF_E_OPCODE_LIST(DECLARE_S390_RRF_E_INSTRUCTIONS)
#undef DECLARE_S390_RRF_E_INSTRUCTIONS
inline void rsi_format(Opcode op, int f1, int f2, int f3) {
DCHECK(is_uint8(op));
DCHECK(is_uint16(f3) || is_int16(f3));
uint32_t code =
getfield<uint32_t, 4, 0, 8>(op) | getfield<uint32_t, 4, 8, 12>(f1) |
getfield<uint32_t, 4, 12, 16>(f2) | getfield<uint32_t, 4, 16, 32>(f3);
emit4bytes(code);
}
#define DECLARE_S390_RSI_INSTRUCTIONS(name, op_name, op_value) \
void name(Register r1, Register r3, const Operand& i2) { \
rsi_format(op_name, r1.code(), r3.code(), i2.immediate()); \
}
S390_RSI_OPCODE_LIST(DECLARE_S390_RSI_INSTRUCTIONS)
#undef DECLARE_S390_RSI_INSTRUCTIONS
inline void rsl_format(Opcode op, uint16_t f1, int f2, int f3, int f4,
int f5) {
DCHECK(is_uint16(op));
uint64_t code =
getfield<uint64_t, 6, 0, 8>(op >> 8) |
getfield<uint64_t, 6, 8, 16>(f1) | getfield<uint64_t, 6, 16, 20>(f2) |
getfield<uint64_t, 6, 20, 32>(f3) | getfield<uint64_t, 6, 32, 36>(f4) |
getfield<uint64_t, 6, 36, 40>(f5) |
getfield<uint64_t, 6, 40, 48>(op & 0x00FF);
emit6bytes(code);
}
#define DECLARE_S390_RSL_A_INSTRUCTIONS(name, op_name, op_value) \
void name(const Operand& l1, Register b1, const Operand& d1) { \
uint16_t L = static_cast<uint16_t>(l1.immediate() << 8); \
rsl_format(op_name, L, b1.code(), d1.immediate(), 0, 0); \
}
S390_RSL_A_OPCODE_LIST(DECLARE_S390_RSL_A_INSTRUCTIONS)
#undef DECLARE_S390_RSL_A_INSTRUCTIONS
#define DECLARE_S390_RSL_B_INSTRUCTIONS(name, op_name, op_value) \
void name(const Operand& l2, Register b2, const Operand& d2, Register r1, \
Condition m3) { \
uint16_t L = static_cast<uint16_t>(l2.immediate()); \
rsl_format(op_name, L, b2.code(), d2.immediate(), r1.code(), m3); \
}
S390_RSL_B_OPCODE_LIST(DECLARE_S390_RSL_B_INSTRUCTIONS)
#undef DECLARE_S390_RSL_B_INSTRUCTIONS
inline void s_format(Opcode op, int f1, int f2) {
DCHECK_NE(op & 0xff00, 0);
DCHECK(is_uint12(f2));
uint32_t code = getfield<uint32_t, 4, 0, 16>(op) |
getfield<uint32_t, 4, 16, 20>(f1) |
getfield<uint32_t, 4, 20, 32>(f2);
emit4bytes(code);
}
#define DECLARE_S390_S_INSTRUCTIONS(name, op_name, op_value) \
void name(Register b1, const Operand& d2) { \
Opcode op = op_name; \
if ((op & 0xFF00) == 0) { \
op = (Opcode)(op << 8); \
} \
s_format(op, b1.code(), d2.immediate()); \
} \
void name(const MemOperand& opnd) { \
Operand d2 = Operand(opnd.getDisplacement()); \
name(opnd.getBaseRegister(), d2); \
}
S390_S_OPCODE_LIST(DECLARE_S390_S_INSTRUCTIONS)
#undef DECLARE_S390_S_INSTRUCTIONS
inline void si_format(Opcode op, int f1, int f2, int f3) {
uint32_t code =
getfield<uint32_t, 4, 0, 8>(op) | getfield<uint32_t, 4, 8, 16>(f1) |
getfield<uint32_t, 4, 16, 20>(f2) | getfield<uint32_t, 4, 20, 32>(f3);
emit4bytes(code);
}
#define DECLARE_S390_SI_INSTRUCTIONS(name, op_name, op_value) \
void name(const Operand& i2, Register b1, const Operand& d1) { \
si_format(op_name, i2.immediate(), b1.code(), d1.immediate()); \
} \
void name(const MemOperand& opnd, const Operand& i2) { \
name(i2, opnd.getBaseRegister(), Operand(opnd.getDisplacement())); \
}
S390_SI_OPCODE_LIST(DECLARE_S390_SI_INSTRUCTIONS)
#undef DECLARE_S390_SI_INSTRUCTIONS
inline void siy_format(Opcode op, int f1, int f2, int f3) {
DCHECK(is_uint20(f3) || is_int20(f3));
DCHECK(is_uint16(op));
DCHECK(is_uint8(f1) || is_int8(f1));
uint64_t code = getfield<uint64_t, 6, 0, 8>(op >> 8) |
getfield<uint64_t, 6, 8, 16>(f1) |
getfield<uint64_t, 6, 16, 20>(f2) |
getfield<uint64_t, 6, 20, 32>(f3) |
getfield<uint64_t, 6, 32, 40>(f3 >> 12) |
getfield<uint64_t, 6, 40, 48>(op & 0x00FF);
emit6bytes(code);
}
#define DECLARE_S390_SIY_INSTRUCTIONS(name, op_name, op_value) \
void name(const Operand& i2, Register b1, const Operand& d1) { \
siy_format(op_name, i2.immediate(), b1.code(), d1.immediate()); \
} \
void name(const MemOperand& opnd, const Operand& i2) { \
name(i2, opnd.getBaseRegister(), Operand(opnd.getDisplacement())); \
}
S390_SIY_OPCODE_LIST(DECLARE_S390_SIY_INSTRUCTIONS)
#undef DECLARE_S390_SIY_INSTRUCTIONS
inline void rrs_format(Opcode op, int f1, int f2, int f3, int f4, int f5) {
DCHECK(is_uint12(f4));
DCHECK(is_uint16(op));
uint64_t code =
getfield<uint64_t, 6, 0, 8>(op >> 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) |
getfield<uint64_t, 6, 32, 36>(f5) |
getfield<uint64_t, 6, 40, 48>(op & 0x00FF);
emit6bytes(code);
}
#define DECLARE_S390_RRS_INSTRUCTIONS(name, op_name, op_value) \
void name(Register r1, Register r2, Register b4, const Operand& d4, \
Condition m3) { \
rrs_format(op_name, r1.code(), r2.code(), b4.code(), d4.immediate(), m3); \
} \
void name(Register r1, Register r2, Condition m3, const MemOperand& opnd) { \
name(r1, r2, opnd.getBaseRegister(), Operand(opnd.getDisplacement()), m3); \
}
S390_RRS_OPCODE_LIST(DECLARE_S390_RRS_INSTRUCTIONS)
#undef DECLARE_S390_RRS_INSTRUCTIONS
inline void ris_format(Opcode op, int f1, int f2, int f3, int f4, int f5) {
DCHECK(is_uint12(f3));
DCHECK(is_uint16(op));
DCHECK(is_uint8(f5));
uint64_t code =
getfield<uint64_t, 6, 0, 8>(op >> 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) |
getfield<uint64_t, 6, 32, 40>(f5) |
getfield<uint64_t, 6, 40, 48>(op & 0x00FF);
emit6bytes(code);
}
#define DECLARE_S390_RIS_INSTRUCTIONS(name, op_name, op_value) \
void name(Register r1, Condition m3, Register b4, const Operand& d4, \
const Operand& i2) { \
ris_format(op_name, r1.code(), m3, b4.code(), d4.immediate(), \
i2.immediate()); \
} \
void name(Register r1, const Operand& i2, Condition m3, \
const MemOperand& opnd) { \
name(r1, m3, opnd.getBaseRegister(), Operand(opnd.getDisplacement()), i2); \
}
S390_RIS_OPCODE_LIST(DECLARE_S390_RIS_INSTRUCTIONS)
#undef DECLARE_S390_RIS_INSTRUCTIONS
inline void sil_format(Opcode op, int f1, int f2, int f3) {
DCHECK(is_uint12(f2));
DCHECK(is_uint16(op));
DCHECK(is_uint16(f3));
uint64_t code =
getfield<uint64_t, 6, 0, 16>(op) | getfield<uint64_t, 6, 16, 20>(f1) |
getfield<uint64_t, 6, 20, 32>(f2) | getfield<uint64_t, 6, 32, 48>(f3);
emit6bytes(code);
}
#define DECLARE_S390_SIL_INSTRUCTIONS(name, op_name, op_value) \
void name(Register b1, const Operand& d1, const Operand& i2) { \
sil_format(op_name, b1.code(), d1.immediate(), i2.immediate()); \
} \
void name(const MemOperand& opnd, const Operand& i2) { \
name(opnd.getBaseRegister(), Operand(opnd.getDisplacement()), i2); \
}
S390_SIL_OPCODE_LIST(DECLARE_S390_SIL_INSTRUCTIONS)
#undef DECLARE_S390_SIL_INSTRUCTIONS
inline void rie_d_format(Opcode opcode, int f1, int f2, int f3, int f4) {
uint32_t op1 = opcode >> 8;
uint32_t op2 = opcode & 0xff;
uint64_t code =
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);
emit6bytes(code);
}
#define DECLARE_S390_RIE_D_INSTRUCTIONS(name, op_name, op_value) \
void name(Register r1, Register r3, const Operand& i2) { \
rie_d_format(op_name, r1.code(), r3.code(), i2.immediate(), 0); \
}
S390_RIE_D_OPCODE_LIST(DECLARE_S390_RIE_D_INSTRUCTIONS)
#undef DECLARE_S390_RIE_D_INSTRUCTIONS
inline void rie_e_format(Opcode opcode, int f1, int f2, int f3) {
uint32_t op1 = opcode >> 8;
uint32_t op2 = opcode & 0xff;
uint64_t code =
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, 40, 48>(op2);
emit6bytes(code);
}
#define DECLARE_S390_RIE_E_INSTRUCTIONS(name, op_name, op_value) \
void name(Register r1, Register r3, const Operand& i2) { \
rie_e_format(op_name, r1.code(), r3.code(), i2.immediate()); \
}
S390_RIE_E_OPCODE_LIST(DECLARE_S390_RIE_E_INSTRUCTIONS)
#undef DECLARE_S390_RIE_E_INSTRUCTIONS
inline void rie_f_format(Opcode opcode, int f1, int f2, int f3, int f4,
int f5) {
uint32_t op1 = opcode >> 8;
uint32_t op2 = opcode & 0xff;
uint64_t code =
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);
emit6bytes(code);
}
#define DECLARE_S390_RIE_F_INSTRUCTIONS(name, op_name, op_value) \
void name(Register dst, Register src, const Operand& startBit, \
const Operand& endBit, const Operand& shiftAmt) { \
DCHECK(is_uint8(startBit.immediate())); \
DCHECK(is_uint8(endBit.immediate())); \
DCHECK(is_uint8(shiftAmt.immediate())); \
rie_f_format(op_name, dst.code(), src.code(), startBit.immediate(), \
endBit.immediate(), shiftAmt.immediate()); \
}
S390_RIE_F_OPCODE_LIST(DECLARE_S390_RIE_F_INSTRUCTIONS)
#undef DECLARE_S390_RIE_F_INSTRUCTIONS
inline void ss_a_format(Opcode op, int f1, int f2, int f3, int f4, int f5) {
DCHECK(is_uint12(f5));
DCHECK(is_uint12(f3));
DCHECK(is_uint8(f1));
DCHECK(is_uint8(op));
uint64_t code =
getfield<uint64_t, 6, 0, 8>(op) | getfield<uint64_t, 6, 8, 16>(f1) |
getfield<uint64_t, 6, 16, 20>(f2) | getfield<uint64_t, 6, 20, 32>(f3) |
getfield<uint64_t, 6, 32, 36>(f4) | getfield<uint64_t, 6, 36, 48>(f5);
emit6bytes(code);
}
#define DECLARE_S390_SS_A_INSTRUCTIONS(name, op_name, op_value) \
void name(Register b1, const Operand& d1, Register b2, const Operand& d2, \
const Operand& length) { \
ss_a_format(op_name, length.immediate(), b1.code(), d1.immediate(), \
b2.code(), d2.immediate()); \
} \
void name(const MemOperand& opnd1, const MemOperand& opnd2, \
const Operand& length) { \
ss_a_format(op_name, length.immediate(), opnd1.getBaseRegister().code(), \
opnd1.getDisplacement(), opnd2.getBaseRegister().code(), \
opnd2.getDisplacement()); \
}
S390_SS_A_OPCODE_LIST(DECLARE_S390_SS_A_INSTRUCTIONS)
#undef DECLARE_S390_SS_A_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,
bool force_long_branch = false);
// Helpers for conditional branch to Label
void b(Condition cond, Label* l, Label::Distance dist = Label::kFar,
bool force_long_branch = false) {
branchOnCond(cond, branch_offset(l),
l->is_bound() || (dist == Label::kNear), force_long_branch);
}
void bc_short(Condition cond, Label* l, Label::Distance dist = Label::kFar) {
b(cond, l, Label::kNear);
}
void bc_long(Condition cond, Label* l) { b(cond, l, Label::kFar, true); }
// 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); }
// wrappers around asm instr
void brxh(Register dst, Register inc, Label* L) {
int offset_halfwords = branch_offset(L) / 2;
CHECK(is_int16(offset_halfwords));
brxh(dst, inc, Operand(offset_halfwords));
}
void brxhg(Register dst, Register inc, Label* L) {
int offset_halfwords = branch_offset(L) / 2;
CHECK(is_int16(offset_halfwords));
brxhg(dst, inc, Operand(offset_halfwords));
}
template <class R1, class R2>
void ledbr(R1 r1, R2 r2) {
ledbra(Condition(0), Condition(0), r1, r2);
}
template <class R1, class R2>
void cdfbr(R1 r1, R2 r2) {
cdfbra(Condition(0), Condition(0), r1, r2);
}
template <class R1, class R2>
void cdgbr(R1 r1, R2 r2) {
cdgbra(Condition(0), Condition(0), r1, r2);
}
template <class R1, class R2>
void cegbr(R1 r1, R2 r2) {
cegbra(Condition(0), Condition(0), r1, r2);
}
template <class R1, class R2>
void cgebr(Condition m3, R1 r1, R2 r2) {
cgebra(m3, Condition(0), r1, r2);
}
template <class R1, class R2>
void cgdbr(Condition m3, R1 r1, R2 r2) {
cgdbra(m3, Condition(0), r1, r2);
}
template <class R1, class R2>
void cfdbr(Condition m3, R1 r1, R2 r2) {
cfdbra(m3, Condition(0), r1, r2);
}
template <class R1, class R2>
void cfebr(Condition m3, R1 r1, R2 r2) {
cfebra(m3, Condition(0), r1, r2);
}
// ---------------------------------------------------------------------------
// 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 LoopHeaderAlign() { 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);
void jump(Handle<Code> target, RelocInfo::Mode rmode, Condition cond);
// S390 instruction generation
#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>(0)) * B8 | \
(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>(0)) * B8 | \
(static_cast<uint64_t>(opcode_value & 0x00FF)); \
emit6bytes(code); \
}
S390_VRR_C_OPCODE_LIST(DECLARE_VRR_C_INSTRUCTIONS)
#undef DECLARE_VRR_C_INSTRUCTIONS
#define DECLARE_VRR_B_INSTRUCTIONS(name, opcode_name, opcode_value) \
void name(DoubleRegister v1, DoubleRegister v2, DoubleRegister v3, \
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>(m5 & 0xF)) * B20 | \
(static_cast<uint64_t>(m4 & 0xF)) * B12 | \
(static_cast<uint64_t>(0)) * B8 | \
(static_cast<uint64_t>(opcode_value & 0x00FF)); \
emit6bytes(code); \
}
S390_VRR_B_OPCODE_LIST(DECLARE_VRR_B_INSTRUCTIONS)
#undef DECLARE_VRR_B_INSTRUCTIONS
#define DECLARE_VRR_E_INSTRUCTIONS(name, opcode_name, opcode_value) \
void name(DoubleRegister v1, DoubleRegister v2, DoubleRegister v3, \
DoubleRegister v4, Condition m6, Condition m5) { \
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)) * B24 | \
(static_cast<uint64_t>(m5 & 0xF)) * B16 | \
(static_cast<uint64_t>(v4.code())) * B12 | \
(static_cast<uint64_t>(0)) * B8 | \
(static_cast<uint64_t>(opcode_value & 0x00FF)); \
emit6bytes(code); \
}
S390_VRR_E_OPCODE_LIST(DECLARE_VRR_E_INSTRUCTIONS)
#undef DECLARE_VRR_E_INSTRUCTIONS
#define DECLARE_VRR_F_INSTRUCTIONS(name, opcode_name, opcode_value) \
void name(DoubleRegister v1, Register r1, Register r2) { \
uint64_t code = (static_cast<uint64_t>(opcode_value & 0xFF00)) * B32 | \
(static_cast<uint64_t>(v1.code())) * B36 | \
(static_cast<uint64_t>(r1.code())) * B32 | \
(static_cast<uint64_t>(r2.code())) * B28 | \
(static_cast<uint64_t>(0)) * B8 | \
(static_cast<uint64_t>(opcode_value & 0x00FF)); \
emit6bytes(code); \
}
S390_VRR_F_OPCODE_LIST(DECLARE_VRR_F_INSTRUCTIONS)
#undef DECLARE_VRR_E_INSTRUCTIONS
#define DECLARE_VRX_INSTRUCTIONS(name, opcode_name, opcode_value) \
void name(DoubleRegister v1, const MemOperand& opnd, Condition m3) { \
uint64_t code = \
(static_cast<uint64_t>(opcode_value & 0xFF00)) * B32 | \
(static_cast<uint64_t>(v1.code())) * B36 | \
(static_cast<uint64_t>(opnd.getIndexRegister().code())) * B32 | \
(static_cast<uint64_t>(opnd.getBaseRegister().code())) * B28 | \
(static_cast<uint64_t>(opnd.getDisplacement())) * B16 | \
(static_cast<uint64_t>(m3 & 0xF)) * B12 | \
(static_cast<uint64_t>(0)) * B8 | \
(static_cast<uint64_t>(opcode_value & 0x00FF)); \
emit6bytes(code); \
}
S390_VRX_OPCODE_LIST(DECLARE_VRX_INSTRUCTIONS)
#undef DECLARE_VRX_INSTRUCTIONS
#define DECLARE_VRS_A_INSTRUCTIONS(name, opcode_name, opcode_value) \
void name(DoubleRegister v1, DoubleRegister v2, const MemOperand& opnd, \
Condition m4 = Condition(0)) { \
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>(opnd.getBaseRegister().code())) * B28 | \
(static_cast<uint64_t>(opnd.getDisplacement())) * B16 | \
(static_cast<uint64_t>(m4 & 0xF)) * B12 | \
(static_cast<uint64_t>(0)) * B8 | \
(static_cast<uint64_t>(opcode_value & 0x00FF)); \
emit6bytes(code); \
}
S390_VRS_A_OPCODE_LIST(DECLARE_VRS_A_INSTRUCTIONS)
#undef DECLARE_VRS_A_INSTRUCTIONS
#define DECLARE_VRS_B_INSTRUCTIONS(name, opcode_name, opcode_value) \
void name(DoubleRegister v1, Register r1, const MemOperand& opnd, \
Condition m4 = Condition(0)) { \
uint64_t code = \
(static_cast<uint64_t>(opcode_value & 0xFF00)) * B32 | \
(static_cast<uint64_t>(v1.code())) * B36 | \
(static_cast<uint64_t>(r1.code())) * B32 | \
(static_cast<uint64_t>(opnd.getBaseRegister().code())) * B28 | \
(static_cast<uint64_t>(opnd.getDisplacement())) * B16 | \
(static_cast<uint64_t>(m4 & 0xF)) * B12 | \
(static_cast<uint64_t>(0)) * B8 | \
(static_cast<uint64_t>(opcode_value & 0x00FF)); \
emit6bytes(code); \
}
S390_VRS_B_OPCODE_LIST(DECLARE_VRS_B_INSTRUCTIONS)
#undef DECLARE_VRS_B_INSTRUCTIONS
#define DECLARE_VRS_C_INSTRUCTIONS(name, opcode_name, opcode_value) \
void name(Register r1, DoubleRegister v1, const MemOperand& opnd, \
Condition m4 = Condition(0)) { \
uint64_t code = \
(static_cast<uint64_t>(opcode_value & 0xFF00)) * B32 | \
(static_cast<uint64_t>(r1.code())) * B36 | \
(static_cast<uint64_t>(v1.code())) * B32 | \
(static_cast<uint64_t>(opnd.getBaseRegister().code())) * B28 | \
(static_cast<uint64_t>(opnd.getDisplacement())) * B16 | \
(static_cast<uint64_t>(m4 & 0xF)) * B12 | \
(static_cast<uint64_t>(0)) * B8 | \
(static_cast<uint64_t>(opcode_value & 0x00FF)); \
emit6bytes(code); \
}
S390_VRS_C_OPCODE_LIST(DECLARE_VRS_C_INSTRUCTIONS)
#undef DECLARE_VRS_C_INSTRUCTIONS
#define DECLARE_VRI_A_INSTRUCTIONS(name, opcode_name, opcode_value) \
void name(DoubleRegister v1, const Operand& i2, Condition m3) { \
uint64_t code = (static_cast<uint64_t>(opcode_value & 0xFF00)) * B32 | \
(static_cast<uint64_t>(v1.code())) * B36 | \
(static_cast<uint32_t>(i2.immediate())) * B16 | \
(static_cast<uint64_t>(m3 & 0xF)) * B12 | \
(static_cast<uint64_t>(0)) * B8 | \
(static_cast<uint64_t>(opcode_value & 0x00FF)); \
emit6bytes(code); \
}
S390_VRI_A_OPCODE_LIST(DECLARE_VRI_A_INSTRUCTIONS)
#undef DECLARE_VRI_A_INSTRUCTIONS
#define DECLARE_VRI_C_INSTRUCTIONS(name, opcode_name, opcode_value) \
void name(DoubleRegister v1, DoubleRegister v2, const Operand& i2, \
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<uint16_t>(i2.immediate())) * B16 | \
(static_cast<uint64_t>(m4 & 0xF)) * B12 | \
(static_cast<uint64_t>(0)) * B8 | \
(static_cast<uint64_t>(opcode_value & 0x00FF)); \
emit6bytes(code); \
}
S390_VRI_C_OPCODE_LIST(DECLARE_VRI_C_INSTRUCTIONS)
#undef DECLARE_VRI_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));
}
// Load Address Instructions
void larl(Register r, Label* l);
void lgrl(Register r, Label* l);
// Exception-generating instructions and debugging support
void stop(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();
}
// Record a deoptimization reason that can be used by a log or cpu profiler.
// Use --trace-deopt to enable.
void RecordDeoptReason(DeoptimizeReason reason, uint32_t node_id,
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, RelocInfo::Mode rmode = RelocInfo::NO_INFO);
void dq(uint64_t data, RelocInfo::Mode rmode = RelocInfo::NO_INFO);
void dp(uintptr_t data, RelocInfo::Mode rmode = RelocInfo::NO_INFO);
// Read/patch instructions
SixByteInstr instr_at(int pos) {
return Instruction::InstructionBits(buffer_start_ + pos);
}
template <typename T>
void instr_at_put(int pos, T instr) {
Instruction::SetInstructionBits<T>(buffer_start_ + pos, instr);
}
// Decodes instruction at pos, and returns its length
int32_t instr_length_at(int pos) {
return Instruction::InstructionLength(buffer_start_ + 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_start_; }
// 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 constexpr int kGap = 32;
STATIC_ASSERT(AssemblerBase::kMinimalBufferSize >= 2 * kGap);
protected:
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:
// Avoid overflows for displacements etc.
static const int kMaximalBufferSize = 512 * MB;
// Relocation info generation
// Each relocation is encoded as a variable size value
static constexpr int kMaxRelocSize = RelocInfoWriter::kMaxSize;
RelocInfoWriter reloc_info_writer;
std::vector<DeferredRelocInfo> relocations_;
// Scratch registers available for use by the Assembler.
RegList scratch_register_list_;
// The bound position, before this we cannot do instruction elimination.
int last_bound_pos_;
// Code emission
void CheckBuffer() {
if (buffer_space() <= kGap) {
GrowBuffer();
}
}
void GrowBuffer(int needed = 0);
inline void TrackBranch();
inline void UntrackBranch();
// Helper to emit the binary encoding of a 2 byte instruction
void emit2bytes(uint16_t x) {
CheckBuffer();
#if V8_TARGET_LITTLE_ENDIAN
// We need to emit instructions in big endian format as disassembler /
// simulator require the first byte of the instruction in order to decode
// the instruction length. Swap the bytes.
x = ((x & 0x00FF) << 8) | ((x & 0xFF00) >> 8);
#endif
*reinterpret_cast<uint16_t*>(pc_) = x;
pc_ += 2;
}
// Helper to emit the binary encoding of a 4 byte instruction
void emit4bytes(uint32_t x) {
CheckBuffer();
#if V8_TARGET_LITTLE_ENDIAN
// We need to emit instructions in big endian format as disassembler /
// simulator require the first byte of the instruction in order to decode
// the instruction length. Swap the bytes.
x = ((x & 0x000000FF) << 24) | ((x & 0x0000FF00) << 8) |
((x & 0x00FF0000) >> 8) | ((x & 0xFF000000) >> 24);
#endif
*reinterpret_cast<uint32_t*>(pc_) = x;
pc_ += 4;
}
// Helper to emit the binary encoding of a 6 byte instruction
void emit6bytes(uint64_t x) {
CheckBuffer();
#if V8_TARGET_LITTLE_ENDIAN
// We need to emit instructions in big endian format as disassembler /
// simulator require the first byte of the instruction in order to decode
// the instruction length. Swap the bytes.
x = (static_cast<uint64_t>(x & 0xFF) << 40) |
(static_cast<uint64_t>((x >> 8) & 0xFF) << 32) |
(static_cast<uint64_t>((x >> 16) & 0xFF) << 24) |
(static_cast<uint64_t>((x >> 24) & 0xFF) << 16) |
(static_cast<uint64_t>((x >> 32) & 0xFF) << 8) |
(static_cast<uint64_t>((x >> 40) & 0xFF));
x |= (*reinterpret_cast<uint64_t*>(pc_) >> 48) << 48;
#else
// We need to pad two bytes of zeros in order to get the 6-bytes
// stored from low address.
x = x << 16;
x |= *reinterpret_cast<uint64_t*>(pc_) & 0xFFFF;
#endif
// It is safe to store 8-bytes, as CheckBuffer() guarantees we have kGap
// space left over.
*reinterpret_cast<uint64_t*>(pc_) = x;
pc_ += 6;
}
// Labels
void print(Label* L);
int max_reach_from(int pos);
void bind_to(Label* L, int pos);
void next(Label* L);
void AllocateAndInstallRequestedHeapObjects(Isolate* isolate);
int WriteCodeComments();
friend class RegExpMacroAssemblerS390;
friend class RelocInfo;
friend class EnsureSpace;
friend class UseScratchRegisterScope;
};
class EnsureSpace {
public:
explicit EnsureSpace(Assembler* assembler) { assembler->CheckBuffer(); }
};
class V8_EXPORT_PRIVATE V8_NODISCARD UseScratchRegisterScope {
public:
explicit UseScratchRegisterScope(Assembler* assembler);
~UseScratchRegisterScope();
Register Acquire();
// Check if we have registers available to acquire.
bool CanAcquire() const { return *assembler_->GetScratchRegisterList() != 0; }
private:
friend class Assembler;
friend class TurboAssembler;
Assembler* assembler_;
RegList old_available_;
};
} // namespace internal
} // namespace v8
#endif // V8_CODEGEN_S390_ASSEMBLER_S390_H_