/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* 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.
*
*/
#include "asm/assembler.hpp"
#include "memory/allocation.hpp"
#include "runtime/icache.hpp"
// We have interface for the following instructions:
// - NativeInstruction
// - - NativeCall
// - - NativeFarCall
// - - NativeMovConstReg
// - - NativeMovConstRegPatching
// - - NativeMovRegMem
// - - NativeMovRegMemPatching
// - - NativeJump
// - - NativeGeneralJump
// - - NativeIllegalInstruction
// The base class for different kinds of native instruction abstractions.
// Provides the primitive operations to manipulate code relative to this.
friend class Relocation;
public:
enum Sparc_specific_constants {
};
bool is_dtrace_trap();
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);
}
bool is_illegal();
bool is_zombie() {
int x = long_at(0);
return is_op3(x,
}
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);
}
bool is_int_jump() {
// is it the output of MacroAssembler::b?
int x = long_at(0);
}
bool is_float_jump() {
// is it the output of MacroAssembler::fb?
int x = long_at(0);
}
bool is_jump() {
return is_int_jump() || is_float_jump();
}
bool is_cond_jump() {
int x = long_at(0);
}
bool is_stack_bang() {
int x = long_at(0);
}
bool is_prefetch() {
int x = long_at(0);
}
bool is_membar() {
int x = long_at(0);
}
bool is_safepoint_poll() {
int x = long_at(0);
#ifdef _LP64
#else
#endif
}
public:
#ifdef ASSERT
static int rdpc_instruction() { return Assembler::op(Assembler::arith_op ) | Assembler::op3(Assembler::rdreg_op3) | Assembler::u_field(5, 18, 14) | Assembler::rd(O7); }
#else
// Temporary fix: in optimized mode, u_field is a macro for efficiency reasons (see Assembler::u_field) - needs to be fixed
static int rdpc_instruction() { return Assembler::op(Assembler::arith_op ) | Assembler::op3(Assembler::rdreg_op3) | u_field(5, 18, 14) | Assembler::rd(O7); }
#endif
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); }
return Assembler::op(Assembler::branch_op) | Assembler::op2(op2val) | Assembler::annul(a) | Assembler::cond(c);
}
static int op3_instruction(Assembler::ops opval, Register rd, Assembler::op3s op3val, Register rs1, int simm13a) {
return Assembler::op(opval) | Assembler::rd(rd) | Assembler::op3(op3val) | Assembler::rs1(rs1) | Assembler::immed(true) | Assembler::simm(simm13a, 13);
}
return Assembler::op(Assembler::branch_op) | Assembler::rd(rd) | Assembler::op2(Assembler::sethi_op2) | Assembler::hi22(imm22a);
}
protected:
}
}
}
// utilities to help subclasses decode:
}
// utility for checking if x is either of 2 small constants
// return x == k1 || x == k2;
}
// utility for checking overflow of signed instruction fields
// cf. Assembler::assert_signed_range()
// return -(1 << nbits-1) <= x && x < ( 1 << nbits-1),
}
// set a signed immediate field
}
// set a wdisp field (disp should be the difference of two addresses)
return (insn &~ Assembler::wdisp((intptr_t)-4, (intptr_t)0, nbits)) | Assembler::wdisp(disp, 0, nbits);
}
}
// get a simm13 field from an arithmetic or memory instruction
}
// set the simm13 field of an arithmetic or memory instruction
}
}
// 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.
}
// note that Assembler::hi22 clips the low 10 bits for us
}
}
}
// 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
int i = 0;
// We first start out with the real sethi instruction
i++;
while ( i < 7 ) {
// We're done if we hit a nop
if ( (int)*pc == nop_instruction() ) break;
break;
break;
break;
default:
assert ( 0, "in gethi - Should not reach here" );
break;
}
pc++;
i++;
}
}
public:
void verify();
void print();
// unit test stuff
};
#ifdef ASSERT
#endif
return inst;
}
//-----------------------------------------------------------------------------
// The NativeCall is an abstraction for accessing/manipulating native call imm32 instructions.
// (used to manipulate inline caches, primitive & dll calls, etc.)
public:
enum Sparc_specific_constants {
displacement_offset = 0,
};
address destination() const { return inv_wdisp(long_at(0), call_displacement_width) + instruction_address(); }
void set_destination(address dest) { set_long_at(0, set_wdisp(long_at(0), dest - instruction_address(), call_displacement_width)); }
void verify();
void print();
// unit test stuff
static void test();
// Creation
// insert a "blank" call:
// check its structure now:
return call;
}
#ifdef ASSERT
#endif
return call;
}
}
}
}
// MT-safe patching of a call instruction.
}
};
#ifdef ASSERT
#endif
return call;
}
// 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.
#ifndef _LP64
// On 32-bit systems, a far call is the same as a near one.
class NativeFarCall;
public:
friend inline NativeFarCall* nativeFarCall_at(address instr) { return (NativeFarCall*)nativeCall_at(instr); }
};
#else
// 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;
public:
enum Sparc_specific_constants {
// instruction_size includes the delay slot instruction.
displacement_offset = 0,
};
}
void verify();
void print();
// unit test stuff
static void test();
// Creation
#ifdef ASSERT
#endif
return call;
}
friend inline NativeFarCall* nativeFarCall_overwriting_at(address instr, address destination = NULL) {
return call;
}
#ifdef ASSERT
#endif
return call;
}
// MT-safe patching of a call instruction.
}
};
#endif // _LP64
// An interface for accessing/manipulating native set_oop imm, reg instructions.
// (used to manipulate inlined data references, etc.)
// set_oop imm, reg
// == sethi %hi22(imm), reg ; add reg, %lo10(imm), reg
class NativeMovConstReg;
public:
enum Sparc_specific_constants {
sethi_offset = 0,
#ifdef _LP64
#else
add_offset = 4,
instruction_size = 8
#endif
};
// (The [set_]data accessor respects oop_type relocs also.)
// report the destination register
void verify();
void print();
// unit test stuff
static void test();
// Creation
#ifdef ASSERT
#endif
return test;
}
#ifdef ASSERT
#endif
return test;
}
};
// An interface for accessing/manipulating native set_oop imm, reg instructions.
// (used to manipulate inlined data references, etc.)
// set_oop 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,
#ifdef _LP64
#else
#endif
};
// (The [set_]data accessor respects oop_type relocs also.)
int data() const;
void set_data(int x);
// report the destination register
void verify();
void print();
// unit test stuff
static void test();
// Creation
#ifdef ASSERT
#endif
return test;
}
#ifdef ASSERT
#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;
public:
enum Sparc_specific_constants {
sethi_offset = 0,
#ifdef _LP64
#else
add_offset = 4,
#endif
};
bool is_immediate() const {
// check if instruction is ld* [reg + offset], reg or st* reg, [reg + offset]
}
#ifdef _LP64
#else
#endif
}
}
if (is_immediate()) {
} else
}
}
void verify();
void print ();
// unit test stuff
static void test();
private:
#ifdef ASSERT
#endif
return test;
}
};
// An interface for accessing/manipulating native memory ops
// ld* [reg + offset], reg
// st* reg, [reg + offset]
// sethi %hi(imm), reg; nop; add reg, %lo(imm), reg; ld* [reg1 + reg], reg2
// sethi %hi(imm), reg; nop; add reg, %lo(imm), reg; st* reg2, [reg1 + reg]
// Ops covered: {lds,ldu,st}{w,b,h}, {ld,st}{d,x}
//
// Note that it is identical to NativeMovRegMem 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 NativeMovRegMemPatching;
public:
enum Sparc_specific_constants {
sethi_offset = 0,
#ifdef _LP64
#else
nop_offset = 4,
#endif
};
bool is_immediate() const {
// check if instruction is ld* [reg + offset], reg or st* reg, [reg + offset]
}
}
int offset() const {
}
void set_offset(int x) {
if (is_immediate()) {
}
else
}
}
void verify();
void print ();
// unit test stuff
static void test();
private:
#ifdef ASSERT
#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;
private:
}
public:
enum Sparc_specific_constants {
sethi_offset = 0,
#ifdef _LP64
#else
#endif
};
#ifdef _LP64
}
}
#else
}
}
#endif
// Creation
#ifdef ASSERT
#endif
return jump;
}
void verify();
void print();
// Unit testing stuff
static void test();
// Insertion of native jump instruction
// MT-safe insertion of native jump at verified method entry
// nothing to do for sparc.
}
};
// Despite the name, handles only simple branches.
class NativeGeneralJump;
public:
enum Sparc_specific_constants {
};
set_long_at(0, patched_instr);
}
int x = long_at(0);
}
// Creation
#ifdef ASSERT
#endif
return jump;
}
// Insertion of native general jump instruction
void verify();
};
public:
enum Sparc_specific_constants {
};
// Insert illegal opcode as specific address
};
#endif // CPU_SPARC_VM_NATIVEINST_SPARC_HPP