Commit 6e2649fe authored by qinsoon's avatar qinsoon

Merge branch 'develop' into issue82-inf-loop-in-peephole

parents 6007d267 6d862159
......@@ -42,6 +42,28 @@ test:cargo:runtime:
script:
- RUST_BACKTRACE=1 RUST_TEST_THREADS=1 ./test-release --color=always test_runtime 2> /dev/null
.build_muc: &build_muc |
if [ -d "tests/test_muc/mu-tool-compiler" ]; then rm -Rf tests/test_muc/mu-tool-compiler; fi
cd tests/test_muc
git clone https://gitlab.anu.edu.au/mu/mu-tool-compiler
cd mu-tool-compiler
mkdir lib
ln -s $MU_ZEBU/target/release/libmu.so lib/
make
cd ..
testmuc:test_simple:
stage: test
script:
- *build_muc
- LD_LIBRARY_PATH=mu-tool-compiler/lib/ MUC=mu-tool-compiler/muc python2 -m pytest test_simple.py -v
testmuc:test_swapstack:
stage: test
script:
- *build_muc
- LD_LIBRARY_PATH=mu-tool-compiler/lib/ MUC=mu-tool-compiler/muc python2 -m pytest test_thread_and_stack.py -v
testjit:milestones:
stage: test
script:
......@@ -60,6 +82,8 @@ testjit:cmpops:
testjit:controlflow:
stage: test
script:
# run this test under test_jit directory
# as a C source file is expected in a relative path to current working directory
- cd tests/test_jit
- RUST_BACKTRACE=1 pytest test_controlflow.py -v --color=yes
......
......@@ -22,6 +22,8 @@ extern crate gcc;
#[cfg(target_arch = "x86_64")]
fn main() {
gcc::Build::new()
.flag("-O3")
.flag("-c")
.file("src/runtime/runtime_c_x64_sysv.c")
.compile("libruntime_c.a");
......@@ -39,6 +41,8 @@ fn main() {
#[cfg(target_arch = "aarch64")]
fn main() {
gcc::Build::new()
.flag("-O3")
.flag("-c")
.file("src/runtime/runtime_c_aarch64_sysv.c")
.compile("libruntime_c.a");
......
......@@ -55,5 +55,5 @@ else
git -C ./RPySOM submodule init
git -C ./RPySOM submodule update
fi
pytest test_*.py -v --color=yes 2>&1 | tee $MU_ZEBU/pytest_out.txt
shopt -s extglob
pytest ./test_!(pypy).py -v --color=yes 2>&1 | tee $MU_ZEBU/pytest_out.txt
......@@ -26,4 +26,5 @@ lazy_static = "*"
log = "*"
simple_logger = "*"
rodal = { git = "https://gitlab.anu.edu.au/mu/rodal", version = ">= 0.0.5" }
regex = "*"
#rodal = { path = "../../../rodal_test/rodal", version = ">= 0.0.5" }
......@@ -63,6 +63,20 @@ impl Instruction {
use inst::Instruction_::*;
match self.v {
Return(_) |
ThreadExit |
Throw(_) |
TailCall(_) |
Branch1(_) |
Branch2 { .. } |
Watchpoint { .. } |
WPBranch { .. } |
Call { .. } |
CCall { .. } |
SwapStackExc { .. } |
SwapStackKill { .. } |
Switch { .. } |
ExnInstruction { .. } => true,
BinOp(_, _, _) |
BinOpWithStatus(_, _, _, _) |
CmpOp(_, _, _) |
......@@ -78,8 +92,7 @@ impl Instruction {
NewHybrid(_, _) |
AllocAHybrid(_, _) |
NewStack(_) |
NewThread(_, _) |
NewThreadExn(_, _) |
NewThread { .. } |
NewFrameCursor(_) |
GetIRef(_) |
GetFieldIRef { .. } |
......@@ -105,20 +118,10 @@ impl Instruction {
CommonInst_Tr64ToTag(_) |
Move(_) |
PrintHex(_) |
SetRetval(_) => false,
Return(_) |
ThreadExit |
Throw(_) |
TailCall(_) |
Branch1(_) |
Branch2 { .. } |
Watchpoint { .. } |
WPBranch { .. } |
Call { .. } |
CCall { .. } |
SwapStack { .. } |
Switch { .. } |
ExnInstruction { .. } => true
SetRetval(_) |
KillStack(_) |
CurrentStack |
SwapStackExpr { .. } => false
}
}
......@@ -135,58 +138,65 @@ impl Instruction {
use inst::Instruction_::*;
match self.v {
BinOp(_, _, _) => false,
BinOpWithStatus(_, _, _, _) => false,
CmpOp(_, _, _) => false,
ConvOp { .. } => false,
ExprCall { .. } => true,
ExprCCall { .. } => true,
Load { .. } => true,
Store { .. } => true,
CmpXchg { .. } => true,
AtomicRMW { .. } => true,
New(_) => true,
AllocA(_) => true,
NewHybrid(_, _) => true,
AllocAHybrid(_, _) => true,
NewStack(_) => true,
NewThread(_, _) => true,
NewThreadExn(_, _) => true,
NewFrameCursor(_) => true,
GetIRef(_) => false,
GetFieldIRef { .. } => false,
GetElementIRef { .. } => false,
ShiftIRef { .. } => false,
GetVarPartIRef { .. } => false,
Fence(_) => true,
Return(_) => true,
ThreadExit => true,
Throw(_) => true,
TailCall(_) => true,
Branch1(_) => true,
Branch2 { .. } => true,
Select { .. } => false,
Watchpoint { .. } => true,
WPBranch { .. } => true,
Call { .. } => true,
CCall { .. } => true,
SwapStack { .. } => true,
Switch { .. } => true,
ExnInstruction { .. } => true,
CommonInst_GetThreadLocal => true,
CommonInst_SetThreadLocal(_) => true,
CommonInst_Pin(_) | CommonInst_Unpin(_) | CommonInst_GetAddr(_) => true,
CommonInst_Tr64IsFp(_) | CommonInst_Tr64IsInt(_) | CommonInst_Tr64IsRef(_) => false,
CommonInst_Tr64FromFp(_) | CommonInst_Tr64FromInt(_) | CommonInst_Tr64FromRef(_, _) => {
false
}
ExprCall { .. } |
ExprCCall { .. } |
Load { .. } |
Store { .. } |
CmpXchg { .. } |
AtomicRMW { .. } |
New(_) |
AllocA(_) |
NewHybrid(_, _) |
AllocAHybrid(_, _) |
NewStack(_) |
NewThread { .. } |
NewFrameCursor(_) |
Fence(_) |
Return(_) |
ThreadExit |
Throw(_) |
TailCall(_) |
Branch1(_) |
Branch2 { .. } |
Watchpoint { .. } |
WPBranch { .. } |
Call { .. } |
CCall { .. } |
SwapStackExpr { .. } |
SwapStackExc { .. } |
SwapStackKill { .. } |
Switch { .. } |
ExnInstruction { .. } |
CommonInst_GetThreadLocal |
CommonInst_SetThreadLocal(_) |
CommonInst_Pin(_) |
CommonInst_Unpin(_) |
CommonInst_GetAddr(_) |
PrintHex(_) |
SetRetval(_) |
KillStack(_) => true,
BinOp(_, _, _) |
BinOpWithStatus(_, _, _, _) |
CmpOp(_, _, _) |
ConvOp { .. } |
GetIRef(_) |
GetFieldIRef { .. } |
GetElementIRef { .. } |
ShiftIRef { .. } |
GetVarPartIRef { .. } |
Select { .. } |
CommonInst_Tr64IsFp(_) |
CommonInst_Tr64IsInt(_) |
CommonInst_Tr64IsRef(_) |
CommonInst_Tr64FromFp(_) |
CommonInst_Tr64FromInt(_) |
CommonInst_Tr64FromRef(_, _) |
CommonInst_Tr64ToFp(_) |
CommonInst_Tr64ToInt(_) |
CommonInst_Tr64ToRef(_) |
CommonInst_Tr64ToTag(_) => false,
Move(_) => false,
PrintHex(_) => true,
SetRetval(_) => true
CommonInst_Tr64ToTag(_) |
Move(_) |
CurrentStack => false
}
}
......@@ -199,9 +209,8 @@ impl Instruction {
Watchpoint { .. } |
Call { .. } |
CCall { .. } |
SwapStack { .. } |
SwapStackExc { .. } |
ExnInstruction { .. } => true,
BinOp(_, _, _) |
BinOpWithStatus(_, _, _, _) |
CmpOp(_, _, _) |
......@@ -217,8 +226,7 @@ impl Instruction {
NewHybrid(_, _) |
AllocAHybrid(_, _) |
NewStack(_) |
NewThread(_, _) |
NewThreadExn(_, _) |
NewThread { .. } |
NewFrameCursor(_) |
GetIRef(_) |
GetFieldIRef { .. } |
......@@ -252,7 +260,11 @@ impl Instruction {
CommonInst_Tr64ToTag(_) |
Move(_) |
PrintHex(_) |
SetRetval(_) => false
SetRetval(_) |
KillStack(_) |
CurrentStack |
SwapStackExpr { .. } |
SwapStackKill { .. } => false
}
}
......@@ -269,9 +281,8 @@ impl Instruction {
Watchpoint { ref resume, .. } |
Call { ref resume, .. } |
CCall { ref resume, .. } |
SwapStack { ref resume, .. } |
SwapStackExc { ref resume, .. } |
ExnInstruction { ref resume, .. } => Some(resume.exn_dest.target),
BinOp(_, _, _) |
BinOpWithStatus(_, _, _, _) |
CmpOp(_, _, _) |
......@@ -287,8 +298,7 @@ impl Instruction {
NewHybrid(_, _) |
AllocAHybrid(_, _) |
NewStack(_) |
NewThread(_, _) |
NewThreadExn(_, _) |
NewThread { .. } |
NewFrameCursor(_) |
GetIRef(_) |
GetFieldIRef { .. } |
......@@ -322,7 +332,11 @@ impl Instruction {
CommonInst_Tr64ToTag(_) |
Move(_) |
PrintHex(_) |
SetRetval(_) => None
SetRetval(_) |
KillStack(_) |
CurrentStack |
SwapStackExpr { .. } |
SwapStackKill { .. } => None
}
}
......@@ -433,13 +447,20 @@ pub enum Instruction_ {
/// args: functionref of the entry function
NewStack(OpIndex),
/// kill the given Mu stack
KillStack(OpIndex),
/// return stackref for the current stack
CurrentStack,
/// create a new Mu thread, yields thread reference
/// args: stackref of a Mu stack, a list of arguments
NewThread(OpIndex, Vec<OpIndex>), // stack, args
/// create a new Mu thread, yields thread reference (thread resumes with exceptional value)
/// args: stackref of a Mu stack, an exceptional value
NewThreadExn(OpIndex, OpIndex), // stack, exception
NewThread {
stack: OpIndex,
thread_local: Option<OpIndex>,
is_exception: bool,
args: Vec<OpIndex>
},
/// create a frame cursor reference
/// args: stackref of a Mu stack
......@@ -539,15 +560,29 @@ pub enum Instruction_ {
resume: ResumptionData
},
/// swapstack. swap current Mu stack with the named Mu stack,
/// and continue with specified resumption
SwapStack {
/// A swap stack with an exception clause (i.e. uses the RET_WITH form)
SwapStackExc {
stack: OpIndex,
is_exception: bool,
args: Vec<OpIndex>,
resume: ResumptionData
},
/// A swap stack without an exception clause that is not a terminator
/// (i.e. uses the RET_WITH form)
SwapStackExpr {
stack: OpIndex,
is_exception: bool,
args: Vec<OpIndex>
},
/// A swapstack without an exception clause that is a terminator (i.e. one with KILL_OLD)
SwapStackKill {
stack: OpIndex,
is_exception: bool,
args: Vec<OpIndex>
},
/// a multiway branch
Switch {
cond: OpIndex,
......@@ -685,17 +720,24 @@ impl Instruction_ {
&Instruction_::AllocA(ref ty) => format!("ALLOCA {}", ty),
&Instruction_::NewHybrid(ref ty, len) => format!("NEWHYBRID {} {}", ty, ops[len]),
&Instruction_::AllocAHybrid(ref ty, len) => format!("ALLOCAHYBRID {} {}", ty, ops[len]),
&Instruction_::NewStack(func) => format!("NEWSTACK {}", ops[func]),
&Instruction_::NewThread(stack, ref args) => {
&Instruction_::NewStack(func) => format!("NEW_STACK {}", ops[func]),
&Instruction_::NewThread {
stack,
thread_local,
is_exception,
ref args
} => {
let thread_local = thread_local
.map(|t| format!("{}", ops[t]))
.unwrap_or("NULL".to_string());
format!(
"NEWTHREAD {} PASS_VALUES {}",
"SWAPSTACK {} THREADLOCAL({}) {} {}",
ops[stack],
op_vector_str(args, ops)
thread_local,
is_exception,
op_vector_str(args, ops),
)
}
&Instruction_::NewThreadExn(stack, exn) => {
format!("NEWTHREAD {} THROW_EXC {}", ops[stack], ops[exn])
}
&Instruction_::NewFrameCursor(stack) => format!("NEWFRAMECURSOR {}", ops[stack]),
&Instruction_::GetIRef(reference) => format!("GETIREF {}", ops[reference]),
&Instruction_::GetFieldIRef {
......@@ -731,6 +773,8 @@ impl Instruction_ {
&Instruction_::Return(ref vals) => format!("RET {}", op_vector_str(vals, ops)),
&Instruction_::ThreadExit => "THREADEXIT".to_string(),
&Instruction_::CurrentStack => "CURRENT_STACK".to_string(),
&Instruction_::KillStack(s) => format!("RET {}", ops[s]),
&Instruction_::Throw(exn_obj) => format!("THROW {}", ops[exn_obj]),
&Instruction_::TailCall(ref call) => format!("TAILCALL {}", call.debug_str(ops)),
&Instruction_::Branch1(ref dest) => format!("BRANCH {}", dest.debug_str(ops)),
......@@ -797,7 +841,19 @@ impl Instruction_ {
ref data,
ref resume
} => format!("CCALL {} {}", data.debug_str(ops), resume.debug_str(ops)),
&Instruction_::SwapStack {
&Instruction_::SwapStackExpr {
stack,
is_exception,
ref args
} => {
format!(
"SWAPSTACK {} {} {}",
ops[stack],
is_exception,
op_vector_str(args, ops),
)
}
&Instruction_::SwapStackExc {
stack,
is_exception,
ref args,
......@@ -811,6 +867,20 @@ impl Instruction_ {
resume.debug_str(ops)
)
}
&Instruction_::SwapStackKill {
stack,
is_exception,
ref args
} => {
format!(
"SWAPSTACK {} {} {}",
ops[stack],
is_exception,
op_vector_str(args, ops),
)
}
&Instruction_::Switch {
cond,
ref default,
......@@ -948,7 +1018,7 @@ impl fmt::Debug for BinOpStatus {
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
pub enum MemoryOrder {
NotAtomic,
Relaxed,
......
......@@ -795,7 +795,7 @@ impl BlockContent {
}
Instruction_::Call { ref resume, .. } |
Instruction_::CCall { ref resume, .. } |
Instruction_::SwapStack { ref resume, .. } |
Instruction_::SwapStackExc { ref resume, .. } |
Instruction_::ExnInstruction { ref resume, .. } => {
let mut live_outs = vec![];
live_outs.append(&mut resume.normal_dest.get_arguments(&ops));
......@@ -1012,6 +1012,23 @@ impl Value {
}
}
pub fn is_const_zero(&self) -> bool {
match self.v {
Value_::Constant(Constant::Int(val)) if val == 0 => true,
Value_::Constant(Constant::Double(val)) if val == 0f64 => true,
Value_::Constant(Constant::Float(val)) if val == 0f32 => true,
Value_::Constant(Constant::IntEx(ref vec)) => {
if vec.iter().all(|x| *x == 0) {
true
} else {
false
}
}
Value_::Constant(Constant::NullRef) => true,
_ => false
}
}
/// disguises a value as another type.
/// This is usually used for treat an integer type as an integer of a different length
/// This method is unsafe
......@@ -1077,7 +1094,7 @@ impl Value {
}
}
const DISPLAY_ID: bool = false;
const DISPLAY_ID: bool = true;
const DISPLAY_TYPE: bool = true;
const PRINT_ABBREVIATE_NAME: bool = true;
......@@ -1469,7 +1486,8 @@ pub fn mangle_name(name: MuName) -> MuName {
"__mu_".to_string() + name.as_str()
}
// WARNING: This only reverses mangle_name above when no warning is issued)
/// demangles a Mu name
// WARNING: This only reverses mangle_name above when no warning is issued)
pub fn demangle_name(mut name: MuName) -> MuName {
let name = if cfg!(target_os = "macos") && name.starts_with("___mu_") {
name.split_off(1)
......@@ -1492,57 +1510,28 @@ pub fn demangle_name(mut name: MuName) -> MuName {
name
}
// TODO: Why the hell isn't this working?
pub fn demangle_text(text: String) -> String {
let text = text.as_bytes();
let n = text.len();
let mut output = String::new();
extern crate regex;
// We have a mangled name
let mut last_i = 0; // The last i value that we dumped to output
let mut i = 0;
// TODO: this should work for utf-8 stuff right? (sinces all mangled names are in ascii)
while i < n {
let c = text[i] as char;
// We're at the beginining of the string
// wait for a word boundry
if c.is_alphanumeric() || c == '_' {
// We just found a mangled name
if text[i..].starts_with("__mu_".as_bytes()) {
output += std::str::from_utf8(&text[last_i..i]).unwrap();
let start = i;
// Find the end of the name
while i < n {
let c = text[i] as char;
if !c.is_alphanumeric() && c != '_' {
break; // We found the end!
}
i += 1;
}
/// identifies mu names and demangles them
pub fn demangle_text(text: String) -> String {
use self::regex::Regex;
output +=
demangle_name(String::from_utf8(text[start..i].to_vec()).unwrap()).as_str();
// Skip to the end of the name
last_i = i;
continue;
} else {
// Skip to the end of this alphanumeric sequence
while i < n {
let c = text[i] as char;
if !c.is_alphanumeric() && c != '_' {
break; // We found the end!
}
i += 1;
}
}
lazy_static!{
static ref IDENT_NAME: Regex = if cfg!(target_os = "macos") {
Regex::new(r"___mu_\w+").unwrap()
} else {
Regex::new(r"__mu_\w+").unwrap()
};
}
continue;
}
// Not the start of mangled name, continue
i += 1;
let mut res = text.clone();
for cap in IDENT_NAME.captures_iter(&text) {
let name = cap.get(0).unwrap().as_str().to_string();
let demangled = demangle_name(name.clone());
res = res.replacen(&name, &demangled, 1);
}
// Return output plus whatever is left of the string
output + std::str::from_utf8(&text[last_i..n]).unwrap()
res
}
......
......@@ -33,6 +33,7 @@
#[macro_use]
extern crate rodal;
extern crate log;
extern crate simple_logger;
#[macro_use]
extern crate lazy_static;
......
......@@ -1037,52 +1037,6 @@ impl ASMCodeGen {
self.cur_mut().code.push(ASMInst::symbolic(code));
}
fn add_asm_call(
&mut self,
code: String,
potentially_excepting: Option<MuName>,
arguments: Vec<P<Value>>,
target: Option<(MuID, ASMLocation)>
) {
// a call instruction will use all the argument registers
// do not need
let mut uses: LinkedHashMap<MuID, Vec<ASMLocation>> = LinkedHashMap::new();
if target.is_some() {
let (id, loc) = target.unwrap();
uses.insert(id, vec![loc]);
}
for arg in arguments {
uses.insert(arg.id(), vec![]);
}
let mut defines: LinkedHashMap<MuID, Vec<ASMLocation>> = LinkedHashMap::new();
for reg in CALLER_SAVED_GPRS.iter() {
if !defines.contains_key(&reg.id()) {
defines.insert(reg.id(), vec![]);
}
}
for reg in CALLER_SAVED_FPRS.iter() {
if !defines.contains_key(&reg.id()) {
defines.insert(reg.id(), vec![]);
}
}
self.add_asm_inst_internal(
code,
defines,
uses,
false,
{
if potentially_excepting.is_some() {
ASMBranchTarget::PotentiallyExcepting(potentially_excepting.unwrap())
} else {
ASMBranchTarget::None
}
},
None
)
}
fn add_asm_inst(
&mut self,
code: String,
......@@ -1147,6 +1101,7 @@ impl ASMCodeGen {
trace!("asm: {}", demangle_text(code.clone()));
trace!(" defines: {:?}", defines);
trace!(" uses: {:?}", uses);
trace!(" target: {:?}", target);
let mc = self.cur_mut();
// put the instruction
......@@ -2162,6 +2117,57 @@ impl ASMCodeGen {
self.add_asm_inst(asm, ignore_zero_register(id1, vec![loc1]), uses, false)
}
fn internal_call(
&mut self,
callsite: Option<String>,
code: String,
pe: Option<MuName>,
args: Vec<P<Value>>,
ret: Vec<P<Value>>,
target: Option<(MuID, ASMLocation)>,
may_return: bool
) -> Option<ValueLocation> {
let mut uses: LinkedHashMap<MuID, Vec<ASMLocation>> = LinkedHashMap::new();
if target.is_some() {
let (id, loc) = target.unwrap();
uses.insert(id, vec![loc]);
}
for arg in args {
uses.insert(arg.id(), vec![]);
}
let mut defines: LinkedHashMap<MuID, Vec<ASMLocation>> = LinkedHashMap::new();
for ret in ret.iter() {
defines.insert(ret.id(), vec![]);
}
self.add_asm_inst_internal(
code,
defines,
uses,
false,
{
if pe.is_some() {
ASMBranchTarget::PotentiallyExcepting(pe.unwrap())
} else if may_return {
ASMBranchTarget::None
} else {
ASMBranchTarget::Return
}
},
None
);
if callsite.is_some() {
let callsite_symbol = mangle_name(callsite.as_ref().unwrap().clone());
self.add_asm_symbolic(directive_globl(callsite_symbol.clone()));
self.add_asm_symbolic(format!("{}:", callsite_symbol.clone()));
Some(ValueLocation::Relocatable(RegGroup::GPR, callsite.unwrap()))
} else {
None
}
}