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
Pipeline #109 failed with stage
in 13 minutes and 43 seconds
...@@ -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