GitLab will be upgraded on June 2nd 2020 at 2.00 pm (AEDT) to 3.00 pm (AEDT) due to Critical Security Patch Availability. During the update, GitLab and Mattermost services will not be available. If you have any concerns with this, please talk to local Gitlab admin team.

Commit 5b723a45 authored by qinsoon's avatar qinsoon

fib in test-jit runs

1. grow/shrink frame size in the pro/epilogue. Note: though we removed
push/pop for unused callee saved registers, we still reserve frame space
for them. Because we generate spill on frame before we know exactly how
large a space is. The solution is to make spill locations patchable.
2. control flow will combine branches in switch instruction if they all
target the same destination.
parent a5af3d18
...@@ -26,7 +26,9 @@ struct ASMCode { ...@@ -26,7 +26,9 @@ struct ASMCode {
name: MuName, name: MuName,
code: Vec<ASMInst>, code: Vec<ASMInst>,
blocks: HashMap<MuName, ASMBlock> blocks: HashMap<MuName, ASMBlock>,
frame_size_patchpoints: Vec<ASMLocation>
} }
unsafe impl Send for ASMCode {} unsafe impl Send for ASMCode {}
...@@ -110,12 +112,17 @@ impl ASMCode { ...@@ -110,12 +112,17 @@ impl ASMCode {
name: self.name.clone(), name: self.name.clone(),
code: vec![], code: vec![],
blocks: hashmap!{}, blocks: hashmap!{},
frame_size_patchpoints: vec![]
}; };
// iterate through old machine code // iterate through old machine code
let mut inst_offset = 0; // how many instructions has been inserted let mut inst_offset = 0; // how many instructions has been inserted
let mut cur_block_start = usize::MAX; let mut cur_block_start = usize::MAX;
// inst N in old machine code is N' in new machine code
// this map stores the relationship
let mut location_map : HashMap<usize, usize> = HashMap::new();
for i in 0..self.number_of_insts() { for i in 0..self.number_of_insts() {
if self.is_block_start(i) { if self.is_block_start(i) {
cur_block_start = i + inst_offset; cur_block_start = i + inst_offset;
...@@ -132,6 +139,9 @@ impl ASMCode { ...@@ -132,6 +139,9 @@ impl ASMCode {
// copy this instruction // copy this instruction
let mut inst = self.code[i].clone(); let mut inst = self.code[i].clone();
// old ith inst is now the (i + inst_offset)th instruction
location_map.insert(i, i + inst_offset);
// this instruction has been offset by several instructions('inst_offset') // this instruction has been offset by several instructions('inst_offset')
// update its info // update its info
// 1. fix defines and uses // 1. fix defines and uses
...@@ -178,6 +188,17 @@ impl ASMCode { ...@@ -178,6 +188,17 @@ impl ASMCode {
} }
} }
// fix patchpoint
for patchpoint in self.frame_size_patchpoints.iter() {
let new_patchpoint = ASMLocation {
line: *location_map.get(&patchpoint.line).unwrap(),
index: patchpoint.index,
len: patchpoint.len
};
ret.frame_size_patchpoints.push(new_patchpoint);
}
ret.control_flow_analysis(); ret.control_flow_analysis();
Box::new(ret) Box::new(ret)
...@@ -323,6 +344,10 @@ impl ASMCode { ...@@ -323,6 +344,10 @@ impl ASMCode {
} }
} }
} }
fn add_frame_size_patchpoint(&mut self, patchpoint: ASMLocation) {
self.frame_size_patchpoints.push(patchpoint);
}
} }
use std::any::Any; use std::any::Any;
...@@ -493,6 +518,18 @@ impl MachineCode for ASMCode { ...@@ -493,6 +518,18 @@ impl MachineCode for ASMCode {
regs_to_remove regs_to_remove
} }
fn patch_frame_size(&mut self, size: usize) {
let size = size.to_string();
debug_assert!(size.len() <= FRAME_SIZE_PLACEHOLDER_LEN);
for loc in self.frame_size_patchpoints.iter() {
let ref mut inst = self.code[loc.line];
string_utils::replace(&mut inst.code, loc.index, &size, size.len());
}
}
fn emit(&self) -> Vec<u8> { fn emit(&self) -> Vec<u8> {
let mut ret = vec![]; let mut ret = vec![];
...@@ -676,6 +713,14 @@ lazy_static! { ...@@ -676,6 +713,14 @@ lazy_static! {
}; };
} }
const FRAME_SIZE_PLACEHOLDER_LEN : usize = 10; // a frame is smaller than 1 << 10
lazy_static! {
pub static ref FRAME_SIZE_PLACEHOLDER : String = {
let blank_spaces = [' ' as u8; FRAME_SIZE_PLACEHOLDER_LEN];
format!("{}", str::from_utf8(&blank_spaces).unwrap())
};
}
impl ASMCodeGen { impl ASMCodeGen {
pub fn new() -> ASMCodeGen { pub fn new() -> ASMCodeGen {
ASMCodeGen { ASMCodeGen {
...@@ -1381,6 +1426,7 @@ impl CodeGenerator for ASMCodeGen { ...@@ -1381,6 +1426,7 @@ impl CodeGenerator for ASMCodeGen {
name: func_name.clone(), name: func_name.clone(),
code: vec![], code: vec![],
blocks: hashmap! {}, blocks: hashmap! {},
frame_size_patchpoints: vec![]
})); }));
// to link with C sources via gcc // to link with C sources via gcc
...@@ -1412,7 +1458,8 @@ impl CodeGenerator for ASMCodeGen { ...@@ -1412,7 +1458,8 @@ impl CodeGenerator for ASMCodeGen {
self.cur = Some(Box::new(ASMCode { self.cur = Some(Box::new(ASMCode {
name: "snippet".to_string(), name: "snippet".to_string(),
code: vec![], code: vec![],
blocks: hashmap! {} blocks: hashmap! {},
frame_size_patchpoints: vec![]
})); }));
} }
...@@ -1520,6 +1567,38 @@ impl CodeGenerator for ASMCodeGen { ...@@ -1520,6 +1567,38 @@ impl CodeGenerator for ASMCodeGen {
} }
} }
fn emit_frame_grow(&mut self) {
trace!("emit frame grow");
let asm = format!("addq $-{},%rsp", FRAME_SIZE_PLACEHOLDER.clone());
let line = self.line();
self.cur_mut().add_frame_size_patchpoint(ASMLocation::new(line, 7, FRAME_SIZE_PLACEHOLDER_LEN));
self.add_asm_inst(
asm,
hashmap!{}, // let reg alloc ignore this instruction
hashmap!{},
false
)
}
fn emit_frame_shrink(&mut self) {
trace!("emit frame shrink");
let asm = format!("addq ${},%rsp", FRAME_SIZE_PLACEHOLDER.clone());
let line = self.line();
self.cur_mut().add_frame_size_patchpoint(ASMLocation::new(line, 6, FRAME_SIZE_PLACEHOLDER_LEN));
self.add_asm_inst(
asm,
hashmap!{},
hashmap!{},
false
)
}
fn emit_nop(&mut self, bytes: usize) { fn emit_nop(&mut self, bytes: usize) {
trace!("emit: nop ({} bytes)", bytes); trace!("emit: nop ({} bytes)", bytes);
......
...@@ -22,6 +22,9 @@ pub trait CodeGenerator { ...@@ -22,6 +22,9 @@ pub trait CodeGenerator {
fn set_block_livein(&mut self, block_name: MuName, live_in: &Vec<P<Value>>); fn set_block_livein(&mut self, block_name: MuName, live_in: &Vec<P<Value>>);
fn set_block_liveout(&mut self, block_name: MuName, live_out: &Vec<P<Value>>); fn set_block_liveout(&mut self, block_name: MuName, live_out: &Vec<P<Value>>);
fn end_block(&mut self, block_name: MuName); fn end_block(&mut self, block_name: MuName);
fn emit_frame_grow(&mut self);
fn emit_frame_shrink(&mut self);
fn emit_nop(&mut self, bytes: usize); fn emit_nop(&mut self, bytes: usize);
......
...@@ -1496,6 +1496,10 @@ impl <'a> InstructionSelection { ...@@ -1496,6 +1496,10 @@ impl <'a> InstructionSelection {
} }
} }
} }
// reserve spaces for current frame
// add x, rbp -> rbp (x is negative, however we do not know x now)
self.backend.emit_frame_grow();
// unload arguments // unload arguments
let mut gpr_arg_count = 0; let mut gpr_arg_count = 0;
...@@ -1542,7 +1546,7 @@ impl <'a> InstructionSelection { ...@@ -1542,7 +1546,7 @@ impl <'a> InstructionSelection {
fn emit_common_epilogue(&mut self, ret_inst: &Instruction, f_content: &FunctionContent, f_context: &mut FunctionContext, vm: &VM) { fn emit_common_epilogue(&mut self, ret_inst: &Instruction, f_content: &FunctionContent, f_context: &mut FunctionContext, vm: &VM) {
// epilogue is not a block (its a few instruction inserted before return) // epilogue is not a block (its a few instruction inserted before return)
// FIXME: this may change in the future // FIXME: this may change in the future
// prepare return regs // prepare return regs
let ref ops = ret_inst.ops.read().unwrap(); let ref ops = ret_inst.ops.read().unwrap();
let ret_val_indices = match ret_inst.v { let ret_val_indices = match ret_inst.v {
...@@ -1572,7 +1576,10 @@ impl <'a> InstructionSelection { ...@@ -1572,7 +1576,10 @@ impl <'a> InstructionSelection {
} else { } else {
unimplemented!(); unimplemented!();
} }
} }
// frame shrink
self.backend.emit_frame_shrink();
// pop all callee-saved registers - reverse order // pop all callee-saved registers - reverse order
for i in (0..x86_64::CALLEE_SAVED_GPRs.len()).rev() { for i in (0..x86_64::CALLEE_SAVED_GPRs.len()).rev() {
......
...@@ -11,6 +11,7 @@ use vm::VM; ...@@ -11,6 +11,7 @@ use vm::VM;
use compiler::CompilerPass; use compiler::CompilerPass;
use compiler::backend::is_callee_saved; use compiler::backend::is_callee_saved;
use compiler::backend::init_machine_regs_for_func; use compiler::backend::init_machine_regs_for_func;
use utils::POINTER_SIZE;
use std::any::Any; use std::any::Any;
pub struct RegisterAllocation { pub struct RegisterAllocation {
...@@ -60,6 +61,9 @@ impl RegisterAllocation { ...@@ -60,6 +61,9 @@ impl RegisterAllocation {
} }
// find out what callee saved registers are used // find out what callee saved registers are used
// FIXME: current not doing this
// reason: we generated frame slots for callee saved registers, then generated slots for spills
// if we delete some callee saved registers, the slots for spills are not correct
{ {
use std::collections::HashSet; use std::collections::HashSet;
...@@ -70,11 +74,26 @@ impl RegisterAllocation { ...@@ -70,11 +74,26 @@ impl RegisterAllocation {
.collect(); .collect();
let used_callee_saved: Vec<MuID> = used_callee_saved.into_iter().collect(); let used_callee_saved: Vec<MuID> = used_callee_saved.into_iter().collect();
let n_used_callee_saved = used_callee_saved.len();
let removed_callee_saved = coloring.cf.mc_mut().remove_unnecessary_callee_saved(used_callee_saved); let removed_callee_saved = coloring.cf.mc_mut().remove_unnecessary_callee_saved(used_callee_saved);
for reg in removed_callee_saved { for reg in removed_callee_saved {
coloring.cf.frame.remove_record_for_callee_saved_reg(reg); coloring.cf.frame.remove_record_for_callee_saved_reg(reg);
} }
// patch frame size
// size for callee saved regs
let size_for_callee_saved_regs = n_used_callee_saved * POINTER_SIZE;
trace!("callee saved registers used {} bytes", size_for_callee_saved_regs);
let total_frame_size = coloring.cf.frame.cur_size();
trace!("frame reserved for {} bytes", total_frame_size);
let size_to_patch = total_frame_size - size_for_callee_saved_regs;
trace!("patching the code to grow/shrink size of {} bytes", size_to_patch);
coloring.cf.mc_mut().patch_frame_size(size_to_patch);
} }
coloring.cf.mc().trace_mc(); coloring.cf.mc().trace_mc();
......
...@@ -55,6 +55,10 @@ impl Frame { ...@@ -55,6 +55,10 @@ impl Frame {
pub fn cur_offset(&self) -> isize { pub fn cur_offset(&self) -> isize {
self.cur_offset self.cur_offset
} }
pub fn cur_size(&self) -> usize {
self.cur_offset.abs() as usize
}
pub fn alloc_slot_for_callee_saved_reg(&mut self, reg: P<Value>, vm: &VM) -> P<Value> { pub fn alloc_slot_for_callee_saved_reg(&mut self, reg: P<Value>, vm: &VM) -> P<Value> {
let slot = self.alloc_slot(&reg, vm); let slot = self.alloc_slot(&reg, vm);
......
...@@ -122,6 +122,7 @@ pub trait MachineCode { ...@@ -122,6 +122,7 @@ pub trait MachineCode {
fn get_block_range(&self, block: &str) -> Option<ops::Range<usize>>; fn get_block_range(&self, block: &str) -> Option<ops::Range<usize>>;
// functions for rewrite // functions for rewrite
/// replace a temp with a machine register (to_reg must be a machine register) /// replace a temp with a machine register (to_reg must be a machine register)
fn replace_reg(&mut self, from: MuID, to: MuID); fn replace_reg(&mut self, from: MuID, to: MuID);
/// replace a temp that is defined in the inst with another temp /// replace a temp that is defined in the inst with another temp
...@@ -133,6 +134,8 @@ pub trait MachineCode { ...@@ -133,6 +134,8 @@ pub trait MachineCode {
/// remove unnecessary push/pop if the callee saved register is not used /// remove unnecessary push/pop if the callee saved register is not used
/// returns what registers push/pop have been deleted /// returns what registers push/pop have been deleted
fn remove_unnecessary_callee_saved(&mut self, used_callee_saved: Vec<MuID>) -> Vec<MuID>; fn remove_unnecessary_callee_saved(&mut self, used_callee_saved: Vec<MuID>) -> Vec<MuID>;
/// patch frame size
fn patch_frame_size(&mut self, size: usize);
fn as_any(&self) -> &Any; fn as_any(&self) -> &Any;
} }
......
...@@ -4,6 +4,7 @@ use utils::vec_utils::as_str as vector_as_str; ...@@ -4,6 +4,7 @@ use utils::vec_utils::as_str as vector_as_str;
use vm::VM; use vm::VM;
use compiler::CompilerPass; use compiler::CompilerPass;
use std::collections::HashMap;
use std::any::Any; use std::any::Any;
pub struct ControlFlowAnalysis { pub struct ControlFlowAnalysis {
...@@ -94,20 +95,39 @@ fn dfs(cur: MuID, stack: &mut Vec<MuID>, visited: &mut Vec<MuID>, func: &mut MuF ...@@ -94,20 +95,39 @@ fn dfs(cur: MuID, stack: &mut Vec<MuID>, visited: &mut Vec<MuID>, func: &mut MuF
const BRANCH_DEFAULT_PROB : f32 = 0.1; const BRANCH_DEFAULT_PROB : f32 = 0.1;
let switch_prob = (1.0f32 - BRANCH_DEFAULT_PROB) / (branches.len() as f32); let switch_prob = (1.0f32 - BRANCH_DEFAULT_PROB) / (branches.len() as f32);
let mut ret : Vec<BlockEdge> = branches.iter().map(|pair| BlockEdge { let map : HashMap<MuID, BlockEdge> = {
target: pair.1.target, let mut ret = HashMap::new();
kind: check_edge_kind(pair.1.target, stack),
is_exception: false, let check_add_edge = |map: &mut HashMap<MuID, BlockEdge>, target: MuID, prob: f32| {
probability: switch_prob if map.contains_key(&target) {
}).collect(); let mut edge : &mut BlockEdge = map.get_mut(&target).unwrap();
edge.probability += prob;
} else {
map.insert(target, BlockEdge{
target: target,
kind: check_edge_kind(target, stack),
is_exception: false,
probability: prob
});
}
};
// default for &(_, ref dest) in branches.iter() {
ret.push(BlockEdge { let target = dest.target;
target: default.target,
kind: check_edge_kind(default.target, stack), check_add_edge(&mut ret, target, switch_prob);
is_exception: false, }
probability: BRANCH_DEFAULT_PROB
}); check_add_edge(&mut ret, default.target, BRANCH_DEFAULT_PROB);
ret
};
let mut ret = vec![];
for edge in map.values() {
ret.push(*edge);
}
ret ret
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment