view hotspot/src/cpu/mips/vm/nativeInst_mips.cpp @ 20:7a9f890eafef

Fix 2 bugs which are related to patching. NativeGeneralJump represents both long and short jump instructions, so they must be dealed respectively in patching. Otherwise patching produces wrong results.
author YANG Yongqiang <yangyongqiang@loongson.cn>
date Mon, 01 Nov 2010 17:41:18 +0800
parents c1e1428eff7c
children 388ae1bd0bdd
line wrap: on
line source

/*
 * Copyright 1997-2008 Sun Microsystems, Inc.  All Rights Reserved.
 * Copyright 2010 Lemote, Inc.  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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
 * CA 95054 USA or visit www.sun.com if you need additional information or
 * have any questions.
 *
 */

# include "incls/_precompiled.incl"
# include "incls/_nativeInst_mips.cpp.incl"

void NativeInstruction::wrote(int offset) {
  ICache::invalidate_word(addr_at(offset));
}

void NativeInstruction::set_long_at(int offset, int i) {
  address addr = addr_at(offset);
  *(int*)addr = i;
  //ICache::invalidate_word(addr);
}

void NativeCall::verify() {
	// make sure code pattern is actually a call instruction
	if ( !is_op(Assembler::lui_op) || 
		!is_op(long_at(4), Assembler::addiu_op) || 
		!is_special_op(long_at(8), Assembler::jalr_op) ) {
	 	fatal("not a call");
	}
}

static int illegal_instruction_bits = 0;

int NativeInstruction::illegal_instruction() {
	if (illegal_instruction_bits == 0) {
		ResourceMark rm;
		char buf[40];
		CodeBuffer cbuf((address)&buf[0], 20);     
		MacroAssembler* a = new MacroAssembler(&cbuf);     
		address ia = a->pc();     
		a->brk(11);
		int bits = *(int*)ia;
		illegal_instruction_bits = bits;   
	}
	return illegal_instruction_bits;
}

bool NativeInstruction::is_int_branch() {
	switch(Assembler::opcode(insn_word())) {
		case Assembler::beq_op:
		case Assembler::beql_op:
		case Assembler::bgtz_op:
		case Assembler::bgtzl_op:
		case Assembler::blez_op:
		case Assembler::blezl_op:
		case Assembler::bne_op:
		case Assembler::bnel_op:
			return true;
		case Assembler::regimm_op:
			switch(Assembler::rt(insn_word())) {
				case Assembler::bgez_op:
				case Assembler::bgezal_op:
				case Assembler::bgezall_op:
				case Assembler::bgezl_op:
				case Assembler::bltz_op:
				case Assembler::bltzal_op:
				case Assembler::bltzall_op:
				case Assembler::bltzl_op:
					return true;
			}
	}

	return false;
}

bool NativeInstruction::is_float_branch() {
	if (!is_op(Assembler::cop1_op) || 
			!is_rs((Register)Assembler::bc_op)) return false;

	switch(Assembler::rt(insn_word())) {
		case Assembler::bcf_op:
		case Assembler::bcfl_op:
		case Assembler::bct_op:
		case Assembler::bctl_op:
			return true;
	}

	return false;
}



address NativeCall::destination() const {
	return (address)Assembler::merge(long_at(4)&0xffff, long_at(0)&0xffff);
}

void NativeCall::print() {
  tty->print_cr(PTR_FORMAT ": call " PTR_FORMAT,
                instruction_address(), destination());
}

// Inserts a native call instruction at a given pc
void NativeCall::insert(address code_pos, address entry) {
  NativeCall *call = nativeCall_at(code_pos);
	CodeBuffer cb(call->addr_at(0), instruction_size);
	MacroAssembler masm(&cb);
#define __ masm.
	//__ move (T9, (int)entry);
	__ lui(T9, Assembler::split_high((int)entry));
	__ addiu(T9, T9, Assembler::split_low((int)entry));
	__ jalr ();
#undef __

	ICache::invalidate_range(call->addr_at(0), instruction_size);
	//  ICache::invalidate_all();
}

// MT-safe patching of a call instruction.
// First patches first word of instruction to two jmp's that jmps to them
// selfs (spinlock). Then patches the last byte, and then atomicly replaces
// the jmp's with the first 4 byte of the new instruction.
void NativeCall::replace_mt_safe(address instr_addr, address code_buffer) {
	Unimplemented();
}


void NativeMovConstReg::verify() {
  if ( !is_op(Assembler::lui_op) || 
	!is_op(long_at(4), Assembler::addiu_op) )
  fatal("not a mov reg, imm32")
}


void NativeMovConstReg::print() {
  tty->print_cr(PTR_FORMAT ": mov reg, " INTPTR_FORMAT,
              	instruction_address(), data());
}

void NativeMovConstReg::set_data(int x) {
	set_long_at(0, (long_at(0) & 0xffff0000) | (Assembler::split_high(x) & 0xffff));
	set_long_at(4, (long_at(4) & 0xffff0000) | (Assembler::split_low(x) & 0xffff));
	ICache::invalidate_range(addr_at(0), 8); 

	//  ICache::invalidate_all();
#ifndef CORE
	// also store the value into an oop_Relocation cell, if any
	CodeBlob* nm = CodeCache::find_blob(instruction_address());
	if (nm != NULL) {
		RelocIterator iter(nm, instruction_address(), instruction_address() + 1); 
		oop* oop_addr = NULL;
		while (iter.next()) {
			if (iter.type() == relocInfo::oop_type) {
				oop_Relocation *r = iter.oop_reloc();
				if (oop_addr == NULL && r->oop_index()!=0) {
					oop_addr = r->oop_addr();
					*oop_addr = (oop)x;
				} else {
					assert(oop_addr == r->oop_addr(), "must be only one set-oop here");
					}   
				}   
		}   
	}

#endif
}

//-------------------------------------------------------------------

int NativeMovRegMem::offset() const{
  if (is_immediate()) 
    return (short)(long_at(instruction_offset)&0xffff);
  else 
    return Assembler::merge(long_at(hiword_offset)&0xffff, long_at(instruction_offset)&0xffff);
}

void NativeMovRegMem::set_offset(int x) {
  if (is_immediate()) {
    assert(Assembler::is_simm16(x), "just check");
    set_long_at(0, (long_at(0)&0xffff0000) | (x&0xffff) );
    if (is_64ldst()) {
      assert(Assembler::is_simm16(x+4), "just check");
			set_long_at(4, (long_at(4)&0xffff0000) | ((x+4)&0xffff) );
		}
  } else {
    set_long_at(0, (long_at(0) & 0xffff0000) | (Assembler::split_high(x) & 0xffff));
    set_long_at(4, (long_at(4) & 0xffff0000) | (Assembler::split_low(x) & 0xffff));
  }
  ICache::invalidate_range(addr_at(0), 8);
}

void NativeMovRegMem::verify() {
	int offset = 0;
	
	if ( Assembler::opcode(long_at(0)) == Assembler::lui_op ) {
		if ( (Assembler::opcode(long_at(4)) != Assembler::addiu_op) ||
				(Assembler::opcode(long_at(8)) != Assembler::special_op) || 
				(Assembler::special(long_at(8)) != Assembler::add_op))
			fatal ("not a mov [reg+offs], reg instruction");
		offset += 12;
	}
	
	switch(Assembler::opcode(long_at(offset))) {
	case Assembler::lb_op:
	case Assembler::lbu_op:
	case Assembler::lh_op:
	case Assembler::lhu_op:
	case Assembler::lw_op:
	case Assembler::lwc1_op:
	case Assembler::sb_op:
	case Assembler::sh_op:
	case Assembler::sw_op:
	case Assembler::swc1_op:
		break;
	default:
		fatal ("not a mov [reg+offs], reg instruction");
	}
}


void NativeMovRegMem::print() {
  tty->print_cr("0x%x: mov reg, [reg + %x]", instruction_address(), offset());
}



void NativeIllegalInstruction::insert(address code_pos) {
  CodeBuffer cb(code_pos, instruction_size);
  MacroAssembler masm(&cb);
#define __ masm.
  __ brk(11);
#undef __

  ICache::invalidate_range(code_pos, instruction_size);
}

void NativeGeneralJump::verify() {
  assert(((NativeInstruction *)this)->is_jump() ||
         ((NativeInstruction *)this)->is_cond_jump(), "not a general jump instruction");
}


void  NativeGeneralJump::set_jump_destination(address dest) {
 	OrderAccess::fence();

	if (is_short()) {
		assert(Assembler::is_simm16(dest-addr_at(4)), "change this code");
		set_long_at(0, (long_at(0) & 0xffff0000) | (dest - addr_at(4)) & 0xffff );
		ICache::invalidate_range(addr_at(0), 4);
	} else {
		set_long_at(0, (long_at(0) & 0xffff0000) | (Assembler::split_high((int)dest) & 0xffff));
		set_long_at(4, (long_at(4) & 0xffff0000) | (Assembler::split_low((int)dest) & 0xffff));
		ICache::invalidate_range(addr_at(0), 8);
	}
}

// we now use b to do this. be careful when using this method
// by yjl 9/16/2005
void NativeGeneralJump::insert_unconditional(address code_pos, address entry) {
  CodeBuffer cb(code_pos, instruction_size + 4);
  MacroAssembler masm(&cb);
#define __ masm. 
//  __ lui(AT, Assembler::split_high((int)entry));
//  __ addiu(AT, AT, Assembler::split_low((int)entry));
//  __ jr (AT);
//  __ delayed()->nop();
  __ b(entry);
  __ delayed()->nop();
#undef __

  ICache::invalidate_range(code_pos, instruction_size + 4);
}


// MT-safe patching of a long jump instruction.
// First patches first word of instruction to two jmp's that jmps to them
// selfs (spinlock). Then patches the last byte, and then atomicly replaces
// the jmp's with the first 4 byte of the new instruction.
void NativeGeneralJump::replace_mt_safe(address instr_addr, address code_buffer) {
	NativeGeneralJump* h_jump =  nativeGeneralJump_at (instr_addr);
  int i0, i1 , i2;
  if(!h_jump->is_short()) {
	  i2 = ((int*)code_buffer)[2];
	  h_jump->set_long_at(2 * BytesPerInstWord, i2);
  }

	i1 = ((int*)code_buffer)[1];
	h_jump->set_long_at(1 * BytesPerInstWord, i1);
	i0 = ((int*)code_buffer)[0];
	h_jump->set_long_at(0 * BytesPerInstWord, i0);
	
	// beq ZERO, ZERO, -1
	// 0001 0000 0000 0000 1111 1111 1111 1111
  
	ICache::invalidate_range(h_jump->addr_at(0), instruction_size);
}

// NOTE : here i use T9 as the destination register, maybe i should get some hint from entry. FIXME
// by yjl 8/30/2005
void NativeGeneralJump::patch_verified_entry(address entry, address verified_entry, address dest) {
	unsigned int code_buffer[4];        
	address tmp = (address)code_buffer;
	// lui(T9, Assembler::split_high(dest))
	// 0011 1100 0001 1001 Assembler::split_high(dest)
	*(unsigned short *)tmp = (unsigned short)Assembler::split_high((int)dest);
	tmp += 2;
	*(unsigned short *)tmp = (unsigned short)(0x3c19);
	tmp += 2;
	//addiu(T9, T9, Assembler::split_low(dest))
	*(unsigned short *)tmp = (unsigned short)Assembler::split_low((int)dest);
	tmp += 2;
	*(unsigned short *)tmp = (unsigned short)(0x2739);
	tmp += 2;
	// jr(T9)
	*(unsigned int *)tmp = (unsigned int)0x03200008;
	tmp += 4;
	// nop
	*(unsigned int *)tmp = (unsigned int)0;

#ifndef CORE
	check_verified_entry_alignment(entry, verified_entry);
#endif /* CORE */

	*(unsigned int *)(verified_entry + 0)  = code_buffer[0];
	*(unsigned int *)(verified_entry + 4)  = code_buffer[1];
	*(unsigned int *)(verified_entry + 8)  = code_buffer[2];
	*(unsigned int *)(verified_entry + 12) = code_buffer[3];

	ICache::invalidate_range(verified_entry, instruction_size + 4);

	//ICache::invalidate_all();
}


bool NativeInstruction::is_dtrace_trap() {
  //return (*(int32_t*)this & 0xff) == 0xcc;
	Unimplemented();
	return false;
}