Commit d404c0e3 authored by Zixian Cai's avatar Zixian Cai

Merge branch 'develop' into new_ci

parents fd0a18c2 844ff350
......@@ -519,6 +519,14 @@ impl ASMCode {
use std::any::Any;
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 {
self
}
......
......@@ -851,6 +851,16 @@ impl MachineCode for ASMCode {
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
/// returns what registers push/pop have been deleted, and the number of callee saved registers
/// that weren't deleted
......
......@@ -1643,6 +1643,7 @@ impl<'a> InstructionSelection {
// FIXME: the semantic of Pin/Unpin is different from spec
// See Issue #33
Instruction_::CommonInst_Pin(op) => {
use runtime::mm::GC_MOVES_OBJECT;
trace!("instsel on PIN");
// call pin() in GC
......@@ -1653,17 +1654,23 @@ impl<'a> InstructionSelection {
let tmp_op = self.emit_ireg(op, f_content, f_context, vm);
let tmp_res = self.get_result_value(node);
self.emit_runtime_entry(
&entrypoints::PIN_OBJECT,
vec![tmp_op.clone()],
Some(vec![tmp_res]),
Some(node),
f_content,
f_context,
vm
);
if GC_MOVES_OBJECT {
self.emit_runtime_entry(
&entrypoints::PIN_OBJECT,
vec![tmp_op.clone()],
Some(vec![tmp_res]),
Some(node),
f_content,
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) => {
use runtime::mm::GC_MOVES_OBJECT;
trace!("instsel on UNPIN");
// call unpin() in GC
......@@ -1673,15 +1680,17 @@ impl<'a> InstructionSelection {
assert!(self.match_ireg(op));
let tmp_op = self.emit_ireg(op, f_content, f_context, vm);
self.emit_runtime_entry(
&entrypoints::UNPIN_OBJECT,
vec![tmp_op.clone()],
None,
Some(node),
f_content,
f_context,
vm
);
if GC_MOVES_OBJECT {
self.emit_runtime_entry(
&entrypoints::UNPIN_OBJECT,
vec![tmp_op.clone()],
None,
Some(node),
f_content,
f_context,
vm
);
}
}
Instruction_::CommonInst_GetAddr(op) => {
trace!("instsel on GETADDR");
......@@ -2672,6 +2681,12 @@ impl<'a> InstructionSelection {
1 | 2 | 4 | 8 => {
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 {
8 => x86_64::RAX.clone(),
4 => x86_64::EAX.clone(),
......@@ -2679,38 +2694,10 @@ impl<'a> InstructionSelection {
1 => x86_64::AL.clone(),
_ => unimplemented!()
};
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)
}
self.backend.emit_mov_r_r(&mreg_op1, &tmp_op1);
// mul op2
if self.match_iimm(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)
}
self.backend.emit_mul_r(&tmp_op2);
// mov rax -> result
let res_size = vm.get_backend_type_size(res_tmp.ty.id());
......@@ -3437,69 +3424,22 @@ impl<'a> InstructionSelection {
)
}
} else {
// size is unknown at compile time
// we need to emit both alloc small and alloc large,
// 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);
// directly call 'alloc'
let tmp_res = self.get_result_value(node);
// alloc small here
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();
let const_align = self.make_int_const(align as u64, vm);
// alloc_large:
self.start_block(blk_alloc_large.clone());
self.emit_alloc_sequence_large(
tmp_allocator.clone(),
size,
align,
node,
self.emit_runtime_entry(
&entrypoints::ALLOC_ANY,
vec![tmp_allocator.clone(), size.clone(), const_align],
Some(vec![tmp_res.clone()]),
Some(node),
f_content,
f_context,
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 {
let compiled_funcs = vm.compiled_funcs().read().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() {
cf.mc().trace_inst(i);
// if two sides of a move instruction are the same,
// it is redundant, and can be eliminated
trace!("trying to remove redundant move");
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
// ..
......@@ -53,9 +62,11 @@ impl CompilerPass for PeepholeOptimization {
// the order matters: we need to run this first, then remove_unnecessary_jump()
// 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);
// 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);
}
......@@ -74,8 +85,6 @@ impl PeepholeOptimization {
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 cf.mc().is_move(inst) && !cf.mc().is_using_mem_op(inst) {
cf.mc().trace_inst(inst);
// get source reg/temp ID
let src: MuID = {
let uses = cf.mc().get_inst_reg_uses(inst);
......@@ -166,34 +175,48 @@ impl PeepholeOptimization {
let opt_dest = mc.is_jmp(cur_inst);
match opt_dest {
Some(ref dest) => {
trace!("current instruction {} jumps to {}", cur_inst, dest);
// if we have already visited this instruction
// this means we met an infinite loop, we need to break
if visited_labels.contains(dest) {
warn!("met an infinite loop in removing jump-to-jump");
warn!("we are not optimizing this case");
return;
} else {
visited_labels.insert(dest.clone());
debug!("visited {}", dest);
}
// get the block for destination
let first_inst = mc.get_block_range(dest).unwrap().start;
debug_assert!(
mc.is_label(first_inst).is_none(),
"expect start inst {} of \
block {} is a inst instead of label",
let first_inst = {
let start = mc.get_block_range(dest).unwrap().start;
let last = mc.number_of_insts();
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,
dest
);
trace!("examining first inst {} of block {}", first_inst, dest);
// if first instruction is jump
match mc.is_jmp(first_inst) {
Some(ref dest2) => {
// its a jump-to-jump case
cur_inst = first_inst;
last_dest = Some(dest2.clone());
visited_labels.insert(dest2.clone());
debug!("visited {}", dest2);
}
None => break
}
......
......@@ -173,6 +173,8 @@ pub trait MachineCode {
fn is_move(&self, index: usize) -> bool;
/// is the specified index using memory operands?
fn is_using_mem_op(&self, index: usize) -> bool;
/// is the specified index is a nop?
fn is_nop(&self, index: usize) -> bool;
/// is the specified index a jump instruction? (unconditional jump)
/// returns an Option for target block
fn is_jmp(&self, index: usize) -> Option<MuName>;
......
......@@ -309,10 +309,11 @@ fn branch_adjustment(func: &mut MuFunctionVersion, vm: &VM) {
let next_block_in_trace: Option<usize> = {
if let Some(index) = trace.iter().position(|x| x == blk_id) {
if index == trace.len() {
if index >= trace.len() - 1 {
// we do not have next block in the trace
None
} else {
Some(index + 1)
Some(trace[index + 1])
}
} else {
warn!("find an unreachable block (a block exists in IR, but is not in trace");
......@@ -344,6 +345,14 @@ fn branch_adjustment(func: &mut MuFunctionVersion, vm: &VM) {
let true_label = true_dest.target;
let false_label = false_dest.target;
trace_if!(LOG_TRACE_SCHEDULE, "true_label = {}", true_label);
trace_if!(LOG_TRACE_SCHEDULE, "false_label = {}", false_label);
trace_if!(
LOG_TRACE_SCHEDULE,
"next_block_in_trace = {:?}",
next_block_in_trace
);
if next_block_in_trace.is_some() &&
next_block_in_trace.unwrap() == false_label
{
......
......@@ -267,7 +267,6 @@ pub fn alloc(mutator: *mut ImmixMutatorLocal, size: usize, align: usize) -> Obje
/// allocates an object in the immix space
// size doesn't include HEADER_SIZE
#[no_mangle]
#[inline(never)]
pub extern "C" fn muentry_alloc_fast(
mutator: *mut ImmixMutatorLocal,
size: usize,
......@@ -332,6 +331,21 @@ pub extern "C" fn muentry_alloc_large(
unsafe { ret.to_object_reference() }
}
#[no_mangle]
// size doesn't include HEADER_SIZE
pub extern "C" fn muentry_alloc_any(
mutator: *mut ImmixMutatorLocal,
size: usize,
align: usize
) -> ObjectReference {
let actual_size = size + OBJECT_HEADER_SIZE;
if actual_size <= LARGE_OBJECT_THRESHOLD {
muentry_alloc_fast(mutator, size, align)
} else {
muentry_alloc_large(mutator, size, align)
}
}
/// initializes a fix-sized object
#[no_mangle]
#[inline(never)]
......
......@@ -96,6 +96,10 @@ lazy_static! {
"muentry_alloc_large",
vec![ADDRESS_TYPE.clone(), UINT64_TYPE.clone(), UINT64_TYPE.clone()],
vec![ADDRESS_TYPE.clone()]);
pub static ref ALLOC_ANY : RuntimeEntrypoint = RuntimeEntrypoint::new(
"muentry_alloc_any",
vec![ADDRESS_TYPE.clone(), UINT64_TYPE.clone(), UINT64_TYPE.clone()],
vec![ADDRESS_TYPE.clone()]);
pub static ref INIT_OBJ : RuntimeEntrypoint = RuntimeEntrypoint::new(
"muentry_init_object",
vec![ADDRESS_TYPE.clone(), ADDRESS_TYPE.clone(), UINT64_TYPE.clone()],
......
......@@ -650,3 +650,125 @@ fn add_int64_nzc() -> VM {
vm
}
#[test]
fn test_nest_mul_simple() {
VM::start_logging_trace();
let lib = linkutils::aot::compile_fnc("nest_mul_simple", &nest_mul_simple);
unsafe {
let nest_mul_simple: libloading::Symbol<unsafe extern "C" fn(u64, u64, u64) -> u64> =
lib.get(b"nest_mul_simple").unwrap();
let res = nest_mul_simple(2, 3, 4);
println!("mul(2, 3, 4) = {}", res);
assert_eq!(res, 24);
}
}
fn nest_mul_simple() -> VM {
let vm = VM::new();
typedef! ((vm) int64 = mu_int(64));
funcsig! ((vm) sig = (int64, int64, int64) -> (int64));
funcdecl! ((vm) <sig> nest_mul_simple);
funcdef! ((vm) <sig> nest_mul_simple VERSION nest_mul_simple_v1);
// %entry(%x, %y, %z)
block! ((vm, nest_mul_simple_v1) blk_entry);
ssa! ((vm, nest_mul_simple_v1) <int64> x);
ssa! ((vm, nest_mul_simple_v1) <int64> y);
ssa! ((vm, nest_mul_simple_v1) <int64> z);
// %a = MUL %x %y
ssa! ((vm, nest_mul_simple_v1) <int64> a);
inst! ((vm, nest_mul_simple_v1) blk_entry_mul1:
a = BINOP (BinOp::Mul) x y
);
// %b = MUL %a %z
ssa! ((vm, nest_mul_simple_v1) <int64> b);
inst! ((vm, nest_mul_simple_v1) blk_entry_mul2:
b = BINOP (BinOp::Mul) a z
);
// RET b
inst! ((vm, nest_mul_simple_v1) blk_entry_ret:
RET (b)
);
define_block!((vm, nest_mul_simple_v1) blk_entry(x, y, z) {
blk_entry_mul1,
blk_entry_mul2,
blk_entry_ret
});
define_func_ver!((vm) nest_mul_simple_v1(entry: blk_entry) {
blk_entry
});
vm
}
#[test]
fn test_nest_mul_times_10() {
VM::start_logging_trace();
let lib = linkutils::aot::compile_fnc("nest_mul_times_10", &nest_mul_times_10);
unsafe {
let nest_mul: libloading::Symbol<unsafe extern "C" fn(u64, u64) -> u64> =
lib.get(b"nest_mul_times_10").unwrap();
let res = nest_mul(2, 3);
println!("mul(2, 3) x 10 = {}", res);
assert_eq!(res, 60);
}
}
fn nest_mul_times_10() -> VM {
let vm = VM::new();
typedef! ((vm) int64 = mu_int(64));
constdef! ((vm) <int64> int64_10 = Constant::Int(10));
funcsig! ((vm) sig = (int64, int64) -> (int64));
funcdecl! ((vm) <sig> nest_mul_times_10);
funcdef! ((vm) <sig> nest_mul_times_10 VERSION nest_mul_times_10_v1);
// %entry(%x, %y)
block! ((vm, nest_mul_times_10_v1) blk_entry);
ssa! ((vm, nest_mul_times_10_v1) <int64> x);
ssa! ((vm, nest_mul_times_10_v1) <int64> y);
consta! ((vm, nest_mul_times_10_v1) int64_10_local = int64_10);
// %a = MUL %x %y
ssa! ((vm, nest_mul_times_10_v1) <int64> a);
inst! ((vm, nest_mul_times_10_v1) blk_entry_mul1:
a = BINOP (BinOp::Mul) x y
);
// %b = MUL 10 %a
ssa! ((vm, nest_mul_times_10_v1) <int64> b);
inst! ((vm, nest_mul_times_10_v1) blk_entry_mul2:
b = BINOP (BinOp::Mul) int64_10_local a
);
// RET b
inst! ((vm, nest_mul_times_10_v1) blk_entry_ret:
RET (b)
);
define_block!((vm, nest_mul_times_10_v1) blk_entry(x, y) {
blk_entry_mul1,
blk_entry_mul2,
blk_entry_ret
});
define_func_ver!((vm) nest_mul_times_10_v1(entry: blk_entry) {
blk_entry
});
vm
}
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