Commit 9296466a authored by qinsoon's avatar qinsoon

implemented wpbranch

parent 2f973136
......@@ -38,4 +38,8 @@ pub fn spill_rewrite(
pub fn jit_patch_call_addr(dest: Address, inst_start: Address, length: ByteSize) {
unimplemented!()
}
pub fn jit_patch_jmp_addr(dest: Address, inst_start: Address, length: ByteSize) {
unimplemented!()
}
\ No newline at end of file
......@@ -3232,6 +3232,16 @@ impl CodeGenerator for ASMCodeGen {
self.add_asm_branch(asm, dest_name)
}
fn emit_wpbranch_jmp(
&mut self,
wpid: MuID,
target: MuName,
disable_target: MuName,
enable_target: MuName
) {
panic!("cannot emit wpbranch_jmp with asm backend (requires runtime code patching)")
}
fn emit_je(&mut self, dest_name: MuName) {
trace!("emit: je {}", dest_name);
......
......@@ -352,6 +352,32 @@ impl BinaryCodeGen {
)
}
fn add_xed_wpbranch(
&mut self,
inst: XedInstruction,
wpid: MuID,
target: MuName,
disable_target: MuName,
enable_target: MuName
) {
self.add_xed_inst_internal(
inst,
linked_hashmap!{},
linked_hashmap!{},
None,
false,
false,
None,
BranchTarget::WPBranch {
wpid: wpid,
target: target,
disable_target: disable_target,
enable_target: enable_target
},
None
)
}
fn add_xed_inst(
&mut self,
inst: XedInstruction,
......@@ -1179,6 +1205,33 @@ impl CodeGenerator for BinaryCodeGen {
self.add_xed_branch(inst, dest)
}
fn emit_wpbranch_jmp(
&mut self,
wpid: MuID,
default_target: MuName,
disable_target: MuName,
enable_target: MuName
) {
trace!(
"emit wpbranch #{} jmp {} (dis: {}, ena: {})",
wpid,
default_target,
disable_target,
enable_target
);
let target = xed_relbr(0, 32);
let inst = xed_inst1(
self.cur().state,
xed_iclass_enum_t::XED_ICLASS_JMP,
64,
target
);
self.add_xed_wpbranch(inst, wpid, default_target, disable_target, enable_target);
}
fn emit_je(&mut self, dest: MuName) {
unimplemented!()
}
......@@ -1556,6 +1609,7 @@ pub fn emit_code(fv: &mut MuFunctionVersion, vm: &VM) {
pub fn jit_patch_call_addr(dest: Address, inst_start: Address, length: ByteSize) {
use std::i32;
info!("patch call instruction at {} to call {}", inst_start, dest);
let call_offset = dest.diff_offset(inst_start + length);
......@@ -1586,3 +1640,37 @@ pub fn jit_patch_call_addr(dest: Address, inst_start: Address, length: ByteSize)
// do nothing
}
}
pub fn jit_patch_jmp_addr(dest: Address, inst_start: Address, length: ByteSize) {
use std::i32;
info!("patch jump instruction at {} to jmp {}", inst_start, dest);
let jmp_offset = dest.diff_offset(inst_start + length);
assert!(
jmp_offset >= i32::MIN as isize && jmp_offset <= i32::MAX as isize,
"jmp offset is not suitable for a jmp"
);
let inst = xed_inst1(
xed_state_64(),
xed_iclass_enum_t::XED_ICLASS_JMP,
64,
xed_relbr(jmp_offset as i32, 32)
);
let new_len = encode_inst(
&inst,
inst_start.to_ptr_mut(),
XED_MAX_INSTRUCTION_BYTES as usize
);
if new_len < length {
warn!("need to pad with a few nops");
unimplemented!();
} else if new_len > length {
panic!("patching used more space than we have");
} else {
// do nothing
}
}
......@@ -51,7 +51,7 @@ pub struct XedContext {
pub insts_addr: Vec<Address>,
pub insts_len: Vec<ByteSize>,
pub callsite_addr: HashMap<MuName, Address>
pub symbol_addr: HashMap<MuName, Address>
}
impl XedContext {
......@@ -65,7 +65,7 @@ impl XedContext {
frame_size_patchpoints: vec![],
insts_addr: vec![],
insts_len: vec![],
callsite_addr: HashMap::new()
symbol_addr: HashMap::new()
}
}
......@@ -280,7 +280,7 @@ impl XedContext {
pub fn generate_code(&mut self, code_ptr: Address, len: ByteSize, vm: &VM) -> ByteSize {
info!("code mission");
// generate code and recording branches and block start address for back patching
// generate code and recording callsite symbol-address mapping for back patching
let code_length = {
let mut cur_ptr = code_ptr;
let mut used = 0;
......@@ -308,7 +308,7 @@ impl XedContext {
match self.insts_info[i].call {
Some(JITCallsite { ref name, .. }) => {
self.callsite_addr.insert(name.clone(), cur_ptr);
self.symbol_addr.insert(name.clone(), cur_ptr);
}
None => {}
}
......@@ -322,19 +322,52 @@ impl XedContext {
used
};
// generate a mapping between block name and address
{
for (blk_name, blk_info) in self.blocks.iter() {
let addr = self.insts_addr[blk_info.start_inst];
self.symbol_addr.insert(blk_name.clone(), addr);
}
}
// another pass through the code, record patchpoints
info!("record patch point...");
for i in 0..self.insts.len() {
let inst_addr = self.insts_addr[i];
let inst_len = self.insts_len[i];
// add patchpoint for callsite
if let Some(ref callsite) = self.insts_info[i].call {
let inst_addr = self.insts_addr[i];
let patchpoint = RuntimePatchpoint::inst(inst_addr, self.insts_len[i]);
let patchpoint = RuntimePatchpoint::call_inst(inst_addr, inst_len);
trace!(
"added callsite for func {} at {}",
"added callsite patchpoint for func {}: {:?}",
callsite.callee_id,
inst_addr
patchpoint
);
vm.add_callsite_patchpoint(callsite.callee_id, patchpoint);
}
// add patchpoint for wpbranch
match self.insts_info[i].branch {
// we dont care which target we actually jump to at the moment
// we only record information
BranchTarget::WPBranch {
wpid,
ref disable_target,
ref enable_target,
..
} => {
let disable_addr = *self.symbol_addr.get(disable_target).unwrap();
let enable_addr = *self.symbol_addr.get(enable_target).unwrap();
// add a wpbranch patchpoint for the watchpoint
let patchpoint =
RuntimePatchpoint::wpbranch(inst_addr, inst_len, disable_addr, enable_addr);
trace!("added wpbranch patchpoint for {}: {:?}", wpid, patchpoint);
vm.add_wpbranch_patchpoint(wpid, patchpoint);
}
_ => {}
}
}
info!("back patching...");
......@@ -421,7 +454,8 @@ impl XedContext {
// we may need to patch offsets
if let Some(new_inst) = match info.branch {
BranchTarget::Unconditional(ref target) => {
BranchTarget::Unconditional(ref target) |
BranchTarget::WPBranch { ref target, .. } => {
let block_start = self.get_block_range(target).unwrap().start;
// the target address we need to jump to
let target_addr = self.insts_addr[block_start];
......@@ -429,7 +463,7 @@ impl XedContext {
let branch_offset = target_addr.diff_offset(cur_ip);
trace!(
"unconditional branch to inst {} of 0x{:x}, with offset {}",
"unconditional branch/wpbranch to inst {} of 0x{:x}, with offset {}",
block_start,
target_addr,
branch_offset
......@@ -482,7 +516,7 @@ impl XedContext {
// we need to resolve the address for the label
// check if it is a local callsite label
if let Some(label_addr) = self.callsite_addr.get(name) {
if let Some(label_addr) = self.symbol_addr.get(name) {
patch_disp(patchpoint, *label_addr)
} else {
let addr = resolve_symbol(name.clone());
......@@ -699,6 +733,71 @@ impl XedContext {
panic!("PEI does not have a fallthrough target");
}
}
BranchTarget::WPBranch {
wpid,
ref disable_target,
ref enable_target,
..
} => {
// we may jump to default target or alt target
// branch to default
{
let target_n = self.blocks.get(disable_target).unwrap().start_inst;
// cur inst's succ is target
code[i].succs.push(target_n);
// target's pred is cur
code[target_n].preds.push(i);
trace_if!(
TRACE_CFA,
"inst {}: is a wpbranch with default branch to {}",
i,
target_n
);
trace_if!(TRACE_CFA, "inst {}: branch target index is {}", i, target_n);
trace_if!(
TRACE_CFA,
"inst {}: set SUCCS as branch target {}",
i,
target_n
);
trace_if!(
TRACE_CFA,
"inst {}: set PREDS as branch source {}",
target_n,
i
);
}
// branch to alternative
{
let target_n = self.blocks.get(enable_target).unwrap().start_inst;
// cur inst's succ is target
code[i].succs.push(target_n);
// target's pred is cur
code[target_n].preds.push(i);
trace_if!(
TRACE_CFA,
"inst {}: is a wpbranch with default branch to {}",
i,
target_n
);
trace_if!(TRACE_CFA, "inst {}: branch target index is {}", i, target_n);
trace_if!(
TRACE_CFA,
"inst {}: set SUCCS as branch target {}",
i,
target_n
);
trace_if!(
TRACE_CFA,
"inst {}: set PREDS as branch source {}",
target_n,
i
);
}
}
BranchTarget::Return => {
trace_if!(TRACE_CFA, "inst {}: is a return", i);
trace_if!(TRACE_CFA, "inst {}: has no successor", i);
......@@ -1125,6 +1224,13 @@ pub enum BranchTarget {
Unconditional(MuName),
/// this instruction may throw exception to target
PotentiallyExcepting(MuName),
/// this instruction branch to default target, but may branch to alt target
WPBranch {
wpid: MuID,
target: MuName,
disable_target: MuName,
enable_target: MuName
},
/// this instruction is a return
Return
}
......
......@@ -20,6 +20,7 @@ pub use self::binary_codegen::BinaryCodeGen;
pub use self::binary_codegen::init_jit;
pub use self::binary_codegen::emit_code;
pub use self::binary_codegen::jit_patch_call_addr;
pub use self::binary_codegen::jit_patch_jmp_addr;
mod code_context;
......
......@@ -224,6 +224,14 @@ pub trait CodeGenerator {
fn emit_jle(&mut self, dest: MuName);
fn emit_js(&mut self, dest: MuName);
fn emit_wpbranch_jmp(
&mut self,
wpid: MuID,
target: MuName,
disable_target: MuName,
enable_target: MuName
);
// call
fn emit_call_near_rel32(
&mut self,
......
......@@ -2175,6 +2175,7 @@ impl<'a> InstructionSelection {
vm
);
}
Instruction_::Watchpoint {
ref id,
ref disable_dest,
......@@ -2188,6 +2189,30 @@ impl<'a> InstructionSelection {
self.emit_trap(node, inst, resume, f_content, f_context, vm);
}
}
Instruction_::WPBranch {
wp,
ref disable_dest,
ref enable_dest
} => {
trace!("instsel on WPBRANCH");
let ref ops = inst.ops;
self.process_dest(&ops, disable_dest, f_content, f_context, vm);
self.process_dest(&ops, enable_dest, f_content, f_context, vm);
let disable_target = f_content.get_block(disable_dest.target).name();
let enable_target = f_content.get_block(enable_dest.target).name();
vm.ensure_watchpoint_registered(wp);
let default_target = if vm.is_watchpoint_enabled(wp) {
enable_target.clone()
} else {
disable_target.clone()
};
self.backend
.emit_wpbranch_jmp(wp, default_target, disable_target, enable_target);
}
Instruction_::PrintHex(index) => {
trace!("instsel on PRINTHEX");
......
......@@ -135,6 +135,9 @@ pub use compiler::backend::x86_64::binary_backend::spill_rewrite as binary_spill
#[cfg(feature = "binary-codegen")]
pub use compiler::backend::x86_64::binary_backend::jit_patch_call_addr;
#[cfg(target_arch = "x86_64")]
#[cfg(feature = "binary-codegen")]
pub use compiler::backend::x86_64::binary_backend::jit_patch_jmp_addr;
#[cfg(target_arch = "x86_64")]
pub use compiler::backend::x86_64::ARGUMENT_GPRS;
#[cfg(target_arch = "x86_64")]
pub use compiler::backend::x86_64::ARGUMENT_FPRS;
......@@ -226,6 +229,9 @@ pub use compiler::backend::aarch64::binary_backend::spill_rewrite as binary_spil
#[cfg(feature = "binary-codegen")]
pub use compiler::backend::aarch64::binary_backend::jit_patch_call_addr;
#[cfg(target_arch = "aarch64")]
#[cfg(feature = "binary-codegen")]
pub use compiler::backend::aarch64::binary_backend::jit_patch_jmp_addr;
#[cfg(target_arch = "aarch64")]
pub use compiler::backend::aarch64::ARGUMENT_GPRS;
#[cfg(target_arch = "aarch64")]
pub use compiler::backend::aarch64::ARGUMENT_FPRS;
......
......@@ -15,6 +15,7 @@
extern crate hprof;
use ast::ir::*;
use runtime::trap::Watchpoint;
use vm::VM;
/// compiler passes
......@@ -196,12 +197,35 @@ pub fn update_callsite_in_old_code(func_id: MuID, func_start: Address, vm: &VM)
use runtime::patchpoint::*;
let patch_table = vm.callsite_patch_table().read().unwrap();
for patchpoint in patch_table.get(&func_id).unwrap().iter() {
assert!(patchpoint.v == RuntimePatchpoint_::Instruction);
assert!(patchpoint.v == RuntimePatchpoint_::CallInst);
backend::jit_patch_call_addr(func_start, patchpoint.start, patchpoint.length);
}
}
pub fn patch_watchpoint(to_enable: bool, wp: &Watchpoint) {
use runtime::patchpoint::*;
// patch for wpbranch inst
for patchpoint in wp.wpbranch_patchpoints.iter() {
match patchpoint.v {
RuntimePatchpoint_::WPBranch {
disable_dest,
enable_dest
} => {
if to_enable {
backend::jit_patch_jmp_addr(enable_dest, patchpoint.start, patchpoint.length);
} else {
backend::jit_patch_jmp_addr(disable_dest, patchpoint.start, patchpoint.length);
}
}
_ => panic!("expected wpbranch patchpoint, found {:?}", patchpoint)
}
}
// TODO: patch for watchpoint inst
}
// rewrite parts of the hprof crates to print via log (instead of print!())
use self::hprof::ProfileNode;
use std::rc::Rc;
......
......@@ -264,7 +264,44 @@ impl CompilerPass for GenMovPhi {
trace!("rewrite to {}", new_inst);
new_body.push(new_inst);
}
Instruction_::WPBranch { .. } => unimplemented!(),
Instruction_::WPBranch {
wp,
disable_dest,
enable_dest
} => {
let disable_dest = process_dest(
disable_dest,
&mut new_blocks_to_insert,
&ops,
vm,
&inst_name,
"wpbranch_disable"
);
let enable_dest = process_dest(
enable_dest,
&mut new_blocks_to_insert,
&ops,
vm,
&inst_name,
"wpbranch_enable"
);
// rewrite the instruction
let new_inst = func.new_inst(Instruction {
hdr: inst.hdr.clone(),
value: inst.value.clone(),
ops: ops.to_vec(),
keepalives: inst.keepalives.clone(),
v: Instruction_::WPBranch {
wp: wp,
disable_dest: disable_dest,
enable_dest: enable_dest
}
});
trace!("rewrite to {}", new_inst);
new_body.push(new_inst);
}
Instruction_::SwapStackExc {
stack,
is_exception,
......
......@@ -12,17 +12,37 @@ pub struct RuntimePatchpoint {
rodal_named!(RuntimePatchpoint);
impl RuntimePatchpoint {
pub fn inst(start: Address, length: ByteSize) -> RuntimePatchpoint {
pub fn call_inst(start: Address, length: ByteSize) -> RuntimePatchpoint {
RuntimePatchpoint {
start: start,
length: length,
v: RuntimePatchpoint_::Instruction
v: RuntimePatchpoint_::CallInst
}
}
pub fn wpbranch(
start: Address,
length: ByteSize,
disable_dest: Address,
enable_dest: Address
) -> RuntimePatchpoint {
RuntimePatchpoint {
start: start,
length: length,
v: RuntimePatchpoint_::WPBranch {
disable_dest: disable_dest,
enable_dest: enable_dest
}
}
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum RuntimePatchpoint_ {
Instruction,
CallInst,
WPBranch {
disable_dest: Address,
enable_dest: Address
},
Offset(ByteSize)
}
\ No newline at end of file
......@@ -4,6 +4,7 @@ use vm::api::prepare_handle;
use vm::api::api_c::*;
use vm::handle::*;
use runtime::thread::MuThread;
use runtime::patchpoint::RuntimePatchpoint;
use utils::Address;
use utils::WORD_SIZE;
......@@ -197,3 +198,10 @@ pub extern "C" fn muentry_trap(wpid: MuID, arg_loc: Address, stack_arg_loc: Addr
_ => panic!("expected trap result: {}", ret_trap_result)
}
}
pub struct Watchpoint {
pub enabled: bool,
pub wpbranch_patchpoints: Vec<RuntimePatchpoint>
}
rodal_named!(Watchpoint);
\ No newline at end of file
......@@ -36,6 +36,7 @@ use runtime::*;
use runtime::mm as gc;
use runtime::patchpoint::RuntimePatchpoint;
use runtime::trap::TrapHandler;
use runtime::trap::Watchpoint;
use runtime::thread::{FrameCursor, FrameCursorStepResult};
use vm::handle::*;
use vm::vm_options::VMOptions;
......@@ -138,6 +139,11 @@ pub struct VM {
/// We keep this as a separate table because we want to index it with function ID
callsite_patchpoints: RwLock<HashMap<MuID, Vec<RuntimePatchpoint>>>,
/// watchpoints in current VM. This serves for multiple uses:
/// * track status of watchpoints (enabled or disabled)
/// * track patchpoints for WPBranch
watchpoints: RwLock<HashMap<WPID, Watchpoint>>,
/// registered trap handler for the client
trap_handler: RwLock<Option<TrapHandler>>,
......@@ -197,6 +203,10 @@ unsafe impl rodal::Dump for VM {
RwLock::new(rodal::EmptyHashMap::<MuID, Vec<RuntimePatchpoint>>::new());
dumper.dump_object_here(&callsite_patchpoints);
dumper.dump_padding(&self.watchpoints);
let watchpoints = RwLock::new(rodal::EmptyHashMap::<WPID, Watchpoint>::new());
dumper.dump_object_here(&watchpoints);
dumper.dump_padding(&self.trap_handler);
let empty_handler = RwLock::new(rodal::EmptyOption::<TrapHandler>::new());
dumper.dump_object_here(&empty_handler);
......@@ -282,6 +292,7 @@ impl<'a> VM {
compiled_callsite_table: RwLock::new(HashMap::new()),
callsite_count: ATOMIC_USIZE_INIT,
callsite_patchpoints: RwLock::new(HashMap::new()),
watchpoints: RwLock::new(HashMap::new()),
trap_handler: RwLock::new(None),
pending_joins: Mutex::new(LinkedList::new()),
framecursors: Mutex::new(HashMap::new())
......@@ -335,6 +346,7 @@ impl<'a> VM {
aot_pending_funcref_store: RwLock::new(HashMap::new()),
compiled_callsite_table: RwLock::new(HashMap::new()),
callsite_patchpoints: RwLock::new(HashMap::new()),
wpbranch_patchpoints: RwLock::new(HashMap::new()),
trap_handler: RwLock::new(None),
callsite_count: ATOMIC_USIZE_INIT,
pending_joins: Mutex::new(LinkedList::new()),
......@@ -493,6 +505,96 @@ impl<'a> VM {
}
}
/// registers a watchpoint
pub fn ensure_watchpoint_registered(&self, wpid: WPID) {
let mut table = self.watchpoints.write().unwrap();
if table.contains_key(&wpid) {
warn!("watchpoint {} was registered, do nothing", wpid);
} else {
info!("registering watchpoint {}", wpid);
table.insert(
wpid,
Watchpoint {
enabled: false,
wpbranch_patchpoints: vec![]
}
);
}
}
/// adds a wpbranch patchpoint
pub fn add_wpbranch_patchpoint(&self, wpid: WPID, patchpoint: RuntimePatchpoint) {
let mut table = self.watchpoints.write().unwrap();
if table.contains_key(&wpid) {
let mut wp = table.get_mut(&wpid).unwrap();
wp.wpbranch_patchpoints.push(patchpoint);
} else {
// we do not have a watchpoint for it yet
panic!(
"trying to add a patchpoint for wpbranch, \
but the watchpoint {} is not registered yet",
wpid
)
}
}
/// checks if a watchpoint is enabled
pub fn is_watchpoint_enabled(&self, wpid: WPID) -> bool {
// it is possible that we haven't compile the code yet
// thus we do not know about the watchpoint
self.ensure_watchpoint_registered(wpid);
let table = self.watchpoints.read().unwrap();
if table.contains_key(&wpid) {
table.get(&wpid).unwrap().enabled
} else {
panic!("watchpoint {} is not registered", wpid)
}
}
/// enables a watchpoint
pub fn enable_watchpoint(&self, wpid: WPID) {
info!("enabling watchpoint {}", wpid);
// it is possible that we haven't compile the code yet
// thus we do not know about the watchpoint
self.ensure_watchpoint_registered(wpid);
let mut table = self.watchpoints.write().unwrap();
match table.get_mut(&wpid) {
Some(ref mut wp) => {
assert!(!wp.enabled);
wp.enabled = true;
compiler::patch_watchpoint(true, wp);
}
None => unreachable!()
}
}
/// disables a watchpoint
pub fn disable_watchpoint(&self, wpid: MuID) {
info!("disabling watchpoint {}", wpid);
// it is possible that we haven't compile the code yet
// thus we do not know about the watchpoint
self.ensure_watchpoint_registered(wpid);
let mut table = self.watchpoints.write().unwrap();
match table.get_mut(&wpid) {
Some(ref mut wp) => {
assert!(wp.enabled);
wp.enabled = false;
compiler::patch_watchpoint(false, wp);
}
None => unreachable!()
}
}