To protect your data, the CISO officer has suggested users to enable GitLab 2FA as soon as possible.

Commit d404c0e3 authored by Zixian Cai's avatar Zixian Cai
Browse files

Merge branch 'develop' into new_ci

parents fd0a18c2 844ff350
...@@ -519,6 +519,14 @@ impl ASMCode { ...@@ -519,6 +519,14 @@ impl ASMCode {
use std::any::Any; use std::any::Any;
impl MachineCode for ASMCode { impl MachineCode for ASMCode {
fn is_nop(&self, index: usize) -> bool {
let ref inst = self.code[index];
if inst.code == "" || inst.code == "NOP" {
true
} else {
false
}
}
fn as_any(&self) -> &Any { fn as_any(&self) -> &Any {
self self
} }
......
...@@ -851,6 +851,16 @@ impl MachineCode for ASMCode { ...@@ -851,6 +851,16 @@ impl MachineCode for ASMCode {
self.code[index].code.clear(); self.code[index].code.clear();
} }
/// is the specified index is a nop?
fn is_nop(&self, index: usize) -> bool {
let ref inst = self.code[index];
if inst.code == "" || inst.code == "nop" {
true
} else {
false
}
}
/// 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, and the number of callee saved registers /// returns what registers push/pop have been deleted, and the number of callee saved registers
/// that weren't deleted /// that weren't deleted
......
...@@ -1643,6 +1643,7 @@ impl<'a> InstructionSelection { ...@@ -1643,6 +1643,7 @@ impl<'a> InstructionSelection {
// FIXME: the semantic of Pin/Unpin is different from spec // FIXME: the semantic of Pin/Unpin is different from spec
// See Issue #33 // See Issue #33
Instruction_::CommonInst_Pin(op) => { Instruction_::CommonInst_Pin(op) => {
use runtime::mm::GC_MOVES_OBJECT;
trace!("instsel on PIN"); trace!("instsel on PIN");
// call pin() in GC // call pin() in GC
...@@ -1653,17 +1654,23 @@ impl<'a> InstructionSelection { ...@@ -1653,17 +1654,23 @@ impl<'a> InstructionSelection {
let tmp_op = self.emit_ireg(op, f_content, f_context, vm); let tmp_op = self.emit_ireg(op, f_content, f_context, vm);
let tmp_res = self.get_result_value(node); let tmp_res = self.get_result_value(node);
self.emit_runtime_entry( if GC_MOVES_OBJECT {
&entrypoints::PIN_OBJECT, self.emit_runtime_entry(
vec![tmp_op.clone()], &entrypoints::PIN_OBJECT,
Some(vec![tmp_res]), vec![tmp_op.clone()],
Some(node), Some(vec![tmp_res]),
f_content, Some(node),
f_context, f_content,
vm f_context,
); vm
);
} else {
// FIXME: this is problematic, as we are not keeping the object alive
self.backend.emit_mov_r_r(&tmp_res, &tmp_op);
}
} }
Instruction_::CommonInst_Unpin(op) => { Instruction_::CommonInst_Unpin(op) => {
use runtime::mm::GC_MOVES_OBJECT;
trace!("instsel on UNPIN"); trace!("instsel on UNPIN");
// call unpin() in GC // call unpin() in GC
...@@ -1673,15 +1680,17 @@ impl<'a> InstructionSelection { ...@@ -1673,15 +1680,17 @@ impl<'a> InstructionSelection {
assert!(self.match_ireg(op)); assert!(self.match_ireg(op));
let tmp_op = self.emit_ireg(op, f_content, f_context, vm); let tmp_op = self.emit_ireg(op, f_content, f_context, vm);
self.emit_runtime_entry( if GC_MOVES_OBJECT {
&entrypoints::UNPIN_OBJECT, self.emit_runtime_entry(
vec![tmp_op.clone()], &entrypoints::UNPIN_OBJECT,
None, vec![tmp_op.clone()],
Some(node), None,
f_content, Some(node),
f_context, f_content,
vm f_context,
); vm
);
}
} }
Instruction_::CommonInst_GetAddr(op) => { Instruction_::CommonInst_GetAddr(op) => {
trace!("instsel on GETADDR"); trace!("instsel on GETADDR");
...@@ -2672,6 +2681,12 @@ impl<'a> InstructionSelection { ...@@ -2672,6 +2681,12 @@ impl<'a> InstructionSelection {
1 | 2 | 4 | 8 => { 1 | 2 | 4 | 8 => {
trace!("emit mul"); trace!("emit mul");
// we need to emit both operands first, then move one into RAX
let tmp_op1 = self.emit_ireg(op1, f_content, f_context, vm);
let tmp_op2 = self.emit_ireg(op2, f_content, f_context, vm);
// move op1 -> RAX
let mreg_op1 = match op_size { let mreg_op1 = match op_size {
8 => x86_64::RAX.clone(), 8 => x86_64::RAX.clone(),
4 => x86_64::EAX.clone(), 4 => x86_64::EAX.clone(),
...@@ -2679,38 +2694,10 @@ impl<'a> InstructionSelection { ...@@ -2679,38 +2694,10 @@ impl<'a> InstructionSelection {
1 => x86_64::AL.clone(), 1 => x86_64::AL.clone(),
_ => unimplemented!() _ => unimplemented!()
}; };
self.backend.emit_mov_r_r(&mreg_op1, &tmp_op1);
if self.match_iimm(op1) {
let imm_op1 = self.node_iimm_to_i32(op1);
self.backend.emit_mov_r_imm(&mreg_op1, imm_op1);
} else if self.match_mem(op1) {
let mem_op1 = self.emit_mem(op1, vm);
self.backend.emit_mov_r_mem(&mreg_op1, &mem_op1);
} else if self.match_ireg(op1) {
let reg_op1 = self.emit_ireg(op1, f_content, f_context, vm);
self.backend.emit_mov_r_r(&mreg_op1, &reg_op1);
} else {
panic!("unexpected op1 for node {:?}", node)
}
// mul op2 // mul op2
if self.match_iimm(op2) { self.backend.emit_mul_r(&tmp_op2);
let imm_op2 = self.node_iimm_to_i32(op2);
// put imm in a temporary
// here we use result reg as temporary
self.backend.emit_mov_r_imm(&res_tmp, imm_op2);
self.backend.emit_mul_r(&res_tmp);
} else if self.match_mem(op2) {
let mem_op2 = self.emit_mem(op2, vm);
self.backend.emit_mul_mem(&mem_op2);
} else if self.match_ireg(op2) {
let reg_op2 = self.emit_ireg(op2, f_content, f_context, vm);
self.backend.emit_mul_r(&reg_op2);
} else {
panic!("unexpected op2 for node {:?}", node)
}
// mov rax -> result // mov rax -> result
let res_size = vm.get_backend_type_size(res_tmp.ty.id()); let res_size = vm.get_backend_type_size(res_tmp.ty.id());
...@@ -3437,69 +3424,22 @@ impl<'a> InstructionSelection { ...@@ -3437,69 +3424,22 @@ impl<'a> InstructionSelection {
) )
} }
} else { } else {
// size is unknown at compile time // directly call 'alloc'
// we need to emit both alloc small and alloc large, let tmp_res = self.get_result_value(node);
// and it is decided at runtime
// emit: cmp size, THRESHOLD
// emit: jg ALLOC_LARGE
// emit: >> small object alloc
// emit: jmp ALLOC_LARGE_END
// emit: ALLOC_LARGE:
// emit: >> large object alloc
// emit: ALLOC_LARGE_END:
let blk_alloc_large = make_block_name(&node.name(), "alloc_large");
let blk_alloc_large_end = make_block_name(&node.name(), "alloc_large_end");
if OBJECT_HEADER_SIZE != 0 {
// if the header size is not zero, we need to calculate a total size to alloc
let size_with_hdr = self.make_temporary(f_context, UINT64_TYPE.clone(), vm);
self.backend.emit_mov_r_r(&size_with_hdr, &size);
self.backend
.emit_add_r_imm(&size_with_hdr, OBJECT_HEADER_SIZE as i32);
self.backend
.emit_cmp_imm_r(mm::LARGE_OBJECT_THRESHOLD as i32, &size_with_hdr);
} else {
self.backend
.emit_cmp_imm_r(mm::LARGE_OBJECT_THRESHOLD as i32, &size);
}
self.backend.emit_jg(blk_alloc_large.clone());
self.finish_block();
let block_name = make_block_name(&node.name(), "allocsmall");
self.start_block(block_name);
// alloc small here let const_align = self.make_int_const(align as u64, vm);
self.emit_alloc_sequence_small(
tmp_allocator.clone(),
size.clone(),
align,
node,
f_content,
f_context,
vm
);
self.backend.emit_jmp(blk_alloc_large_end.clone());
// finishing current block
self.finish_block();
// alloc_large: self.emit_runtime_entry(
self.start_block(blk_alloc_large.clone()); &entrypoints::ALLOC_ANY,
self.emit_alloc_sequence_large( vec![tmp_allocator.clone(), size.clone(), const_align],
tmp_allocator.clone(), Some(vec![tmp_res.clone()]),
size, Some(node),
align,
node,
f_content, f_content,
f_context, f_context,
vm vm
); );
self.finish_block();
// alloc_large_end:
self.start_block(blk_alloc_large_end.clone());
self.get_result_value(node) tmp_res
} }
} }
......
...@@ -37,10 +37,19 @@ impl CompilerPass for PeepholeOptimization { ...@@ -37,10 +37,19 @@ impl CompilerPass for PeepholeOptimization {
let compiled_funcs = vm.compiled_funcs().read().unwrap(); let compiled_funcs = vm.compiled_funcs().read().unwrap();
let mut cf = compiled_funcs.get(&func.id()).unwrap().write().unwrap(); let mut cf = compiled_funcs.get(&func.id()).unwrap().write().unwrap();
// remove redundant move first
for i in 0..cf.mc().number_of_insts() { for i in 0..cf.mc().number_of_insts() {
cf.mc().trace_inst(i);
// if two sides of a move instruction are the same, // if two sides of a move instruction are the same,
// it is redundant, and can be eliminated // it is redundant, and can be eliminated
trace!("trying to remove redundant move");
self.remove_redundant_move(i, &mut cf); self.remove_redundant_move(i, &mut cf);
}
// then remove jumps (because removing movs will affect this)
for i in 0..cf.mc().number_of_insts() {
cf.mc().trace_inst(i);
// if a branch jumps a label that contains another jump, such as // if a branch jumps a label that contains another jump, such as
// .. // ..
...@@ -53,9 +62,11 @@ impl CompilerPass for PeepholeOptimization { ...@@ -53,9 +62,11 @@ impl CompilerPass for PeepholeOptimization {
// the order matters: we need to run this first, then remove_unnecessary_jump() // the order matters: we need to run this first, then remove_unnecessary_jump()
// as this will give us more chances to remove unnecessary jumps // as this will give us more chances to remove unnecessary jumps
trace!("trying to remove jump-to-jump");
self.remove_jump_to_jump(i, &mut cf); self.remove_jump_to_jump(i, &mut cf);
// if a branch targets a block that immediately follow it, it can be eliminated // if a branch targets a block that immediately follow it, it can be eliminated
trace!("trying to remove unnecessary jmp");
self.remove_unnecessary_jump(i, &mut cf); self.remove_unnecessary_jump(i, &mut cf);
} }
...@@ -74,8 +85,6 @@ impl PeepholeOptimization { ...@@ -74,8 +85,6 @@ impl PeepholeOptimization {
fn remove_redundant_move(&mut self, inst: usize, cf: &mut CompiledFunction) { fn remove_redundant_move(&mut self, inst: usize, cf: &mut CompiledFunction) {
// if this instruction is a move, and move from register to register (no memory operands) // if this instruction is a move, and move from register to register (no memory operands)
if cf.mc().is_move(inst) && !cf.mc().is_using_mem_op(inst) { if cf.mc().is_move(inst) && !cf.mc().is_using_mem_op(inst) {
cf.mc().trace_inst(inst);
// get source reg/temp ID // get source reg/temp ID
let src: MuID = { let src: MuID = {
let uses = cf.mc().get_inst_reg_uses(inst); let uses = cf.mc().get_inst_reg_uses(inst);
...@@ -166,34 +175,48 @@ impl PeepholeOptimization { ...@@ -166,34 +175,48 @@ impl PeepholeOptimization {
let opt_dest = mc.is_jmp(cur_inst); let opt_dest = mc.is_jmp(cur_inst);
match opt_dest { match opt_dest {
Some(ref dest) => { Some(ref dest) => {
trace!("current instruction {} jumps to {}", cur_inst, dest);
// if we have already visited this instruction // if we have already visited this instruction
// this means we met an infinite loop, we need to break // this means we met an infinite loop, we need to break
if visited_labels.contains(dest) { if visited_labels.contains(dest) {
warn!("met an infinite loop in removing jump-to-jump"); warn!("met an infinite loop in removing jump-to-jump");
warn!("we are not optimizing this case"); warn!("we are not optimizing this case");
return; return;
} else {
visited_labels.insert(dest.clone());
debug!("visited {}", dest);
} }
// get the block for destination // get the block for destination
let first_inst = mc.get_block_range(dest).unwrap().start; let first_inst = {
debug_assert!( let start = mc.get_block_range(dest).unwrap().start;
mc.is_label(first_inst).is_none(), let last = mc.number_of_insts();
"expect start inst {} of \
block {} is a inst instead of label", let mut first = start;
for i in start..last {
if mc.is_label(i).is_some() || mc.is_nop(i) {
continue;
} else {
first = i;
break;
}
}
first
};
trace!(
"examining first valid inst {} from block {}",
first_inst, first_inst,
dest dest
); );
trace!("examining first inst {} of block {}", first_inst, dest);
// if first instruction is jump // if first instruction is jump
match mc.is_jmp(first_inst) { match mc.is_jmp(first_inst) {
Some(ref dest2) => { Some(ref dest2) => {
// its a jump-to-jump case // its a jump-to-jump case
cur_inst = first_inst; cur_inst = first_inst;
last_dest = Some(dest2.clone()); last_dest = Some(dest2.clone());
visited_labels.insert(dest2.clone());
debug!("visited {}", dest2);
} }
None => break None => break
} }
......
...@@ -20,13 +20,9 @@ use compiler::backend; ...@@ -20,13 +20,9 @@ use compiler::backend;
use utils::LinkedHashSet; use utils::LinkedHashSet;
use utils::LinkedHashMap; use utils::LinkedHashMap;
use compiler::backend::reg_alloc::graph_coloring::petgraph;
use compiler::backend::reg_alloc::graph_coloring::petgraph::Graph;
use compiler::backend::reg_alloc::graph_coloring::petgraph::graph::NodeIndex;
/// GraphNode represents a node in the interference graph. /// GraphNode represents a node in the interference graph.
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
pub struct GraphNode { pub struct Node {
/// temp ID (could be register) /// temp ID (could be register)
temp: MuID, temp: MuID,
/// assigned color /// assigned color
...@@ -41,8 +37,29 @@ pub struct GraphNode { ...@@ -41,8 +37,29 @@ pub struct GraphNode {
/// We need to know the moves so that we can coalesce. /// We need to know the moves so that we can coalesce.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct Move { pub struct Move {
pub from: NodeIndex, pub from: MuID,
pub to: NodeIndex pub to: MuID
}
#[inline(always)]
fn is_precolored(reg: MuID) -> bool {
if reg < MACHINE_ID_END {
true
} else {
false
}
}
#[inline(always)]
fn is_usable(reg: MuID) -> bool {
if backend::all_usable_regs()
.iter()
.any(|x| x.id() == backend::get_color_for_precolored(reg))
{
true
} else {
false
}
} }
/// InterferenceGraph represents the interference graph, including /// InterferenceGraph represents the interference graph, including
...@@ -50,12 +67,11 @@ pub struct Move { ...@@ -50,12 +67,11 @@ pub struct Move {
/// * all the nodes and its NodeIndex (a node is referred to by NodeIndex) /// * all the nodes and its NodeIndex (a node is referred to by NodeIndex)
/// * all the moves /// * all the moves
pub struct InterferenceGraph { pub struct InterferenceGraph {
/// the internal graph nodes: LinkedHashMap<MuID, Node>,
graph: Graph<GraphNode, (), petgraph::Undirected>,
/// a map of all nodes (from temp ID to node index) adj_set: LinkedHashSet<(MuID, MuID)>,
/// node index is how nodes are referred to with pet_graph adj_list: LinkedHashMap<MuID, LinkedHashSet<MuID>>,
nodes: LinkedHashMap<MuID, NodeIndex>, degree: LinkedHashMap<MuID, usize>,
/// a set of all moves
moves: LinkedHashSet<Move> moves: LinkedHashSet<Move>
} }
...@@ -63,7 +79,9 @@ impl InterferenceGraph { ...@@ -63,7 +79,9 @@ impl InterferenceGraph {
/// creates a new graph /// creates a new graph
fn new() -> InterferenceGraph { fn new() -> InterferenceGraph {
InterferenceGraph { InterferenceGraph {
graph: Graph::new_undirected(), adj_set: LinkedHashSet::new(),
adj_list: LinkedHashMap::new(),
degree: LinkedHashMap::new(),
nodes: LinkedHashMap::new(), nodes: LinkedHashMap::new(),
moves: LinkedHashSet::new() moves: LinkedHashSet::new()
} }
...@@ -71,49 +89,34 @@ impl InterferenceGraph { ...@@ -71,49 +89,34 @@ impl InterferenceGraph {
/// creates a new node for a temp (if we already created a temp for the temp, returns the node) /// creates a new node for a temp (if we already created a temp for the temp, returns the node)
/// This function will increase spill cost for the node by 1 each tiem it is called for the temp /// This function will increase spill cost for the node by 1 each tiem it is called for the temp
fn new_node(&mut self, reg_id: MuID, context: &FunctionContext) -> NodeIndex { fn new_node(&mut self, reg_id: MuID, context: &FunctionContext) -> MuID {
let entry = context.get_value(reg_id).unwrap(); let entry = context.get_value(reg_id).unwrap();
// if it is the first time, create the node // if it is the first time, create the node
if !self.nodes.contains_key(&reg_id) { if !self.nodes.contains_key(&reg_id) {
let node = GraphNode { let node = Node {
temp: reg_id, temp: reg_id,
color: None, color: None,
group: backend::RegGroup::get_from_ty(entry.ty()), group: backend::RegGroup::get_from_ty(entry.ty()),
spill_cost: 0.0f32 spill_cost: 0.0f32
}; };
// add to the graph self.nodes.insert(reg_id, node);
let index = self.graph.add_node(node); self.adj_list.insert(reg_id, LinkedHashSet::new());
// save index self.degree.insert(reg_id, 0);
self.nodes.insert(reg_id, index);
} }
// get the node index
let node_index = *self.nodes.get(&reg_id).unwrap();
// get node // get node
let node_mut = self.graph.node_weight_mut(node_index).unwrap(); let node_mut = self.nodes.get_mut(&reg_id).unwrap();
// increase node spill cost // increase node spill cost
node_mut.spill_cost += 1.0f32; node_mut.spill_cost += 1.0f32;
node_index reg_id
}
/// returns the node index for a temp
pub fn get_node(&self, reg: MuID) -> NodeIndex {
match self.nodes.get(&reg) {
Some(index) => *index,
None => panic!("do not have a node for {}", reg)
}
} }
/// returns all the nodes in the graph /// returns all the nodes in the graph
pub fn nodes(&self) -> Vec<NodeIndex> { pub fn nodes(&self) -> Vec<MuID> {
let mut ret = vec![]; self.nodes.keys().map(|x| *x).collect()
for index in self.nodes.values() {
ret.push(*index);
}
ret
} }
/// returns all the moves in the graph /// returns all the moves in the graph
...@@ -122,23 +125,19 @@ impl InterferenceGraph { ...@@ -122,23 +125,19 @@ impl InterferenceGraph {
} }
/// adds a move edge between two nodes /// adds a move edge between two nodes
fn add_move(&mut self, src: NodeIndex, dst: NodeIndex) { fn add_move(&mut self, src: MuID, dst: MuID) {
let src = { let src = {
let temp_src = self.get_temp_of(src); if is_precolored(src) {
if temp_src < MACHINE_ID_END {
// get the color for the machine register, e.g. rax for eax/ax/al/ah