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.

To protect your data, the CISO officer has suggested users to enable 2FA as soon as possible.
Currently 2.7% of users enabled 2FA.

Commit 841d47a1 authored by Yi Lin's avatar Yi Lin
Browse files

Merge branch 'aarch64' into 'master'

Merge aarch64 into master

See merge request !10
parents 99a65c69 f9dcc877
......@@ -21,6 +21,7 @@ ast = {path = "src/ast"}
utils = {path = "src/utils"}
gc = {path = "src/gc"}
field-offset = "0.1.1"
libloading = "0.3"
lazy_static = "0.1.15"
......@@ -34,4 +35,5 @@ time = "0.1.34"
maplit = "0.1.4"
docopt = "0.6"
petgraph = "0.4.1"
extprim = "*"
\ No newline at end of file
extprim = "*"
num-traits = "*"
......@@ -735,6 +735,14 @@ impl Value {
})
}
pub fn is_int_ex_const(&self) -> bool {
match self.v {
Value_::Constant(Constant::IntEx(_)) => true,
_ => false
}
}
pub fn is_int_const(&self) -> bool {
match self.v {
Value_::Constant(Constant::Int(_)) => true,
......@@ -742,7 +750,13 @@ impl Value {
_ => false
}
}
pub fn is_fp_const(&self) -> bool {
match self.v {
Value_::Constant(Constant::Float(_)) => true,
Value_::Constant(Constant::Double(_)) => true,
_ => false
}
}
pub fn extract_int_const(&self) -> u64 {
match self.v {
Value_::Constant(Constant::Int(val)) => val,
......@@ -751,6 +765,13 @@ impl Value {
}
}
pub fn extract_int_ex_const(&self) -> Vec<u64> {
match self.v {
Value_::Constant(Constant::IntEx(ref val)) => val.clone(),
_ => panic!("expect int ex const")
}
}
pub fn extract_ssa_id(&self) -> Option<MuID> {
match self.v {
Value_::SSAVar(id) => Some(id),
......
......@@ -210,7 +210,7 @@ impl CmpOp {
FUGT => FULT,
FULT => FUGT,
_ => self, // all other comparisons are reflexive
_ => self, // all other comparisons are symmetric
}
}
pub fn invert(self) -> CmpOp {
......@@ -256,6 +256,18 @@ impl CmpOp {
FTRUE => FFALSE,
}
}
// gets the unsigned version of the comparison
pub fn get_unsigned(self) -> CmpOp {
use op::CmpOp::*;
match self {
SGE => UGE,
SLT => ULT,
SGT => UGT,
SLE => ULE,
_ => self,
}
}
pub fn is_signed(self) -> bool {
use op::CmpOp::*;
match self {
......@@ -263,6 +275,14 @@ impl CmpOp {
_ => false
}
}
pub fn is_symmetric(self) -> bool {
use op::CmpOp::*;
match self {
EQ | NE | FORD| FUNO| FUNE | FUEQ | FONE | FOEQ => true,
_ => false
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, RustcEncodable, RustcDecodable)]
......
......@@ -16,11 +16,9 @@ use utils::LinkedHashMap;
use ast::ptr::P;
use ast::ir::*;
use ast::types::*;
use std::str;
use std::usize;
use std::slice::Iter;
use std::ops;
use std::collections::HashSet;
......@@ -96,16 +94,6 @@ impl ASMCode {
panic!("didnt find any block for inst {}", inst)
}
fn get_block_by_start_inst(&self, inst: usize) -> Option<&ASMBlock> {
for block in self.blocks.values() {
if block.start_inst == inst {
return Some(block);
}
}
None
}
fn rewrite_insert(
&self,
insert_before: LinkedHashMap<usize, Vec<Box<ASMCode>>>,
......@@ -269,20 +257,8 @@ impl ASMCode {
// control flow analysis
let n_insts = self.number_of_insts();
let ref blocks = self.blocks;
let ref mut asm = self.code;
let block_start = {
let mut ret = vec![];
for block in blocks.values() {
if TRACE_CFA {
trace!("Block starts at {}", block.start_inst);
}
ret.push(block.start_inst);
}
ret
};
for i in 0..n_insts {
if TRACE_CFA {
trace!("---inst {}---", i);
......@@ -896,21 +872,6 @@ impl ASMInst {
spill_info: spill_info
}
}
fn nop() -> ASMInst {
ASMInst {
code: "".to_string(),
defines: LinkedHashMap::new(),
uses: LinkedHashMap::new(),
is_symbol: false,
is_mem_op_used: false,
preds: vec![],
succs: vec![],
branch: ASMBranchTarget::None,
spill_info: None
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
......@@ -993,14 +954,7 @@ impl ASMCodeGen {
self.cur().code.len()
}
fn add_asm_label(&mut self, code: String) {
let l = self.line();
trace!("emit: {}", code);
self.cur_mut().code.push(ASMInst::symbolic(code));
}
fn add_asm_block_label(&mut self, code: String, block_name: MuName) {
let l = self.line();
trace!("emit: [{}]{}", block_name, code);
self.cur_mut().code.push(ASMInst::symbolic(code));
}
......@@ -1010,10 +964,6 @@ impl ASMCodeGen {
self.cur_mut().code.push(ASMInst::symbolic(code));
}
fn prepare_machine_regs(&self, regs: Iter<P<Value>>) -> Vec<MuID> {
regs.map(|x| self.prepare_machine_reg(x)).collect()
}
fn add_asm_call(&mut self, code: String, potentially_excepting: Option<MuName>, target: Option<(MuID, ASMLocation)>) {
// a call instruction will use all the argument registers
// do not need
......@@ -1057,22 +1007,6 @@ impl ASMCodeGen {
}, None)
}
fn add_asm_ret(&mut self, code: String) {
// return instruction does not use anything (not RETURN REGS)
// otherwise it will keep RETURN REGS alive
// and if there is no actual move into RETURN REGS, it will keep RETURN REGS for alive for very long
// and prevents anything using those regsiters
self.add_asm_inst_internal(code, linked_hashmap! {}, linked_hashmap! {}, false, ASMBranchTarget::Return, None);
}
fn add_asm_branch(&mut self, code: String, target: MuName) {
self.add_asm_inst_internal(code, linked_hashmap! {}, linked_hashmap! {}, false, ASMBranchTarget::Unconditional(target), None);
}
fn add_asm_branch2(&mut self, code: String, target: MuName) {
self.add_asm_inst_internal(code, linked_hashmap! {}, linked_hashmap! {}, false, ASMBranchTarget::Conditional(target), None);
}
fn add_asm_inst(
&mut self,
code: String,
......@@ -1113,7 +1047,6 @@ impl ASMCodeGen {
target: ASMBranchTarget,
spill_info: Option<SpillMemInfo>)
{
let line = self.line();
trace!("asm: {}", code);
trace!(" defines: {:?}", defines);
trace!(" uses: {:?}", uses);
......@@ -1136,30 +1069,6 @@ impl ASMCodeGen {
(str, op.extract_ssa_id().unwrap(), ASMLocation::new(self.line(), loc, len, check_op_len(&op.ty)))
}
fn prepare_fpreg(&self, op: &P<Value>, loc: usize) -> (String, MuID, ASMLocation) {
if cfg!(debug_assertions) {
match op.v {
Value_::SSAVar(_) => {},
_ => panic!("expecting register op")
}
}
let str = self.asm_reg_op(op);
let len = str.len();
(str, op.extract_ssa_id().unwrap(), ASMLocation::new(self.line(), loc, len, 64))
}
fn prepare_machine_reg(&self, op: &P<Value>) -> MuID {
if cfg!(debug_assertions) {
match op.v {
Value_::SSAVar(_) => {},
_ => panic!("expecting machine register op")
}
}
op.extract_ssa_id().unwrap()
}
fn prepare_mem(&self, op: &P<Value>, loc: usize) -> (String, LinkedHashMap<MuID, Vec<ASMLocation>>) {
if cfg!(debug_assertions) {
match op.v {
......@@ -1203,7 +1112,9 @@ impl ASMCodeGen {
result_str.push_str(",");
let n = offset.ty.get_int_length().unwrap();
let shift_type =
if n == 64 { if signed { "SXTX" } else { "LSL" } } else if n == 32 { if signed { "SXTW" } else { "UXTW" } } else { panic!("Unexpected size for offset register") };
if n == 64 { if signed { "SXTX" } else { "LSL" } }
else if n == 32 { if signed { "SXTW" } else { "UXTW" } }
else { panic!("Unexpected size for offset register") };
result_str.push_str(&shift_type);
result_str.push_str(" #");
......@@ -1426,6 +1337,23 @@ impl ASMCodeGen {
)
}
fn internal_binop_str(&mut self, inst: &str, dest: &P<Value>, src1: &P<Value>, src2: &str) {
let inst = inst.to_string();
trace!("emit: \t{} {}, {} -> {}", inst, src1, src2, dest);
let (reg1, id1, loc1) = self.prepare_reg(dest, inst.len() + 1);
let (reg2, id2, loc2) = self.prepare_reg(src1, inst.len() + 1 + reg1.len() + 1);
let asm = format!("{} {},{},#{}", inst, reg1, reg2, src2);
self.add_asm_inst(
asm,
ignore_zero_register(id1, vec![loc1]),
ignore_zero_register(id2, vec![loc2]),
false
)
}
// dest <= inst(src1, src2)
fn internal_unop_shift(&mut self, inst: &str, dest: &P<Value>, src: &P<Value>, shift: &str, amount: u8) {
let inst = inst.to_string();
......@@ -1781,7 +1709,6 @@ impl ASMCodeGen {
}
};
trace!("emit: \t{} {} -> {}", inst, src, dest);
let (reg, id, loc) = self.prepare_reg(dest, inst.len() + 1);
......@@ -1819,8 +1746,8 @@ impl ASMCodeGen {
let inst = inst.to_string();
trace!("emit: \t{} {} -> {},{}", inst, src, dest1, dest2);
let (reg1, id1, loc1) = self.prepare_reg(dest1, 3 + 1);
let (reg2, id2, loc2) = self.prepare_reg(dest2, 3 + 1 + reg1.len() + 1);
let (reg1, id1, loc1) = self.prepare_reg(dest1, inst.len() + 1);
let (reg2, id2, loc2) = self.prepare_reg(dest2, inst.len() + 1 + reg1.len() + 1);
let (mem, uses) = self.prepare_mem(src, inst.len() + 1 + reg1.len() + 1 + reg2.len() + 1);
let asm = format!("{} {},{},{}", inst, reg1, reg2, mem);
......@@ -1931,8 +1858,8 @@ impl ASMCodeGen {
let inst = inst.to_string();
trace!("emit: \t{} {},{} -> {}", inst, src1, src2, dest);
let (reg1, id1, loc1) = self.prepare_reg(src2, inst.len() + 1);
let (reg2, id2, loc2) = self.prepare_reg(src1, inst.len() + 1 + reg1.len() + 1);
let (reg1, id1, loc1) = self.prepare_reg(src1, inst.len() + 1);
let (reg2, id2, loc2) = self.prepare_reg(src2, inst.len() + 1 + reg1.len() + 1);
let (mem, mut uses) = self.prepare_mem(dest, inst.len() + 1 + reg1.len() + 1 + reg2.len() + 1);
if is_zero_register_id(id1) {
......@@ -1968,8 +1895,8 @@ impl ASMCodeGen {
trace!("emit: \t{} {},{} -> {},{}", inst, src1, src2, dest, status);
let (reg1, id1, loc1) = self.prepare_reg(status, inst.len() + 1);
let (reg2, id2, loc2) = self.prepare_reg(src2, inst.len() + 1 + reg1.len() + 1);
let (reg3, id3, loc3) = self.prepare_reg(src1, inst.len() + 1 + reg1.len() + 1 + reg2.len() + 1);
let (reg2, id2, loc2) = self.prepare_reg(src1, inst.len() + 1 + reg1.len() + 1);
let (reg3, id3, loc3) = self.prepare_reg(src2, inst.len() + 1 + reg1.len() + 1 + reg2.len() + 1);
let (mem, mut uses) = self.prepare_mem(dest, inst.len() + 1 + reg1.len() + 1 + reg2.len() + 1 + reg3.len() + 1);
if is_zero_register_id(id2) {
......@@ -2004,19 +1931,6 @@ impl ASMCodeGen {
fn emit_str_spill(&mut self, dest: Mem, src: Reg) { self.internal_store("STR", dest, src, true, false); }
}
// Only used for loads and stores
#[inline(always)]
fn op_postfix(op_len: usize) -> &'static str {
match op_len {
1 => "B",
8 => "B",
16 => "H",
32 => "",
64 => "",
_ => panic!("unexpected op size: {}", op_len)
}
}
impl CodeGenerator for ASMCodeGen {
fn start_code(&mut self, func_name: MuName, entry: MuName) -> ValueLocation {
self.cur = Some(Box::new(ASMCode {
......@@ -2172,23 +2086,28 @@ impl CodeGenerator for ASMCodeGen {
}
}
fn add_cfi_sections(&mut self, arg: &str) { self.add_asm_symbolic(format!(".cfi_sections {}", arg)); }
fn add_cfi_startproc(&mut self) {
self.add_asm_symbolic("\t.cfi_startproc".to_string());
self.add_asm_symbolic(".cfi_startproc".to_string());
}
fn add_cfi_endproc(&mut self) {
self.add_asm_symbolic("\t.cfi_endproc".to_string());
self.add_asm_symbolic(".cfi_endproc".to_string());
}
fn add_cfi_def_cfa_register(&mut self, reg: Reg) {
let reg = self.asm_reg_op(reg);
self.add_asm_symbolic(format!("\t.cfi_def_cfa_register {}", reg));
self.add_asm_symbolic(format!(".cfi_def_cfa_register {}", reg));
}
fn add_cfi_def_cfa_offset(&mut self, offset: i32) {
self.add_asm_symbolic(format!("\t.cfi_def_cfa_offset {}", offset));
self.add_asm_symbolic(format!(".cfi_def_cfa_offset {}", offset));
}
fn add_cfi_def_cfa(&mut self, reg: Reg, offset: i32) {
let reg = self.asm_reg_op(reg);
self.add_asm_symbolic(format!(".cfi_def_cfa {}, {}", reg, offset));
}
fn add_cfi_offset(&mut self, reg: Reg, offset: i32) {
let reg = self.asm_reg_op(reg);
self.add_asm_symbolic(format!("\t.cfi_offset {}, {}", reg, offset));
self.add_asm_symbolic(format!(".cfi_offset {}, {}", reg, offset));
}
fn emit_frame_grow(&mut self) {
......@@ -2222,6 +2141,8 @@ impl CodeGenerator for ASMCodeGen {
)
}
fn emit_add_str(&mut self, dest: Reg, src1: Reg, src2: &str) {self.internal_binop_str("ADD", dest, src1, src2)}
// Pushes a pair of registers on the givne stack (uses the STP instruction)
fn emit_push_pair(&mut self, src1: &P<Value>, src2: &P<Value>, stack: &P<Value>) {
trace!("emit: \tpush_pair {},{} -> {}[-8,-16]", src1, src2, stack);
......@@ -2424,7 +2345,13 @@ impl CodeGenerator for ASMCodeGen {
trace!("emit: \tFMOV {} -> {}", src, dest);
let (reg1, id1, loc1) = self.prepare_reg(dest, 4 + 1);
let asm = format!("FMOV {},#{}", reg1, src);
// GCC complains if the immediate argument has no decimal part (it will treat it as an integer)
// (e.g. #1 is an error, but #1.0 is not)
let asm = if src == src.trunc() { // src is an integer, append '.0'
format!("FMOV {},#{}.0", reg1, src)
} else {
format!("FMOV {},#{}", reg1, src)
};
self.add_asm_inst(
asm,
......@@ -2745,10 +2672,10 @@ pub fn emit_code(fv: &mut MuFunctionVersion, vm: &VM) {
Ok(file) => file
};
file.write("\t.arch armv8-a\n".as_bytes()).unwrap();
file.write(".arch armv8-a\n".as_bytes()).unwrap();
// constants in text section
file.write("\t.text\n".as_bytes()).unwrap();
file.write(".text\n".as_bytes()).unwrap();
write_const_min_align(&mut file);
......@@ -2784,7 +2711,7 @@ fn write_const_min_align(f: &mut File) {
#[cfg(target_os = "linux")]
fn write_align(f: &mut File, align: ByteSize) {
use std::io::Write;
f.write_fmt(format_args!("\t.balign {}\n", check_min_align(align))).unwrap();
f.write_fmt(format_args!(".balign {}\n", check_min_align(align))).unwrap();
}
fn write_const(f: &mut File, constant: P<Value>, loc: P<Value>) {
......@@ -2815,30 +2742,30 @@ fn write_const_value(f: &mut File, constant: P<Value>) {
&Constant::Int(val) => {
let len = ty.get_int_length().unwrap();
match len {
8 => f.write_fmt(format_args!("\t.byte {}\n", val as u8 )).unwrap(),
16 => f.write_fmt(format_args!("\t.word {}\n", val as u16)).unwrap(),
32 => f.write_fmt(format_args!("\t.long {}\n", val as u32)).unwrap(),
64 => f.write_fmt(format_args!("\t.quad {}\n", val as u64)).unwrap(),
8 => f.write_fmt(format_args!(".byte {}\n", val as u8 )).unwrap(),
16 => f.write_fmt(format_args!(".word {}\n", val as u16)).unwrap(),
32 => f.write_fmt(format_args!(".long {}\n", val as u32)).unwrap(),
64 => f.write_fmt(format_args!(".quad {}\n", val as u64)).unwrap(),
_ => panic!("unimplemented int length: {}", len)
}
}
&Constant::Float(val) => {
let bytes: [u8; 4] = unsafe {mem::transmute(val)};
f.write("\t.long ".as_bytes()).unwrap();
f.write(".long ".as_bytes()).unwrap();
f.write(&bytes).unwrap();
f.write("\n".as_bytes()).unwrap();
}
&Constant::Double(val) => {
let bytes: [u8; 8] = unsafe {mem::transmute(val)};
f.write("\t.quad ".as_bytes()).unwrap();
f.write(".quad ".as_bytes()).unwrap();
f.write(&bytes).unwrap();
f.write("\n".as_bytes()).unwrap();
}
&Constant::NullRef => {
f.write_fmt(format_args!("\t.quad 0\n")).unwrap()
f.write_fmt(format_args!(".quad 0\n")).unwrap()
}
&Constant::ExternSym(ref name) => {
f.write_fmt(format_args!("\t.quad {}\n", name)).unwrap()
f.write_fmt(format_args!(".quad {}\n", name)).unwrap()
}
&Constant::List(ref vals) => {
for val in vals {
......@@ -2871,10 +2798,10 @@ pub fn emit_context_with_reloc(vm: &VM,
};
// bss
file.write_fmt(format_args!("\t.bss\n")).unwrap();
file.write_fmt(format_args!(".bss\n")).unwrap();
// data
file.write("\t.data\n".as_bytes()).unwrap();
file.write(".data\n".as_bytes()).unwrap();
{
use runtime::mm;
......@@ -2943,20 +2870,20 @@ pub fn emit_context_with_reloc(vm: &VM,
let load_ref = unsafe {cur_addr.load::<Address>()};
if load_ref.is_zero() {
// write 0
file.write("\t.quad 0\n".as_bytes()).unwrap();
file.write(".quad 0\n".as_bytes()).unwrap();
} else {
let label = match relocatable_refs.get(&load_ref) {
Some(label) => label,
None => panic!("cannot find label for address {}, it is not dumped by GC (why GC didn't trace to it)", load_ref)
};
file.write_fmt(format_args!("\t.quad {}\n", label.clone())).unwrap();
file.write_fmt(format_args!(".quad {}\n", label.clone())).unwrap();
}
} else if fields.contains_key(&cur_addr) {
// write uptr (or other relocatable value) with label
let label = fields.get(&cur_addr).unwrap();
file.write_fmt(format_args!("\t.quad {}\n", label.clone())).unwrap();
file.write_fmt(format_args!(".quad {}\n", label.clone())).unwrap();
} else {
// write plain word (as bytes)
let next_word_addr = cur_addr.plus(POINTER_SIZE);
......@@ -3002,7 +2929,7 @@ fn write_data_bytes(f: &mut File, from: Address, to: Address) {
use std::io::Write;
if from < to {
f.write("\t.byte ".as_bytes()).unwrap();
f.write(".byte ".as_bytes()).unwrap();
let mut cursor = from;
while cursor < to {
......@@ -3024,10 +2951,6 @@ fn directive_globl(name: String) -> String {
format!(".globl {}", name)
}
fn directive_comm(name: String, size: ByteSize, align: ByteSize) -> String {
format!("\t.comm {},{},{}", name, size, align)
}
use compiler::machine_code::CompiledFunction;
pub fn spill_rewrite(
......@@ -3081,7 +3004,7 @@ pub fn spill_rewrite(
codegen.start_code_sequence();
let spill_mem = emit_mem(&mut codegen, &spill_mem, &mut func.context, vm);
let spill_mem = emit_mem(&mut codegen, &spill_mem, get_type_alignment(&temp.ty, vm), &mut func.context, vm);
codegen.emit_ldr_spill(&temp, &spill_mem);
codegen.finish_code_sequence_asm()
......@@ -3128,7 +3051,7 @@ pub fn spill_rewrite(
let mut codegen = ASMCodeGen::new();
codegen.start_code_sequence();
let spill_mem = emit_mem(&mut codegen, &spill_mem, &mut func.context, vm);
let spill_mem = emit_mem(&mut codegen, &spill_mem, get_type_alignment(&temp.ty, vm), &mut func.context, vm);
codegen.emit_str_spill(&spill_mem, &temp);
codegen.finish_code_sequence_asm()
......
......@@ -23,8 +23,10 @@ pub trait CodeGenerator {
fn end_block(&mut self, block_name: MuName);
// add CFI info
fn add_cfi_sections(&mut self, arg: &str);
fn add_cfi_startproc(&mut self);
fn add_cfi_endproc(&mut self);
fn add_cfi_def_cfa(&mut self, reg: Reg, offset: i32);
fn add_cfi_def_cfa_register(&mut self, reg: Reg);
fn add_cfi_def_cfa_offset(&mut self, offset: i32);
fn add_cfi_offset(&mut self, reg: Reg, offset: i32);
......@@ -35,6 +37,10 @@ pub trait CodeGenerator {
fn emit_frame_grow(&mut self); // Emits a SUB
fn emit_frame_shrink(&mut self); // Emits an ADD
// Used to pass a string that the assembler will interpret as an immediate argument
// (This is neccesary to support the use of ELF relocations like ':tprel_hi12:foo')
fn emit_add_str(&mut self, dest: Reg, src1: Reg, src2: &str);
// stack minimpulation
fn emit_push_pair(&mut self, src1: Reg, src2: Reg, stack: Reg); // Emits a STP
fn emit_pop_pair(&mut self, dest1: Reg, dest2: Reg, stack: Reg); // Emits a LDP
......
#![allow(unused_variables)]
#![warn(unused_imports)]
#![warn(unreachable_code)]
#![warn(dead_code)]
use ast::ir::*;
use ast::ptr::*;
use ast::inst::*;
use ast::op;
use ast::op::*;
use ast::types;
use ast::types::*;
use vm::VM;
use runtime::mm;
......@@ -40,12 +38,13 @@ pub struct InstructionSelection {
current_callsite_id: usize,
current_frame: Option<Frame>,
current_block: Option<MuName>,
current_block_in_ir: Option<MuName>,
current_func_start: Option<ValueLocation>,
// key: block id, val: callsite that names the block as exception block
current_exn_callsites: HashMap<MuID, Vec<ValueLocation>>,
// key: block id, val: block location
current_exn_blocks: HashMap<MuID, ValueLocation>,