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 {
name: MuName,
code: Vec<ASMInst>,
blocks: HashMap<MuName, ASMBlock>
blocks: HashMap<MuName, ASMBlock>,
frame_size_patchpoints: Vec<ASMLocation>
}
unsafe impl Send for ASMCode {}
......@@ -110,12 +112,17 @@ impl ASMCode {
name: self.name.clone(),
code: vec![],
blocks: hashmap!{},
frame_size_patchpoints: vec![]
};
// iterate through old machine code
let mut inst_offset = 0; // how many instructions has been inserted
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() {
if self.is_block_start(i) {
cur_block_start = i + inst_offset;
......@@ -132,6 +139,9 @@ impl ASMCode {
// copy this instruction
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')
// update its info
// 1. fix defines and uses
......@@ -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();
Box::new(ret)
......@@ -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;
......@@ -494,6 +519,18 @@ impl MachineCode for ASMCode {
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> {
let mut ret = vec![];
......@@ -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 {
pub fn new() -> ASMCodeGen {
ASMCodeGen {
......@@ -1381,6 +1426,7 @@ impl CodeGenerator for ASMCodeGen {
name: func_name.clone(),
code: vec![],
blocks: hashmap! {},
frame_size_patchpoints: vec![]
}));
// to link with C sources via gcc
......@@ -1412,7 +1458,8 @@ impl CodeGenerator for ASMCodeGen {
self.cur = Some(Box::new(ASMCode {
name: "snippet".to_string(),
code: vec![],
blocks: hashmap! {}
blocks: hashmap! {},
frame_size_patchpoints: vec![]
}));
}
......@@ -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) {
trace!("emit: nop ({} bytes)", bytes);
......
......@@ -23,6 +23,9 @@ pub trait CodeGenerator {
fn set_block_liveout(&mut self, block_name: MuName, live_out: &Vec<P<Value>>);
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);
// comparison
......
......@@ -1497,6 +1497,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
let mut gpr_arg_count = 0;
let mut fpr_arg_count = 0;
......@@ -1574,6 +1578,9 @@ impl <'a> InstructionSelection {
}
}
// frame shrink
self.backend.emit_frame_shrink();
// pop all callee-saved registers - reverse order
for i in (0..x86_64::CALLEE_SAVED_GPRs.len()).rev() {
let ref reg = x86_64::CALLEE_SAVED_GPRs[i];
......
......@@ -11,6 +11,7 @@ use vm::VM;
use compiler::CompilerPass;
use compiler::backend::is_callee_saved;
use compiler::backend::init_machine_regs_for_func;
use utils::POINTER_SIZE;
use std::any::Any;
pub struct RegisterAllocation {
......@@ -60,6 +61,9 @@ impl RegisterAllocation {
}
// 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;
......@@ -70,11 +74,26 @@ impl RegisterAllocation {
.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);
for reg in removed_callee_saved {
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();
......
......@@ -56,6 +56,10 @@ impl Frame {
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> {
let slot = self.alloc_slot(&reg, vm);
slot.make_memory_op(reg.ty.clone(), vm)
......
......@@ -122,6 +122,7 @@ pub trait MachineCode {
fn get_block_range(&self, block: &str) -> Option<ops::Range<usize>>;
// functions for rewrite
/// replace a temp with a machine register (to_reg must be a machine register)
fn replace_reg(&mut self, from: MuID, to: MuID);
/// replace a temp that is defined in the inst with another temp
......@@ -133,6 +134,8 @@ pub trait MachineCode {
/// remove unnecessary push/pop if the callee saved register is not used
/// returns what registers push/pop have been deleted
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;
}
......
......@@ -4,6 +4,7 @@ use utils::vec_utils::as_str as vector_as_str;
use vm::VM;
use compiler::CompilerPass;
use std::collections::HashMap;
use std::any::Any;
pub struct ControlFlowAnalysis {
......@@ -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;
let switch_prob = (1.0f32 - BRANCH_DEFAULT_PROB) / (branches.len() as f32);
let mut ret : Vec<BlockEdge> = branches.iter().map(|pair| BlockEdge {
target: pair.1.target,
kind: check_edge_kind(pair.1.target, stack),
is_exception: false,
probability: switch_prob
}).collect();
let map : HashMap<MuID, BlockEdge> = {
let mut ret = HashMap::new();
// default
ret.push(BlockEdge {
target: default.target,
kind: check_edge_kind(default.target, stack),
let check_add_edge = |map: &mut HashMap<MuID, BlockEdge>, target: MuID, prob: f32| {
if map.contains_key(&target) {
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: BRANCH_DEFAULT_PROB
probability: prob
});
}
};
for &(_, ref dest) in branches.iter() {
let target = dest.target;
check_add_edge(&mut ret, target, switch_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
}
......
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