WARNING! Access to this system is limited to authorised users only.
Unauthorised users may be subject to prosecution.
Unauthorised access to this system is a criminal offence under Australian law (Federal Crimes Act 1914 Part VIA)
It is a criminal offence to:
(1) Obtain access to data without authority. -Penalty 2 years imprisonment.
(2) Damage, delete, alter or insert data without authority. -Penalty 10 years imprisonment.
User activity is monitored and recorded. Anyone using this system expressly consents to such monitoring and recording.

Commit 7c9c215b authored by Yi Lin's avatar Yi Lin
Browse files

Merge branch 'aarch64-fixes' into 'develop'

Aarch64 fixes

See merge request !41
parents de9d3125 6456325b
......@@ -21,7 +21,7 @@ export MU_LOG_LEVEL=none
export RUST_TEST_THREADS=1
export RUST_BACKTRACE=0
export PYTHONPATH="$MU_ZEBU/tests/test_jit/mu-client-pypy/:$MU_ZEBU/tests/test_jit/RPySOM/src"
export LD_LIBRARY_PATH="$MU_ZEBU/tests/test_jit/:$MU_ZEBU/tests/test_jit"
export LD_LIBRARY_PATH="$MU_ZEBU/tests/test_jit/:$MU_ZEBU/tests/test_jit:$LD_LIBRARY_PATH"
export ZEBU_BUILD=release
rm -rf $MU_ZEBU/emit
......@@ -29,7 +29,7 @@ rm -rf $MU_ZEBU/tests/test_jit/emit
cargo update
#cargo clean
cargo-fmt -- --write-mode=diff --verbose -- src/ast/src/lib.rs src/gc/src/lib.rs src/utils/src/lib.rs | tee cargo_fmt_out.txt
cargo-fmt -- --write-mode=diff --verbose -- src/lib.rs src/ast/src/lib.rs src/gc/src/lib.rs src/utils/src/lib.rs | tee cargo_fmt_out.txt
cargo test --release --no-run --color=always 2>&1 | tee build_out.txt
$(exit ${PIPESTATUS[0]}) # this command will exit the shell but only if the above cargo test failed
......
......@@ -1050,6 +1050,13 @@ impl Value {
}
pub fn is_func_const(&self) -> bool {
match self.v {
Value_::Constant(Constant::FuncRef(_)) => true,
_ => false
}
}
pub fn is_int_const(&self) -> bool {
match self.v {
Value_::Constant(Constant::Int(_)) => true,
......
......@@ -50,7 +50,8 @@ struct ASMCode {
entry: MuName,
blocks: LinkedHashMap<MuName, ASMBlock>,
frame_size_patchpoints: Vec<ASMLocation>
frame_size_lower_patchpoints: Vec<ASMLocation>,
frame_size_upper_patchpoints: Vec<ASMLocation>
}
unsafe impl Send for ASMCode {}
......@@ -126,7 +127,8 @@ impl ASMCode {
entry: self.entry.clone(),
code: vec![],
blocks: linked_hashmap!{},
frame_size_patchpoints: vec![]
frame_size_lower_patchpoints: vec![],
frame_size_upper_patchpoints: vec![]
};
// iterate through old machine code
......@@ -217,7 +219,7 @@ impl ASMCode {
}
// fix patchpoint
for patchpoint in self.frame_size_patchpoints.iter() {
for patchpoint in self.frame_size_lower_patchpoints.iter() {
let new_patchpoint = ASMLocation {
line: *location_map.get(&patchpoint.line).unwrap(),
index: patchpoint.index,
......@@ -225,9 +227,20 @@ impl ASMCode {
oplen: patchpoint.oplen
};
ret.frame_size_patchpoints.push(new_patchpoint);
ret.frame_size_lower_patchpoints.push(new_patchpoint);
}
// fix patchpoint
for patchpoint in self.frame_size_upper_patchpoints.iter() {
let new_patchpoint = ASMLocation {
line: *location_map.get(&patchpoint.line).unwrap(),
index: patchpoint.index,
len: patchpoint.len,
oplen: patchpoint.oplen
};
ret.frame_size_upper_patchpoints.push(new_patchpoint);
}
ret.control_flow_analysis();
Box::new(ret)
......@@ -511,8 +524,11 @@ impl ASMCode {
}
}
fn add_frame_size_patchpoint(&mut self, patchpoint: ASMLocation) {
self.frame_size_patchpoints.push(patchpoint);
fn add_frame_size_lower_patchpoint(&mut self, patchpoint: ASMLocation) {
self.frame_size_lower_patchpoints.push(patchpoint);
}
fn add_frame_size_upper_patchpoint(&mut self, patchpoint: ASMLocation) {
self.frame_size_upper_patchpoints.push(patchpoint);
}
}
......@@ -715,9 +731,10 @@ impl MachineCode for ASMCode {
}
fn set_inst_nop(&mut self, index: usize) {
self.code[index].code.clear();
// self.code.remove(index);
// self.code.insert(index, ASMInst::nop());
let ref mut inst = self.code[index];
inst.code.clear();
inst.defines.clear();
inst.uses.clear();
}
fn remove_unnecessary_callee_saved(&mut self, used_callee_saved: Vec<MuID>) -> HashSet<MuID> {
......@@ -766,15 +783,31 @@ impl MachineCode for ASMCode {
fn patch_frame_size(&mut self, size: usize) {
debug_assert!(size % 16 == 0);
let size = size as u64;
let lower_size = (size & bits_ones(12)).to_string();
let upper_size = (size >> 12).to_string();
assert!(size <= bits_ones(24)); // Maximum frame size (16 MiB - 1B)
let size = size.to_string();
debug_assert!(size.len() <= FRAME_SIZE_PLACEHOLDER_LEN);
for loc in self.frame_size_patchpoints.iter() {
for loc in self.frame_size_lower_patchpoints.iter() {
let ref mut inst = self.code[loc.line];
string_utils::replace(&mut inst.code, loc.index, &size, size.len());
if size & bits_ones(12) == 0 {
//self.set_inst_nop(loc.line);
inst.code.clear();
inst.defines.clear();
inst.uses.clear();
} else {
string_utils::replace(&mut inst.code, loc.index, &lower_size, lower_size.len());
}
}
for loc in self.frame_size_upper_patchpoints.iter() {
let ref mut inst = self.code[loc.line];
if size >> 12 == 0 {
inst.code.clear();
inst.defines.clear();
inst.uses.clear();
} else {
string_utils::replace(&mut inst.code, loc.index, &upper_size, upper_size.len());
}
}
}
......@@ -1015,13 +1048,9 @@ 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())
};
}
// Maximum frame size is: '4095; SUB SP, SP,
const FRAME_SIZE_PART_PLACEHOLDER_LEN: usize = 4; // maximum 'frame' size part is 4095
const FRAME_SIZE_PART_PLACEHOLDER: &str = " "; // maximum 'frame' size part is 4095
impl ASMCodeGen {
pub fn new() -> ASMCodeGen {
......@@ -1193,7 +1222,8 @@ impl ASMCodeGen {
let n = offset.ty.get_int_length().unwrap();
let shift_type = if n == 64 {
if signed {
"SXTX"
"LSL"
//"SXTX"
} else {
"LSL"
}
......@@ -1523,10 +1553,10 @@ impl ASMCodeGen {
)
}
// dest <= inst(src1, src2)
fn internal_binop(&mut self, inst: &str, dest: &P<Value>, src1: &P<Value>, src2: &P<Value>) {
let inst = inst.to_string();
trace_emit!("\t{} {}, {} -> {}", inst, src1, src2, dest);
let (reg1, id1, loc1) = self.prepare_reg(dest, inst.len() + 1);
......@@ -2191,7 +2221,8 @@ impl CodeGenerator for ASMCodeGen {
entry: entry,
code: vec![],
blocks: linked_hashmap!{},
frame_size_patchpoints: vec![]
frame_size_lower_patchpoints: vec![],
frame_size_upper_patchpoints: vec![]
}));
// to link with C sources via gcc
......@@ -2241,7 +2272,8 @@ impl CodeGenerator for ASMCodeGen {
entry: Arc::new("none".to_string()),
code: vec![],
blocks: linked_hashmap!{},
frame_size_patchpoints: vec![]
frame_size_lower_patchpoints: vec![],
frame_size_upper_patchpoints: vec![]
}));
}
......@@ -2338,19 +2370,40 @@ impl CodeGenerator for ASMCodeGen {
}
fn emit_frame_grow(&mut self) {
trace_emit!("\tSUB SP, SP, #FRAME_SIZE_PLACEHOLDER");
let asm = format!("SUB SP,SP,#{}", FRAME_SIZE_PLACEHOLDER.clone());
trace_emit!("\tFRAME GROW");
let asm = format!("SUB SP,SP,#{}", FRAME_SIZE_PART_PLACEHOLDER.clone());
let line = self.line();
self.cur_mut()
.add_frame_size_lower_patchpoint(ASMLocation::new(
line,
11,
FRAME_SIZE_PART_PLACEHOLDER_LEN,
0
));
self.add_asm_inst(
asm,
linked_hashmap!{}, // let reg alloc ignore this instruction
linked_hashmap!{},
false
);
let asm = format!("SUB SP,SP,#{},LSL #12", FRAME_SIZE_PART_PLACEHOLDER.clone());
let line = self.line();
self.cur_mut()
.add_frame_size_patchpoint(ASMLocation::new(line, 11, FRAME_SIZE_PLACEHOLDER_LEN, 0));
.add_frame_size_upper_patchpoint(ASMLocation::new(
line,
11,
FRAME_SIZE_PART_PLACEHOLDER_LEN,
0
));
self.add_asm_inst(
asm,
linked_hashmap!{}, // let reg alloc ignore this instruction
linked_hashmap!{},
false
)
);
}
// Pushes a pair of registers on the givne stack (uses the STP instruction)
......
......@@ -4833,6 +4833,14 @@ impl<'a> InstructionSelection {
self.record_callsite(resumption, callsite, stack_arg_size);
if resumption.is_some() {
self.finish_block();
// This is needed as the above call instruction may 'branch' to the exceptional
// destination, branches are only supposed to occur at the end of assembly blocks
let block_name = make_block_name(&cur_node.name(), "normal_cont_for_call");
self.start_block(block_name);
}
// deal with ret vals
self.emit_postcall_convention(
&func_sig.ret_tys,
......@@ -4843,6 +4851,12 @@ impl<'a> InstructionSelection {
f_context,
vm
);
if resumption.is_some() {
let ref normal_dest = resumption.as_ref().unwrap().normal_dest;
let normal_target_name = f_content.get_block(normal_dest.target).name();
self.backend.emit_b(normal_target_name);
}
}
}
......
......@@ -1060,11 +1060,11 @@ pub fn is_valid_arithmetic_imm(val: u64) -> bool {
// (the resulting value will be valid iff 'val' is valid, and the lower 'n' bits will equal val)
pub fn replicate_logical_imm(val: u64, n: usize) -> u64 {
let op_size = if n <= 32 { 32 } else { 64 };
let mut val = val;
let mut new_val = val;
for i in 1..op_size / n {
val |= val << i * n;
new_val |= val << i * n;
}
val
new_val
}
......@@ -1389,6 +1389,15 @@ pub fn match_value_int_imm(op: &P<Value>) -> bool {
_ => false
}
}
pub fn match_value_zero(op: &P<Value>) -> bool {
match op.v {
Value_::Constant(Constant::Int(x)) => x == 0,
Value_::Constant(Constant::NullRef) => true,
_ => false
}
}
pub fn match_value_ref_imm(op: &P<Value>) -> bool {
match op.v {
Value_::Constant(Constant::NullRef) => true,
......@@ -1416,6 +1425,13 @@ pub fn match_node_int_imm(op: &TreeNode) -> bool {
}
}
pub fn match_node_zero(op: &TreeNode) -> bool {
match op.v {
TreeNode_::Value(ref pv) => match_value_zero(pv),
_ => false
}
}
// The only valid ref immediate is a null ref
pub fn match_node_ref_imm(op: &TreeNode) -> bool {
match op.v {
......@@ -1576,6 +1592,21 @@ and arg is the name of the argument that can't be the zero register (do so for e
let arg = replace_zero_register(backend, &arg, f_context, vm);
*/
pub fn replace_unexpected_zero_register(
backend: &mut CodeGenerator,
val: &P<Value>,
f_context: &mut FunctionContext,
vm: &VM
) -> P<Value> {
if is_zero_register(&val) {
let temp = make_temporary(f_context, val.ty.clone(), vm);
backend.emit_mov_imm(&temp, 0);
temp
} else {
val.clone()
}
}
pub fn replace_zero_register(
backend: &mut CodeGenerator,
val: &P<Value>,
......@@ -1896,6 +1927,7 @@ fn emit_cmp_u64(
} else if is_valid_arithmetic_imm(val) {
let imm_shift = val > 4096;
let imm_val = if imm_shift { val >> 12 } else { val };
let src1 = replace_unexpected_zero_register(backend, src1, f_context, vm);
backend.emit_cmp_imm(&src1, imm_val as u16, imm_shift);
} else {
let tmp = make_temporary(f_context, UINT64_TYPE.clone(), vm);
......@@ -1919,6 +1951,7 @@ fn emit_cmn_u64(
} else if is_valid_arithmetic_imm(val) {
let imm_shift = val > 4096;
let imm_val = if imm_shift { val >> 12 } else { val };
let src1 = replace_unexpected_zero_register(backend, src1, f_context, vm);
backend.emit_cmn_imm(&src1, imm_val as u16, imm_shift);
} else {
let tmp = make_temporary(f_context, UINT64_TYPE.clone(), vm);
......@@ -1986,61 +2019,12 @@ fn emit_reg_value(
f_context: &mut FunctionContext,
vm: &VM
) -> P<Value> {
match pv.v {
Value_::SSAVar(_) => pv.clone(),
Value_::Constant(ref c) => {
match c {
&Constant::Int(val) => {
/*if val == 0 {
// TODO emit the zero register (NOTE: it can't be used by all instructions)
// Use the zero register (saves having to use a temporary)
get_alias_for_length(XZR.id(), get_bit_size(&pv.ty, vm))
} else {*/
let tmp = make_temporary(f_context, pv.ty.clone(), vm);
debug!("tmp's ty: {}", tmp.ty);
emit_mov_u64(backend, &tmp, val);
tmp
//}
}
&Constant::IntEx(ref val) => {
assert!(val.len() == 2);
let tmp = make_temporary(f_context, pv.ty.clone(), vm);
let (tmp_l, tmp_h) = split_int128(&tmp, f_context, vm);
emit_mov_u64(backend, &tmp_l, val[0]);
emit_mov_u64(backend, &tmp_h, val[1]);
tmp
}
&Constant::FuncRef(func_id) => {
let tmp = make_temporary(f_context, pv.ty.clone(), vm);
let mem =
make_value_symbolic(vm.get_name_for_func(func_id), true, &ADDRESS_TYPE, vm);
emit_calculate_address(backend, &tmp, &mem, vm);
tmp
}
&Constant::NullRef => {
let tmp = make_temporary(f_context, pv.ty.clone(), vm);
backend.emit_mov_imm(&tmp, 0);
tmp
//get_alias_for_length(XZR.id(), get_bit_size(&pv.ty, vm))
}
&Constant::Double(val) => {
let tmp = make_temporary(f_context, pv.ty.clone(), vm);
emit_mov_f64(backend, &tmp, f_context, vm, val);
tmp
}
&Constant::Float(val) => {
let tmp = make_temporary(f_context, pv.ty.clone(), vm);
emit_mov_f32(backend, &tmp, f_context, vm, val);
tmp
}
_ => panic!("expected fpreg or ireg")
}
}
_ => panic!("expected fpreg or ireg")
if is_int_reg(&pv) || is_int_ex_reg(&pv) {
emit_ireg_value(backend, pv, f_context, vm)
} else if is_fp_reg(&pv) {
emit_fpreg_value(backend, pv, f_context, vm)
} else {
unreachable!();
}
}
......@@ -2056,10 +2040,8 @@ pub fn emit_ireg_value(
Value_::Constant(ref c) => {
match c {
&Constant::Int(val) => {
// TODO Deal with zero case
// TODO: Deal with zero case
/*if val == 0 {
// TODO: Are there any (integer) instructions that can't use the Zero reg?
// Use the zero register (saves having to use a temporary)
get_alias_for_length(XZR.id(), get_bit_size(&pv.ty, vm))
} else {*/
let tmp = make_temporary(f_context, pv.ty.clone(), vm);
......@@ -2762,6 +2744,14 @@ fn emit_move_value_to_value(
if src.is_int_const() {
let imm = value_imm_to_u64(src);
emit_mov_u64(backend, dest, imm);
} else if src.is_func_const() {
let func_id = match src.v {
Value_::Constant(Constant::FuncRef(id)) => id,
_ => unreachable!()
};
let mem =
make_value_symbolic(vm.get_name_for_func(func_id), true, &ADDRESS_TYPE, vm);
emit_calculate_address(backend, &dest, &mem, vm);
} else if is_int_reg(&src) {
backend.emit_mov(dest, src);
} else if src.is_mem() {
......
......@@ -1283,6 +1283,7 @@ struct BundleLoader<'lb, 'lvm> {
built_ref_of: IdPMap<MuType>,
built_iref_of: IdPMap<MuType>,
built_uptr_of: IdPMap<MuType>,
built_strong_variant: IdPMap<MuType>,
built_constint_of: HashMap<u64, P<Value>>,
current_sig: Option<P<MuFuncSig>>,
......@@ -1324,6 +1325,7 @@ fn load_bundle(b: &mut MuIRBuilder) {
built_ref_of: Default::default(),
built_iref_of: Default::default(),
built_uptr_of: Default::default(),
built_strong_variant: Default::default(),
built_constint_of: Default::default(),
current_sig: Default::default(),
current_entry: Default::default()
......@@ -1389,6 +1391,27 @@ impl<'lb, 'lvm> BundleLoader<'lb, 'lvm> {
impl_refvoid
}
fn ensure_strong_variant(&mut self, ty: &P<MuType>) -> P<MuType> {
if let Some(ref sty) = self.built_strong_variant.get(&ty.id()) {
return (*sty).clone();
}
let sty = match &ty.v {
&MuType_::WeakRef(ref t) => {
let id = self.vm.next_id();
let sty = P(MuType::new(id, MuType_::muref(t.clone())));
self.built_types.insert(id, sty.clone());
sty
}
_ => ty.clone()
};
trace!("Ensure strong variant is defined: {} {:?}", sty.id(), sty);
self.built_strong_variant.insert(ty.id(), sty.clone());
sty
}
fn ensure_refi64(&mut self) -> P<MuType> {
if let Some(ref refi64) = self.built_refi64 {
return refi64.clone();
......@@ -3296,8 +3319,8 @@ impl<'lb, 'lvm> BundleLoader<'lb, 'lvm> {
let impl_ord = self.build_mem_ord(ord);
let impl_loc = self.get_treenode(fcb, loc);
let impl_rvtype = self.get_built_type(refty);
let impl_rv = self.new_ssa(fcb, result_id, self.vm.make_strong_type(impl_rvtype))
.clone_value();
let impl_actual_rvtype = self.ensure_strong_variant(&impl_rvtype);
let impl_rv = self.new_ssa(fcb, result_id, impl_actual_rvtype).clone_value();
let impl_refty = self.get_built_type(refty);
assert_ir!(impl_ord != MemoryOrder::Release && impl_ord != MemoryOrder::AcqRel);
......
......@@ -54,4 +54,4 @@ mod deps {
extern crate mu_ast as ast;
}
pub use self::api_impl::VALIDATE_IR;
\ No newline at end of file
pub use self::api_impl::VALIDATE_IR;
......@@ -77,8 +77,6 @@ pub struct VM {
name_id_map: RwLock<HashMap<MuName, MuID>>, // +64
/// types declared to the VM
types: RwLock<HashMap<MuID, P<MuType>>>, // +120
/// Ref types declared by 'make_strong_type', the key is the ID of the Referant
ref_types: RwLock<HashMap<MuID, P<MuType>>>,
/// types that are resolved as BackendType
backend_type_info: RwLock<HashMap<MuID, Box<BackendType>>>,
/// constants declared to the VM
......@@ -137,7 +135,6 @@ unsafe impl rodal::Dump for VM {
dumper.dump_object(&self.id_name_map);
dumper.dump_object(&self.name_id_map);
dumper.dump_object(&self.types);
dumper.dump_object(&self.ref_types);
dumper.dump_object(&self.backend_type_info);
dumper.dump_object(&self.constants);
dumper.dump_object(&self.globals);
......@@ -232,7 +229,6 @@ impl<'a> VM {
name_id_map: RwLock::new(HashMap::new()),
constants: RwLock::new(HashMap::new()),
types: RwLock::new(HashMap::new()),
ref_types: RwLock::new(HashMap::new()),
backend_type_info: RwLock::new(HashMap::new()),
globals: RwLock::new(HashMap::new()),
global_locations: RwLock::new(hashmap!{}),
......@@ -815,27 +811,6 @@ impl<'a> VM {
}
}
pub fn make_strong_type(&self, ty: P<MuType>) -> P<MuType> {
match &ty.v {
&MuType_::WeakRef(ref t) => {
let res = self.ref_types
.read()
.unwrap()
.get(&t.id())
.map(|x| x.clone());
match res {
Some(ty) => ty,
None => {
let ty = P(MuType::new(self.next_id(), MuType_::muref(t.clone())));
self.ref_types.write().unwrap().insert(t.id(), ty.clone());
ty
}
}
}
_ => ty.clone()
}
}
/// adds a new bundle into VM.
/// This function will drain the contents of all arguments. Ideally, this function should
/// happen atomically. e.g. The client should not see a new type added without also seeing
......
......@@ -235,7 +235,7 @@ def build_quicksort_bundle(bldr, rmu):
j = bldr.gen_sym("@partition.v1.blk3.j")
i = bldr.gen_sym("@partition.v1.blk3.i")
parr = bldr.gen_sym("@partition.v1.blk3.parr")
pelm = bldr.gen_sym("@partition.v1.blk2.pelm")
pelm = bldr.gen_sym("@partition.v1.blk3.pelm")
pelm_i = bldr.gen_sym("@partition.v1.blk3.pelm_i")
t = bldr.gen_sym("@partition.v1.blk3.t")
pelm_j = bldr.gen_sym("@partition.v1.blk3.pelm_j")
......@@ -258,7 +258,7 @@ def build_quicksort_bundle(bldr, rmu):
i = bldr.gen_sym("@partition.v1.blk4.i")
parr = bldr.gen_sym("@partition.v1.blk4.parr")
idx_high = bldr.gen_sym("@partition.v1.blk4.idx_high")
pelm = bldr.gen_sym("@partition.v1.blk2.pelm")
pelm = bldr.gen_sym("@partition.v1.blk4.pelm")
pelm_i = bldr.gen_sym("@partition.v1.blk4.pelm_i")
t = bldr.gen_sym("@partition.v1.blk4.t")
pelm_idx_high = bldr.gen_sym("@partition.v1.blk4.pelm_idx_high")
......
......@@ -178,3 +178,66 @@ def test_double_inline():
RET (a b)
}
""", "test_double_inline");
def test_funcref():
lib = load_bundle(
"""
.funcdef triv<()->()>
{
entry():
RET
}
.funcdef ret_funcref <()->(funcref<()->()>)>
{
entry():