--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/cpu/sparc/nativeInst_sparc.hpp Tue Sep 12 19:03:39 2017 +0200
@@ -0,0 +1,811 @@
+/*
+ * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#ifndef CPU_SPARC_VM_NATIVEINST_SPARC_HPP
+#define CPU_SPARC_VM_NATIVEINST_SPARC_HPP
+
+#include "asm/macroAssembler.hpp"
+#include "memory/allocation.hpp"
+#include "runtime/icache.hpp"
+#include "runtime/os.hpp"
+
+// We have interface for the following instructions:
+// - NativeInstruction
+// - - NativeCall
+// - - NativeFarCall
+// - - NativeMovConstReg
+// - - NativeMovConstRegPatching
+// - - NativeMovRegMem
+// - - NativeJump
+// - - NativeGeneralJump
+// - - NativeIllegalInstruction
+// The base class for different kinds of native instruction abstractions.
+// Provides the primitive operations to manipulate code relative to this.
+class NativeInstruction VALUE_OBJ_CLASS_SPEC {
+ friend class Relocation;
+
+ public:
+ enum Sparc_specific_constants {
+ nop_instruction_size = 4
+ };
+
+ bool is_nop() { return long_at(0) == nop_instruction(); }
+ bool is_call() { return is_op(long_at(0), Assembler::call_op); }
+ bool is_call_reg() { return is_op(long_at(0), Assembler::arith_op); }
+ bool is_sethi() { return (is_op2(long_at(0), Assembler::sethi_op2)
+ && inv_rd(long_at(0)) != G0); }
+
+ bool sets_cc() {
+ // conservative (returns true for some instructions that do not set the
+ // the condition code, such as, "save".
+ // Does not return true for the deprecated tagged instructions, such as, TADDcc
+ int x = long_at(0);
+ return (is_op(x, Assembler::arith_op) &&
+ (inv_op3(x) & Assembler::cc_bit_op3) == Assembler::cc_bit_op3);
+ }
+ bool is_illegal();
+ bool is_zombie() {
+ int x = long_at(0);
+ return (is_op3(x, Assembler::ldsw_op3, Assembler::ldst_op) &&
+ inv_rs1(x) == G0 && inv_rd(x) == O7);
+ }
+ bool is_ic_miss_trap(); // Inline-cache uses a trap to detect a miss
+ bool is_return() {
+ // is it the output of MacroAssembler::ret or MacroAssembler::retl?
+ int x = long_at(0);
+ const int pc_return_offset = 8; // see frame_sparc.hpp
+ return is_op3(x, Assembler::jmpl_op3, Assembler::arith_op)
+ && (inv_rs1(x) == I7 || inv_rs1(x) == O7)
+ && inv_immed(x) && inv_simm(x, 13) == pc_return_offset
+ && inv_rd(x) == G0;
+ }
+ bool is_int_jump() {
+ // is it the output of MacroAssembler::b?
+ int x = long_at(0);
+ return is_op2(x, Assembler::bp_op2) || is_op2(x, Assembler::br_op2);
+ }
+ bool is_float_jump() {
+ // is it the output of MacroAssembler::fb?
+ int x = long_at(0);
+ return is_op2(x, Assembler::fbp_op2) || is_op2(x, Assembler::fb_op2);
+ }
+ bool is_jump() {
+ return is_int_jump() || is_float_jump();
+ }
+ bool is_cond_jump() {
+ int x = long_at(0);
+ return (is_int_jump() && Assembler::inv_cond(x) != Assembler::always) ||
+ (is_float_jump() && Assembler::inv_cond(x) != Assembler::f_always);
+ }
+
+ bool is_stack_bang() {
+ int x = long_at(0);
+ return is_op3(x, Assembler::stw_op3, Assembler::ldst_op) &&
+ (inv_rd(x) == G0) && (inv_rs1(x) == SP) && (inv_rs2(x) == G3_scratch);
+ }
+
+ bool is_prefetch() {
+ int x = long_at(0);
+ return is_op3(x, Assembler::prefetch_op3, Assembler::ldst_op);
+ }
+
+ bool is_membar() {
+ int x = long_at(0);
+ return is_op3(x, Assembler::membar_op3, Assembler::arith_op) &&
+ (inv_rd(x) == G0) && (inv_rs1(x) == O7);
+ }
+
+ bool is_safepoint_poll() {
+ int x = long_at(0);
+ return is_op3(x, Assembler::ldx_op3, Assembler::ldst_op) &&
+ (inv_rd(x) == G0) && (inv_immed(x) ? Assembler::inv_simm13(x) == 0 : inv_rs2(x) == G0);
+ }
+
+ bool is_zero_test(Register ®);
+ bool is_load_store_with_small_offset(Register reg);
+
+ public:
+ static int nop_instruction() { return Assembler::op(Assembler::branch_op) | Assembler::op2(Assembler::sethi_op2); }
+ static int illegal_instruction(); // the output of __ breakpoint_trap()
+ static int call_instruction(address destination, address pc) { return Assembler::op(Assembler::call_op) | Assembler::wdisp((intptr_t)destination, (intptr_t)pc, 30); }
+
+protected:
+ address addr_at(int offset) const { return address(this) + offset; }
+ int long_at(int offset) const { return *(int*)addr_at(offset); }
+ void set_long_at(int offset, int i); /* deals with I-cache */
+ void set_jlong_at(int offset, jlong i); /* deals with I-cache */
+ void set_addr_at(int offset, address x); /* deals with I-cache */
+
+ address instruction_address() const { return addr_at(0); }
+ address next_instruction_address() const { return addr_at(BytesPerInstWord); }
+
+ static bool is_op( int x, Assembler::ops opval) {
+ return Assembler::inv_op(x) == opval;
+ }
+ static bool is_op2(int x, Assembler::op2s op2val) {
+ return Assembler::inv_op(x) == Assembler::branch_op && Assembler::inv_op2(x) == op2val;
+ }
+ static bool is_op3(int x, Assembler::op3s op3val, Assembler::ops opval) {
+ return Assembler::inv_op(x) == opval && Assembler::inv_op3(x) == op3val;
+ }
+
+ // utilities to help subclasses decode:
+ static Register inv_rd( int x ) { return Assembler::inv_rd( x); }
+ static Register inv_rs1( int x ) { return Assembler::inv_rs1(x); }
+ static Register inv_rs2( int x ) { return Assembler::inv_rs2(x); }
+
+ static bool inv_immed( int x ) { return Assembler::inv_immed(x); }
+ static bool inv_annul( int x ) { return (Assembler::annul(true) & x) != 0; }
+ static int inv_cond( int x ) { return Assembler::inv_cond(x); }
+
+ static int inv_op( int x ) { return Assembler::inv_op( x); }
+ static int inv_op2( int x ) { return Assembler::inv_op2(x); }
+ static int inv_op3( int x ) { return Assembler::inv_op3(x); }
+
+ static int inv_simm( int x, int nbits ) { return Assembler::inv_simm(x, nbits); }
+ static intptr_t inv_wdisp( int x, int nbits ) { return Assembler::inv_wdisp( x, 0, nbits); }
+ static intptr_t inv_wdisp16( int x ) { return Assembler::inv_wdisp16(x, 0); }
+ static int branch_destination_offset(int x) { return MacroAssembler::branch_destination(x, 0); }
+ static int patch_branch_destination_offset(int dest_offset, int x) {
+ return MacroAssembler::patched_branch(dest_offset, x, 0);
+ }
+
+ // utility for checking if x is either of 2 small constants
+ static bool is_either(int x, int k1, int k2) {
+ // return x == k1 || x == k2;
+ return (1 << x) & (1 << k1 | 1 << k2);
+ }
+
+ // utility for checking overflow of signed instruction fields
+ static bool fits_in_simm(int x, int nbits) {
+ // cf. Assembler::assert_signed_range()
+ // return -(1 << nbits-1) <= x && x < ( 1 << nbits-1),
+ return (unsigned)(x + (1 << nbits-1)) < (unsigned)(1 << nbits);
+ }
+
+ // set a signed immediate field
+ static int set_simm(int insn, int imm, int nbits) {
+ return (insn &~ Assembler::simm(-1, nbits)) | Assembler::simm(imm, nbits);
+ }
+
+ // set a wdisp field (disp should be the difference of two addresses)
+ static int set_wdisp(int insn, intptr_t disp, int nbits) {
+ return (insn &~ Assembler::wdisp((intptr_t)-4, (intptr_t)0, nbits)) | Assembler::wdisp(disp, 0, nbits);
+ }
+
+ static int set_wdisp16(int insn, intptr_t disp) {
+ return (insn &~ Assembler::wdisp16((intptr_t)-4, 0)) | Assembler::wdisp16(disp, 0);
+ }
+
+ // get a simm13 field from an arithmetic or memory instruction
+ static int get_simm13(int insn) {
+ assert(is_either(Assembler::inv_op(insn),
+ Assembler::arith_op, Assembler::ldst_op) &&
+ (insn & Assembler::immed(true)), "must have a simm13 field");
+ return Assembler::inv_simm(insn, 13);
+ }
+
+ // set the simm13 field of an arithmetic or memory instruction
+ static bool set_simm13(int insn, int imm) {
+ get_simm13(insn); // tickle the assertion check
+ return set_simm(insn, imm, 13);
+ }
+
+ // combine the fields of a sethi stream (7 instructions ) and an add, jmp or ld/st
+ static intptr_t data64( address pc, int arith_insn ) {
+ assert(is_op2(*(unsigned int *)pc, Assembler::sethi_op2), "must be sethi");
+ intptr_t hi = (intptr_t)gethi( (unsigned int *)pc );
+ intptr_t lo = (intptr_t)get_simm13(arith_insn);
+ assert((unsigned)lo < (1 << 10), "offset field of set_metadata must be 10 bits");
+ return hi | lo;
+ }
+
+ // Regenerate the instruction sequence that performs the 64 bit
+ // sethi. This only does the sethi. The disp field (bottom 10 bits)
+ // must be handled separately.
+ static void set_data64_sethi(address instaddr, intptr_t x);
+ static void verify_data64_sethi(address instaddr, intptr_t x);
+
+ // combine the fields of a sethi/simm13 pair (simm13 = or, add, jmpl, ld/st)
+ static int data32(int sethi_insn, int arith_insn) {
+ assert(is_op2(sethi_insn, Assembler::sethi_op2), "must be sethi");
+ int hi = Assembler::inv_hi22(sethi_insn);
+ int lo = get_simm13(arith_insn);
+ assert((unsigned)lo < (1 << 10), "offset field of set_metadata must be 10 bits");
+ return hi | lo;
+ }
+
+ static int set_data32_sethi(int sethi_insn, int imm) {
+ // note that Assembler::hi22 clips the low 10 bits for us
+ assert(is_op2(sethi_insn, Assembler::sethi_op2), "must be sethi");
+ return (sethi_insn &~ Assembler::hi22(-1)) | Assembler::hi22(imm);
+ }
+
+ static int set_data32_simm13(int arith_insn, int imm) {
+ get_simm13(arith_insn); // tickle the assertion check
+ int imm10 = Assembler::low10(imm);
+ return (arith_insn &~ Assembler::simm(-1, 13)) | Assembler::simm(imm10, 13);
+ }
+
+ static int low10(int imm) {
+ return Assembler::low10(imm);
+ }
+
+ // Perform the inverse of the LP64 Macroassembler::sethi
+ // routine. Extracts the 54 bits of address from the instruction
+ // stream. This routine must agree with the sethi routine in
+ // assembler_inline_sparc.hpp
+ static address gethi( unsigned int *pc ) {
+ int i = 0;
+ uintptr_t adr;
+ // We first start out with the real sethi instruction
+ assert(is_op2(*pc, Assembler::sethi_op2), "in gethi - must be sethi");
+ adr = (unsigned int)Assembler::inv_hi22( *(pc++) );
+ i++;
+ while ( i < 7 ) {
+ // We're done if we hit a nop
+ if ( (int)*pc == nop_instruction() ) break;
+ assert ( Assembler::inv_op(*pc) == Assembler::arith_op, "in gethi - must be arith_op" );
+ switch ( Assembler::inv_op3(*pc) ) {
+ case Assembler::xor_op3:
+ adr ^= (intptr_t)get_simm13( *pc );
+ return ( (address)adr );
+ break;
+ case Assembler::sll_op3:
+ adr <<= ( *pc & 0x3f );
+ break;
+ case Assembler::or_op3:
+ adr |= (intptr_t)get_simm13( *pc );
+ break;
+ default:
+ assert ( 0, "in gethi - Should not reach here" );
+ break;
+ }
+ pc++;
+ i++;
+ }
+ return ( (address)adr );
+ }
+
+ public:
+ void verify();
+ void print();
+
+ // unit test stuff
+ static void test() {} // override for testing
+
+ inline friend NativeInstruction* nativeInstruction_at(address address);
+};
+
+inline NativeInstruction* nativeInstruction_at(address address) {
+ NativeInstruction* inst = (NativeInstruction*)address;
+#ifdef ASSERT
+ inst->verify();
+#endif
+ return inst;
+}
+
+
+
+//-----------------------------------------------------------------------------
+
+// The NativeCall is an abstraction for accessing/manipulating native call imm32 instructions.
+// (used to manipulate inline caches, primitive & dll calls, etc.)
+inline NativeCall* nativeCall_at(address instr);
+inline NativeCall* nativeCall_overwriting_at(address instr,
+ address destination);
+inline NativeCall* nativeCall_before(address return_address);
+class NativeCall: public NativeInstruction {
+ public:
+ enum Sparc_specific_constants {
+ instruction_size = 8,
+ return_address_offset = 8,
+ call_displacement_width = 30,
+ displacement_offset = 0,
+ instruction_offset = 0
+ };
+ address instruction_address() const { return addr_at(0); }
+ address next_instruction_address() const { return addr_at(instruction_size); }
+ address return_address() const { return addr_at(return_address_offset); }
+
+ address destination() const { return inv_wdisp(long_at(0), call_displacement_width) + instruction_address(); }
+ address displacement_address() const { return addr_at(displacement_offset); }
+ void set_destination(address dest) { set_long_at(0, set_wdisp(long_at(0), dest - instruction_address(), call_displacement_width)); }
+ void set_destination_mt_safe(address dest);
+
+ void verify_alignment() {} // do nothing on sparc
+ void verify();
+ void print();
+
+ // unit test stuff
+ static void test();
+
+ // Creation
+ friend inline NativeCall* nativeCall_at(address instr);
+ friend NativeCall* nativeCall_overwriting_at(address instr, address destination = NULL) {
+ // insert a "blank" call:
+ NativeCall* call = (NativeCall*)instr;
+ call->set_long_at(0 * BytesPerInstWord, call_instruction(destination, instr));
+ call->set_long_at(1 * BytesPerInstWord, nop_instruction());
+ assert(call->addr_at(2 * BytesPerInstWord) - instr == instruction_size, "instruction size");
+ // check its structure now:
+ assert(nativeCall_at(instr)->destination() == destination, "correct call destination");
+ return call;
+ }
+
+ friend inline NativeCall* nativeCall_before(address return_address) {
+ NativeCall* call = (NativeCall*)(return_address - return_address_offset);
+ #ifdef ASSERT
+ call->verify();
+ #endif
+ return call;
+ }
+
+ static bool is_call_at(address instr) {
+ return nativeInstruction_at(instr)->is_call();
+ }
+
+ static bool is_call_before(address instr) {
+ return nativeInstruction_at(instr - return_address_offset)->is_call();
+ }
+
+ static bool is_call_to(address instr, address target) {
+ return nativeInstruction_at(instr)->is_call() &&
+ nativeCall_at(instr)->destination() == target;
+ }
+
+ // MT-safe patching of a call instruction.
+ static void insert(address code_pos, address entry) {
+ (void)nativeCall_overwriting_at(code_pos, entry);
+ }
+
+ static void replace_mt_safe(address instr_addr, address code_buffer);
+};
+inline NativeCall* nativeCall_at(address instr) {
+ NativeCall* call = (NativeCall*)instr;
+#ifdef ASSERT
+ call->verify();
+#endif
+ return call;
+}
+
+class NativeCallReg: public NativeInstruction {
+ public:
+ enum Sparc_specific_constants {
+ instruction_size = 8,
+ return_address_offset = 8,
+ instruction_offset = 0
+ };
+
+ address next_instruction_address() const {
+ return addr_at(instruction_size);
+ }
+};
+
+// The NativeFarCall is an abstraction for accessing/manipulating native call-anywhere
+// instructions in the sparcv9 vm. Used to call native methods which may be loaded
+// anywhere in the address space, possibly out of reach of a call instruction.
+
+// The format of this extended-range call is:
+// jumpl_to addr, lreg
+// == sethi %hi54(addr), O7 ; jumpl O7, %lo10(addr), O7 ; <delay>
+// That is, it is essentially the same as a NativeJump.
+class NativeFarCall;
+inline NativeFarCall* nativeFarCall_overwriting_at(address instr, address destination);
+inline NativeFarCall* nativeFarCall_at(address instr);
+class NativeFarCall: public NativeInstruction {
+ public:
+ enum Sparc_specific_constants {
+ // instruction_size includes the delay slot instruction.
+ instruction_size = 9 * BytesPerInstWord,
+ return_address_offset = 9 * BytesPerInstWord,
+ jmpl_offset = 7 * BytesPerInstWord,
+ displacement_offset = 0,
+ instruction_offset = 0
+ };
+ address instruction_address() const { return addr_at(0); }
+ address next_instruction_address() const { return addr_at(instruction_size); }
+ address return_address() const { return addr_at(return_address_offset); }
+
+ address destination() const {
+ return (address) data64(addr_at(0), long_at(jmpl_offset));
+ }
+ address displacement_address() const { return addr_at(displacement_offset); }
+ void set_destination(address dest);
+
+ bool destination_is_compiled_verified_entry_point();
+
+ void verify();
+ void print();
+
+ // unit test stuff
+ static void test();
+
+ // Creation
+ friend inline NativeFarCall* nativeFarCall_at(address instr) {
+ NativeFarCall* call = (NativeFarCall*)instr;
+ #ifdef ASSERT
+ call->verify();
+ #endif
+ return call;
+ }
+
+ friend inline NativeFarCall* nativeFarCall_overwriting_at(address instr, address destination = NULL) {
+ Unimplemented();
+ NativeFarCall* call = (NativeFarCall*)instr;
+ return call;
+ }
+
+ friend NativeFarCall* nativeFarCall_before(address return_address) {
+ NativeFarCall* call = (NativeFarCall*)(return_address - return_address_offset);
+ #ifdef ASSERT
+ call->verify();
+ #endif
+ return call;
+ }
+
+ static bool is_call_at(address instr);
+
+ // MT-safe patching of a call instruction.
+ static void insert(address code_pos, address entry) {
+ (void)nativeFarCall_overwriting_at(code_pos, entry);
+ }
+ static void replace_mt_safe(address instr_addr, address code_buffer);
+};
+
+
+// An interface for accessing/manipulating 32 bit native set_metadata imm, reg instructions
+// (used to manipulate inlined data references, etc.)
+// set_metadata imm, reg
+// == sethi %hi22(imm), reg ; add reg, %lo10(imm), reg
+class NativeMovConstReg32;
+inline NativeMovConstReg32* nativeMovConstReg32_at(address address);
+class NativeMovConstReg32: public NativeInstruction {
+ public:
+ enum Sparc_specific_constants {
+ sethi_offset = 0,
+ add_offset = 4,
+ instruction_size = 8
+ };
+
+ address instruction_address() const { return addr_at(0); }
+ address next_instruction_address() const { return addr_at(instruction_size); }
+
+ // (The [set_]data accessor respects oop_type relocs also.)
+ intptr_t data() const;
+ void set_data(intptr_t x);
+
+ // report the destination register
+ Register destination() { return inv_rd(long_at(sethi_offset)); }
+
+ void verify();
+ void print();
+
+ // unit test stuff
+ static void test();
+
+ // Creation
+ friend inline NativeMovConstReg32* nativeMovConstReg32_at(address address) {
+ NativeMovConstReg32* test = (NativeMovConstReg32*)address;
+ #ifdef ASSERT
+ test->verify();
+ #endif
+ return test;
+ }
+};
+
+// An interface for accessing/manipulating native set_metadata imm, reg instructions.
+// (used to manipulate inlined data references, etc.)
+// set_metadata imm, reg
+// == sethi %hi22(imm), reg ; add reg, %lo10(imm), reg
+class NativeMovConstReg;
+inline NativeMovConstReg* nativeMovConstReg_at(address address);
+class NativeMovConstReg: public NativeInstruction {
+ public:
+ enum Sparc_specific_constants {
+ sethi_offset = 0,
+ add_offset = 7 * BytesPerInstWord,
+ instruction_size = 8 * BytesPerInstWord
+ };
+
+ address instruction_address() const { return addr_at(0); }
+ address next_instruction_address() const { return addr_at(instruction_size); }
+
+ // (The [set_]data accessor respects oop_type relocs also.)
+ intptr_t data() const;
+ void set_data(intptr_t x);
+
+ // report the destination register
+ Register destination() { return inv_rd(long_at(sethi_offset)); }
+
+ void verify();
+ void print();
+
+ // unit test stuff
+ static void test();
+
+ // Creation
+ friend inline NativeMovConstReg* nativeMovConstReg_at(address address) {
+ NativeMovConstReg* test = (NativeMovConstReg*)address;
+ #ifdef ASSERT
+ test->verify();
+ #endif
+ return test;
+ }
+
+
+ friend NativeMovConstReg* nativeMovConstReg_before(address address) {
+ NativeMovConstReg* test = (NativeMovConstReg*)(address - instruction_size);
+ #ifdef ASSERT
+ test->verify();
+ #endif
+ return test;
+ }
+
+};
+
+
+// An interface for accessing/manipulating native set_metadata imm, reg instructions.
+// (used to manipulate inlined data references, etc.)
+// set_metadata imm, reg
+// == sethi %hi22(imm), reg; nop; add reg, %lo10(imm), reg
+//
+// Note that it is identical to NativeMovConstReg with the exception of a nop between the
+// sethi and the add. The nop is required to be in the delay slot of the call instruction
+// which overwrites the sethi during patching.
+class NativeMovConstRegPatching;
+inline NativeMovConstRegPatching* nativeMovConstRegPatching_at(address address);class NativeMovConstRegPatching: public NativeInstruction {
+ public:
+ enum Sparc_specific_constants {
+ sethi_offset = 0,
+ nop_offset = 7 * BytesPerInstWord,
+ add_offset = nop_offset + BytesPerInstWord,
+ instruction_size = add_offset + BytesPerInstWord
+ };
+
+ address instruction_address() const { return addr_at(0); }
+ address next_instruction_address() const { return addr_at(instruction_size); }
+
+ // (The [set_]data accessor respects oop_type relocs also.)
+ int data() const;
+ void set_data(int x);
+
+ // report the destination register
+ Register destination() { return inv_rd(long_at(sethi_offset)); }
+
+ void verify();
+ void print();
+
+ // unit test stuff
+ static void test();
+
+ // Creation
+ friend inline NativeMovConstRegPatching* nativeMovConstRegPatching_at(address address) {
+ NativeMovConstRegPatching* test = (NativeMovConstRegPatching*)address;
+ #ifdef ASSERT
+ test->verify();
+ #endif
+ return test;
+ }
+
+
+ friend NativeMovConstRegPatching* nativeMovConstRegPatching_before(address address) {
+ NativeMovConstRegPatching* test = (NativeMovConstRegPatching*)(address - instruction_size);
+ #ifdef ASSERT
+ test->verify();
+ #endif
+ return test;
+ }
+
+};
+
+
+// An interface for accessing/manipulating native memory ops
+// ld* [reg + offset], reg
+// st* reg, [reg + offset]
+// sethi %hi(imm), reg; add reg, %lo(imm), reg; ld* [reg1 + reg], reg2
+// sethi %hi(imm), reg; add reg, %lo(imm), reg; st* reg2, [reg1 + reg]
+// Ops covered: {lds,ldu,st}{w,b,h}, {ld,st}{d,x}
+//
+class NativeMovRegMem;
+inline NativeMovRegMem* nativeMovRegMem_at (address address);
+class NativeMovRegMem: public NativeInstruction {
+ public:
+ enum Sparc_specific_constants {
+ op3_mask_ld = 1 << Assembler::lduw_op3 |
+ 1 << Assembler::ldub_op3 |
+ 1 << Assembler::lduh_op3 |
+ 1 << Assembler::ldd_op3 |
+ 1 << Assembler::ldsw_op3 |
+ 1 << Assembler::ldsb_op3 |
+ 1 << Assembler::ldsh_op3 |
+ 1 << Assembler::ldx_op3,
+ op3_mask_st = 1 << Assembler::stw_op3 |
+ 1 << Assembler::stb_op3 |
+ 1 << Assembler::sth_op3 |
+ 1 << Assembler::std_op3 |
+ 1 << Assembler::stx_op3,
+ op3_ldst_int_limit = Assembler::ldf_op3,
+ op3_mask_ldf = 1 << (Assembler::ldf_op3 - op3_ldst_int_limit) |
+ 1 << (Assembler::lddf_op3 - op3_ldst_int_limit),
+ op3_mask_stf = 1 << (Assembler::stf_op3 - op3_ldst_int_limit) |
+ 1 << (Assembler::stdf_op3 - op3_ldst_int_limit),
+
+ offset_width = 13,
+ sethi_offset = 0,
+ add_offset = 7 * BytesPerInstWord,
+ ldst_offset = add_offset + BytesPerInstWord
+ };
+ bool is_immediate() const {
+ // check if instruction is ld* [reg + offset], reg or st* reg, [reg + offset]
+ int i0 = long_at(0);
+ return (is_op(i0, Assembler::ldst_op));
+ }
+
+ address instruction_address() const { return addr_at(0); }
+ address next_instruction_address() const {
+ return addr_at(is_immediate() ? 4 : (7 * BytesPerInstWord));
+ }
+ intptr_t offset() const {
+ return is_immediate()? inv_simm(long_at(0), offset_width) :
+ nativeMovConstReg_at(addr_at(0))->data();
+ }
+ void set_offset(intptr_t x) {
+ if (is_immediate()) {
+ guarantee(fits_in_simm(x, offset_width), "data block offset overflow");
+ set_long_at(0, set_simm(long_at(0), x, offset_width));
+ } else
+ nativeMovConstReg_at(addr_at(0))->set_data(x);
+ }
+
+ void add_offset_in_bytes(intptr_t radd_offset) {
+ set_offset (offset() + radd_offset);
+ }
+
+ void copy_instruction_to(address new_instruction_address);
+
+ void verify();
+ void print ();
+
+ // unit test stuff
+ static void test();
+
+ private:
+ friend inline NativeMovRegMem* nativeMovRegMem_at (address address) {
+ NativeMovRegMem* test = (NativeMovRegMem*)address;
+ #ifdef ASSERT
+ test->verify();
+ #endif
+ return test;
+ }
+};
+
+
+// An interface for accessing/manipulating native jumps
+// jump_to addr
+// == sethi %hi22(addr), temp ; jumpl reg, %lo10(addr), G0 ; <delay>
+// jumpl_to addr, lreg
+// == sethi %hi22(addr), temp ; jumpl reg, %lo10(addr), lreg ; <delay>
+class NativeJump;
+inline NativeJump* nativeJump_at(address address);
+class NativeJump: public NativeInstruction {
+ private:
+ void guarantee_displacement(int disp, int width) {
+ guarantee(fits_in_simm(disp, width + 2), "branch displacement overflow");
+ }
+
+ public:
+ enum Sparc_specific_constants {
+ sethi_offset = 0,
+ jmpl_offset = 7 * BytesPerInstWord,
+ instruction_size = 9 * BytesPerInstWord // includes delay slot
+ };
+
+ address instruction_address() const { return addr_at(0); }
+ address next_instruction_address() const { return addr_at(instruction_size); }
+
+ address jump_destination() const {
+ return (address) data64(instruction_address(), long_at(jmpl_offset));
+ }
+ void set_jump_destination(address dest) {
+ set_data64_sethi( instruction_address(), (intptr_t)dest);
+ set_long_at(jmpl_offset, set_data32_simm13( long_at(jmpl_offset), (intptr_t)dest));
+ }
+
+ // Creation
+ friend inline NativeJump* nativeJump_at(address address) {
+ NativeJump* jump = (NativeJump*)address;
+ #ifdef ASSERT
+ jump->verify();
+ #endif
+ return jump;
+ }
+
+ void verify();
+ void print();
+
+ // Unit testing stuff
+ static void test();
+
+ // Insertion of native jump instruction
+ static void insert(address code_pos, address entry);
+ // MT-safe insertion of native jump at verified method entry
+ static void check_verified_entry_alignment(address entry, address verified_entry) {
+ // nothing to do for sparc.
+ }
+ static void patch_verified_entry(address entry, address verified_entry, address dest);
+};
+
+
+
+// Despite the name, handles only simple branches.
+class NativeGeneralJump;
+inline NativeGeneralJump* nativeGeneralJump_at(address address);
+class NativeGeneralJump: public NativeInstruction {
+ public:
+ enum Sparc_specific_constants {
+ instruction_size = 8
+ };
+
+ address instruction_address() const { return addr_at(0); }
+ address jump_destination() const { return addr_at(0) + branch_destination_offset(long_at(0)); }
+ void set_jump_destination(address dest) {
+ int patched_instr = patch_branch_destination_offset(dest - addr_at(0), long_at(0));
+ set_long_at(0, patched_instr);
+ }
+ NativeInstruction *delay_slot_instr() { return nativeInstruction_at(addr_at(4));}
+ void fill_delay_slot(int instr) { set_long_at(4, instr);}
+ Assembler::Condition condition() {
+ int x = long_at(0);
+ return (Assembler::Condition) Assembler::inv_cond(x);
+ }
+
+ // Creation
+ friend inline NativeGeneralJump* nativeGeneralJump_at(address address) {
+ NativeGeneralJump* jump = (NativeGeneralJump*)(address);
+#ifdef ASSERT
+ jump->verify();
+#endif
+ return jump;
+ }
+
+ // Insertion of native general jump instruction
+ static void insert_unconditional(address code_pos, address entry);
+ static void replace_mt_safe(address instr_addr, address code_buffer);
+
+ void verify();
+};
+
+
+class NativeIllegalInstruction: public NativeInstruction {
+ public:
+ enum Sparc_specific_constants {
+ instruction_size = 4
+ };
+
+ // Insert illegal opcode as specific address
+ static void insert(address code_pos);
+};
+
+#endif // CPU_SPARC_VM_NATIVEINST_SPARC_HPP