Added the RTAttr type and the relevant unit-test.

parent 7e06b6a8
Pipeline #3700 failed with stages
in 3 minutes and 25 seconds
......@@ -15,7 +15,7 @@
[package]
name = "mu"
version = "0.0.1"
authors = [ "Your name <you@example.com>" ]
authors = [ "Your name <you@example.com>", "Javad Amiri <javad.amiri@anu.edu.au>" ]
build = "build.rs"
[lib]
......@@ -26,7 +26,7 @@ doctest = false
default = ["aot"]
aot = []
jit = []
realtime = []
realtime = ["mu_ast/realtime", "mu_utils/realtime", "mu_gc/realtime"]
[build-dependencies]
cc = "*"
......
......@@ -15,11 +15,17 @@
[package]
name = "mu_ast"
version = "0.0.1"
authors = ["qinsoon <qinsoon@gmail.com>"]
authors = ["qinsoon <qinsoon@gmail.com>", "Javad Amiri <javad.amiri@anu.edu.au>"]
[features]
realtime = []
[lib]
crate-type = ["rlib"]
[build-dependencies]
log = "*"
[dependencies]
mu_utils = {path = "../utils"}
lazy_static = "0.2.11"
......
......@@ -46,6 +46,8 @@ impl Instruction {
clone
}
// RTMU add new rt instructions to various spec functions here
/// is this instruction the terminal inst of its block?
/// Terminal instructions end Mu blocks, and Mu block ends with a terminal instruction.
pub fn is_terminal_inst(&self) -> bool {
......@@ -82,6 +84,7 @@ impl Instruction {
| AllocAHybrid(_, _)
| NewStack(_)
| NewThread { .. }
| NewThreadRT { .. }
| NewFrameCursor(_)
| GetIRef(_)
| GetFieldIRef { .. }
......@@ -140,6 +143,7 @@ impl Instruction {
| AllocAHybrid(_, _)
| NewStack(_)
| NewThread { .. }
| NewThreadRT { .. }
| NewFrameCursor(_)
| Fence(_)
| Return(_)
......@@ -218,6 +222,7 @@ impl Instruction {
| AllocAHybrid(_, _)
| NewStack(_)
| NewThread { .. }
| NewThreadRT { .. }
| NewFrameCursor(_)
| GetIRef(_)
| GetFieldIRef { .. }
......@@ -291,6 +296,7 @@ impl Instruction {
| AllocAHybrid(_, _)
| NewStack(_)
| NewThread { .. }
| NewThreadRT { .. }
| NewFrameCursor(_)
| GetIRef(_)
| GetFieldIRef { .. }
......@@ -366,6 +372,7 @@ impl Instruction {
AllocAHybrid(_, _) |
NewStack(_) |
NewThread { .. } |
NewThreadRT { .. } |
NewFrameCursor(_) |
GetIRef(_) |
GetFieldIRef { .. } |
......@@ -541,6 +548,23 @@ impl Instruction {
ops[stack], thread_local, new_stack_clause,
)
}
// Fixme write a better debug-str
&Instruction_::NewThreadRT {
attr,
stack,
thread_local,
is_exception,
ref args,
} => {
let new_stack_clause = format_new_stack_clause(is_exception, args, ops);
let thread_local = thread_local
.map(|t| format!(" THREADLOCAL({})", ops[t]))
.unwrap_or("".to_string());
format!(
"NEWTHREADRT {}{}{} {}",
ops[attr], ops[stack], thread_local, new_stack_clause,
)
}
&Instruction_::NewFrameCursor(stack) => {
format!("COMMINST @uvm.meta.new_cursor({})", ops[stack])
}
......@@ -928,6 +952,16 @@ pub enum Instruction_ {
args: Vec<OpIndex>,
},
/// create a new Mu thread, yields thread reference
/// args: stackref of a Mu stack, a list of arguments
NewThreadRT {
attr: OpIndex,
stack: OpIndex,
thread_local: Option<OpIndex>,
is_exception: bool,
args: Vec<OpIndex>,
},
/// create a frame cursor reference
/// args: stackref of a Mu stack
NewFrameCursor(OpIndex), // stack
......
......@@ -15,9 +15,9 @@
#[cfg(target_os = "linux")]
pub use ir_rt_linux::*;
pub type RTMuThreadID = SysThreadID;
pub type RTMuThreadAttr = SysThreadAttr;
pub type RTMuPriority = SysPriority;
pub type RTMuTime = SysTime;
pub type RTMuAffinityMask = SysAffinityMask;
pub type RTMuAffinitySize = SysAffinitySize;
pub type MuThreadID = SysThreadID;
pub type MuThreadAttr = SysThreadAttr;
pub type MuPriority = SysPriority;
pub type MuTime = SysTime;
pub type MuAffinityMask = SysAffinityMask;
pub type MuAffinitySize = SysAffinitySize;
......@@ -79,8 +79,8 @@ pub mod op;
pub mod ptr;
pub mod types;
#[cfg(feature="realtime")]
#[cfg(feature = "realtime")]
pub mod ir_rt;
#[cfg(all(feature="realtime", target_os = "linux"))]
pub mod ir_rt_linux;
\ No newline at end of file
#[cfg(all(feature = "realtime", target_os = "linux"))]
pub mod ir_rt_linux;
......@@ -26,6 +26,7 @@ use std::sync::atomic::{AtomicPtr, Ordering};
use std::sync::RwLock;
// some common types that the compiler may use internally
#[cfg(not(feature = "realtime"))]
lazy_static! {
pub static ref ADDRESS_TYPE: P<MuType> = P(MuType::new(
new_internal_id(),
......@@ -80,6 +81,63 @@ lazy_static! {
];
}
#[cfg(feature = "realtime")]
lazy_static! {
pub static ref ADDRESS_TYPE: P<MuType> = P(MuType::new(
new_internal_id(),
MuType_::int(POINTER_SIZE * 8)
));
pub static ref UINT1_TYPE: P<MuType> = P(MuType::new(new_internal_id(), MuType_::int(1)));
pub static ref UINT8_TYPE: P<MuType> = P(MuType::new(new_internal_id(), MuType_::int(8)));
pub static ref UINT16_TYPE: P<MuType> = P(MuType::new(new_internal_id(), MuType_::int(16)));
pub static ref UINT32_TYPE: P<MuType> = P(MuType::new(new_internal_id(), MuType_::int(32)));
pub static ref UINT64_TYPE: P<MuType> = P(MuType::new(new_internal_id(), MuType_::int(64)));
pub static ref UINT128_TYPE: P<MuType> = P(MuType::new(new_internal_id(), MuType_::int(128)));
pub static ref FLOAT_TYPE: P<MuType> = P(MuType::new(new_internal_id(), MuType_::float()));
pub static ref DOUBLE_TYPE: P<MuType> = P(MuType::new(new_internal_id(), MuType_::double()));
pub static ref VOID_TYPE: P<MuType> = P(MuType::new(new_internal_id(), MuType_::void()));
pub static ref REF_VOID_TYPE: P<MuType> = P(MuType::new(
new_internal_id(),
MuType_::muref(VOID_TYPE.clone())
));
pub static ref IREF_VOID_TYPE: P<MuType> = P(MuType::new(
new_internal_id(),
MuType_::iref(VOID_TYPE.clone())
));
pub static ref UPTR_U8_TYPE: P<MuType> = P(MuType::new(
new_internal_id(),
MuType_::uptr(UINT8_TYPE.clone())
));
pub static ref UPTR_U64_TYPE: P<MuType> = P(MuType::new(
new_internal_id(),
MuType_::uptr(UINT64_TYPE.clone())
));
pub static ref RTATTR_TYPE: P<MuType> = P(MuType::new(new_internal_id(), MuType_::RTAttr));
pub static ref STACKREF_TYPE: P<MuType> = P(MuType::new(new_internal_id(), MuType_::StackRef));
pub static ref THREADREF_TYPE: P<MuType> =
P(MuType::new(new_internal_id(), MuType_::ThreadRef));
pub static ref INTERNAL_TYPES: Vec<P<MuType>> = vec![
ADDRESS_TYPE.clone(),
UINT1_TYPE.clone(),
UINT8_TYPE.clone(),
UINT16_TYPE.clone(),
UINT32_TYPE.clone(),
UINT64_TYPE.clone(),
UINT128_TYPE.clone(),
FLOAT_TYPE.clone(),
DOUBLE_TYPE.clone(),
FLOAT_TYPE.clone(),
VOID_TYPE.clone(),
REF_VOID_TYPE.clone(),
IREF_VOID_TYPE.clone(),
STACKREF_TYPE.clone(),
THREADREF_TYPE.clone(),
UPTR_U8_TYPE.clone(),
UPTR_U64_TYPE.clone(),
RTATTR_TYPE.clone()
];
}
/// clear struct/hybrid maps, called when creating new VM
pub fn init_types() {
{
......@@ -196,6 +254,15 @@ impl MuType {
}
}
#[cfg(feature = "realtime")]
pub fn is_rtattr(&self) -> bool {
match self.v {
// #[cfg(realtime)]
MuType_::RTAttr => true,
_ => false,
}
}
pub fn is_eq_comparable(&self) -> bool {
self.is_int()
|| self.is_ptr()
......@@ -239,6 +306,8 @@ impl MuType {
| MuType_::StackRef
| MuType_::Tagref64
| MuType_::UPtr(_) => true,
#[cfg(feature = "realtime")]
MuType_::RTAttr => true,
_ => false,
}
}
......@@ -525,7 +594,12 @@ pub enum MuType_ {
/// ufuncptr<@sig>
UFuncPtr(P<MuFuncSig>),
/// RTMu-specific
/// Real-Time Threads' attributes
#[cfg(feature = "realtime")]
RTAttr,
}
impl MuType_ {
pub fn strong_variant(&self) -> MuType_ {
match self {
......@@ -534,10 +608,17 @@ impl MuType_ {
}
}
}
#[cfg(not(feature = "realtime"))]
rodal_enum!(MuType_{(Int: size), Float, Double, (Ref: ty), (IRef: ty), (WeakRef: ty), (UPtr: ty),
(Struct: tag), (Array: ty, size), (Hybrid: tag), Void, ThreadRef, StackRef, Tagref64,
(Vector: ty, size), (FuncRef: ty), (UFuncPtr: ty)});
#[cfg(feature = "realtime")]
rodal_enum!(MuType_{(Int: size), Float, Double, (Ref: ty), (IRef: ty), (WeakRef: ty), (UPtr: ty),
(Struct: tag), (Array: ty, size), (Hybrid: tag), Void, ThreadRef, RTAttr, StackRef, Tagref64,
(Vector: ty, size), (FuncRef: ty), (UFuncPtr: ty)});
impl fmt::Display for MuType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.v)
......@@ -557,6 +638,8 @@ impl fmt::Display for MuType_ {
&MuType_::Array(ref ty, size) => write!(f, "array<{} {}>", ty, size),
&MuType_::Void => write!(f, "void"),
&MuType_::ThreadRef => write!(f, "threadref"),
#[cfg(feature = "realtime")]
&MuType_::RTAttr => write!(f, "rtattr"),
&MuType_::StackRef => write!(f, "stackref"),
&MuType_::Tagref64 => write!(f, "tagref64"),
&MuType_::Vector(ref ty, size) => write!(f, "vector<{} {}>", ty, size),
......@@ -699,6 +782,10 @@ impl MuType_ {
pub fn threadref() -> MuType_ {
MuType_::ThreadRef
}
#[cfg(feature = "realtime")]
pub fn rtattr() -> MuType_ {
MuType_::RTAttr
}
pub fn stackref() -> MuType_ {
MuType_::StackRef
}
......
......@@ -1989,6 +1989,160 @@ impl<'a> InstructionSelection {
}
}
/// This instruction is the same as NewThread, with one change:
/// - the `NEW_THREAD_NORMAL` entrypoint function for realtime threads (RTMu),
/// gets an additional `attr: RTAttr` argument
Instruction_::NewThreadRT {
attr,
stack,
thread_local,
is_exception,
ref args,
} => {
trace!("instsel on NEWTHREADRT");
let ref ops = inst.ops;
let res = self.get_result_value(node);
let attr = self.emit_ireg(&ops[attr], f_content, f_context, vm);
let stack = self.emit_ireg(&ops[stack], f_content, f_context, vm);
let tl = match thread_local {
Some(tl) => self.emit_ireg(&ops[tl], f_content, f_context, vm),
None => self.make_nullref(vm),
};
if is_exception {
let exc = self.emit_ireg(&ops[args[0]], f_content, f_context, vm);
self.emit_runtime_entry(
&entrypoints::NEW_THREAD_EXCEPTIONAL,
vec![attr, stack, tl, exc],
Some(vec![res]),
Some(node),
f_content,
f_context,
vm,
);
} else {
let new_sp = self.make_temporary(f_context, ADDRESS_TYPE.clone(), vm);
self.emit_load_base_offset(
&new_sp,
&stack,
*thread::MUSTACK_SP_OFFSET as i32,
vm,
);
// prepare arguments on the new stack in the generated code
// check thread::MuStack::setup_args() for how we do it in the runtime
//
// 1. the stack arguments will be put to a reserved location during
// MuStack::new(), it is from (new_sp - 2*POINTER_SIZE) to
// (new_sp - 2*POINTER_SIZE - stack_arg_size)
// 2. the register arguments will be pushed to current SP, the start
// function will consume them.
{
use compiler::backend::x86_64::callconv::swapstack;
use compiler::backend::x86_64::callconv::CallConvResult;
use compiler::backend::x86_64::{ARGUMENT_FPRS, ARGUMENT_GPRS};
let arg_values =
self.process_arguments(&args, ops, f_content, f_context, vm);
// compute call convention
let arg_tys = arg_values.iter().map(|x| x.ty.clone()).collect();
let callconv = swapstack::compute_arguments(&arg_tys);
let mut gpr_args = vec![];
let mut fpr_args = vec![];
let mut stack_args = vec![];
for i in 0..callconv.len() {
let ref arg = arg_values[i];
let ref cc = callconv[i];
match cc {
&CallConvResult::GPR(_) => gpr_args.push(arg.clone()),
&CallConvResult::GPREX(_, _) => {
let (arg_l, arg_h) =
self.split_int128(arg, f_context, vm);
gpr_args.push(arg_l);
gpr_args.push(arg_h);
}
&CallConvResult::FPR(_) => fpr_args.push(arg.clone()),
&CallConvResult::STACK => stack_args.push(arg.clone()),
}
}
// for arguments that are not used, we push a 0
let zero = self.make_int64_const(0, vm);
let mut word_pushed = 0;
for i in 0..ARGUMENT_FPRS.len() {
let val = {
if i < fpr_args.len() {
&fpr_args[i]
} else {
&zero
}
};
word_pushed += 1;
self.emit_store_base_offset(
&new_sp,
-(word_pushed * WORD_SIZE as i32),
val,
vm,
);
}
for i in 0..ARGUMENT_GPRS.len() {
let val = {
if i < gpr_args.len() {
&gpr_args[i]
} else {
&zero
}
};
word_pushed += 1;
self.emit_store_base_offset(
&new_sp,
-(word_pushed * WORD_SIZE as i32),
val,
vm,
);
}
if !stack_args.is_empty() {
// need to put stack arguments to the preserved space
self.emit_store_stack_values(
&stack_args,
Some((&new_sp, 2 * WORD_SIZE as i32)),
MU_CALL_CONVENTION,
vm,
);
}
// adjust sp - we have pushed all argument registers
// (some could be 0 though)
self.backend
.emit_sub_r_imm(&new_sp, word_pushed * WORD_SIZE as i32);
// store the sp back to MuStack
self.emit_store_base_offset(
&stack,
*thread::MUSTACK_SP_OFFSET as i32,
&new_sp,
vm,
);
// call runtime entry
self.emit_runtime_entry(
&entrypoints::NEW_THREAD_NORMAL,
vec![attr, stack, tl],
Some(vec![res.clone()]),
Some(node),
f_content,
f_context,
vm,
);
}
}
}
Instruction_::CurrentStack => {
trace!("instsel on CURRENT_STACK");
......
......@@ -685,7 +685,7 @@ pub fn estimate_insts_for_ir(inst: &Instruction) -> usize {
// runtime call
New(_) | NewHybrid(_, _) => 10,
NewStack(_) | NewThread { .. } | NewFrameCursor(_) => 10,
NewStack(_) | NewThread { .. } | NewThreadRT { .. } | NewFrameCursor(_) => 10,
ThreadExit => 10,
CurrentStack => 10,
KillStack(_) => 10,
......
......@@ -262,6 +262,14 @@ impl BackendType {
| MuType_::FuncRef(_)
| MuType_::ThreadRef
| MuType_::StackRef => TypeEncode::short_noref(MINIMAL_ALIGNMENT, 1),
// RTMu-specific
#[cfg(feature = "realtime")]
MuType_::RTAttr => TypeEncode::short_noref(
MINIMAL_ALIGNMENT,
::runtime::thread::RTAttr::get_size() as u8,
),
// tag ref
MuType_::Tagref64 => TypeEncode::short_tagref(),
// floating point
......@@ -367,6 +375,11 @@ impl BackendType {
debug_assert!(pointer_aligned);
res.push(WordType::NonRef);
}
#[cfg(feature = "realtime")]
MuType_::RTAttr => {
debug_assert!(pointer_aligned);
res.push(WordType::NonRef);
}
MuType_::Tagref64 => {
debug_assert!(pointer_aligned);
res.push(WordType::TaggedRef);
......@@ -489,6 +502,17 @@ impl BackendType {
gc_type: None,
gc_type_hybrid_full: None,
},
// rtattr
#[cfg(feature = "realtime")]
MuType_::RTAttr => BackendType {
ty: ty.clone(),
size: std::mem::size_of::<::runtime::thread::RTAttr>(),
alignment: 8,
struct_layout: None,
elem_size: None,
gc_type: None,
gc_type_hybrid_full: None,
},
// tagref
MuType_::Tagref64 => BackendType {
ty: ty.clone(),
......
......@@ -79,6 +79,7 @@ fn is_suitable_child(inst: &Instruction) -> bool {
| AllocAHybrid(_, _)
| NewStack(_)
| NewThread { .. }
| NewThreadRT { .. }
| NewFrameCursor(_)
| Select { .. }
| Fence(_)
......
......@@ -15,7 +15,7 @@
[package]
name = "mu_gc"
version = "0.0.1"
authors = ["qinsoon <qinsoon@gmail.com>"]
authors = ["qinsoon <qinsoon@gmail.com>", "Javad Amiri <javad.amiri@anu.edu.au>"]
build = "build.rs"
[lib]
......@@ -23,6 +23,7 @@ crate-type = ["rlib"]
[features]
default = []
realtime = []
[build-dependencies]
cc = "*"
......
......@@ -66,6 +66,7 @@ impl Allocator for ImmixAllocator {
#[inline(always)]
fn alloc(&mut self, size: usize, align: usize) -> Address {
trace!("immix_mutator::alloc(self, {}, {});", size, align);
// this part of code will slow down allocation
let align = objectmodel::check_alignment(align);
// end
......@@ -126,6 +127,7 @@ impl ImmixAllocator {
#[inline(never)]
pub fn alloc_slow(&mut self, size: usize, align: usize) -> Address {
trace!("immix_mutator::alloc_slow(self, {}, {});", size, align);
if size > BYTES_IN_LINE {
trace_if!(TRACE_ALLOC, "Mutator: overflow alloc()");
self.overflow_alloc(size, align)
......@@ -148,6 +150,7 @@ impl ImmixAllocator {
}
pub fn overflow_alloc(&mut self, size: usize, align: usize) -> Address {
trace!("immix_mutator::overflow_alloc(self, {}, {});", size, align);
let start = self.large_cursor.align_up(align);
let end = start + size;
......@@ -175,6 +178,11 @@ impl ImmixAllocator {
}
pub fn try_alloc_from_local(&mut self, size: usize, align: usize) -> Address {
trace!(
"immix_mutator::try_alloc_from_local(self, {}, {});",
size,
align
);
if self.line < LINES_IN_BLOCK {
let opt_next_available_line = {
let cur_line = self.line;
......@@ -216,6 +224,12 @@ impl ImmixAllocator {
}
fn alloc_from_global(&mut self, size: usize, align: usize, request_large: bool) -> Address {
trace!(
"immix_mutator::alloc_from_global(self, {}, {}, {});",
size,
align,
request_large
);
trace!("Mutator: slowpath: alloc_from_global()");
self.return_block(request_large);
......@@ -264,6 +278,7 @@ impl ImmixAllocator {
}
fn return_block(&mut self, request_large: bool) {
trace!("immix_mutator::return_block(self, {});", request_large);
if request_large {
if self.large_block.is_some() {
trace!(
......
......@@ -232,6 +232,7 @@ impl MutatorGlobal {
}
#[inline(always)]
pub fn take_yield(&self) -> bool {
trace!("heap::mod::take_yield");
self.take_yield.load(Ordering::SeqCst)
}
}
......@@ -353,6 +353,7 @@ pub extern "C" fn muentry_alloc_tiny_slow(
size: usize,
align: usize,
) -> Address {
trace!("gc::src::lib::muentry_alloc_tiny_slow");
let m = mutator_ref(mutator);
m.tiny.alloc_slow(size, align)
}
......@@ -365,6 +366,7 @@ pub extern "C" fn muentry_alloc_normal_slow(
size: usize,
align: usize,
) -> Address {
trace!("gc::src::lib::muentry_alloc_normal_slow");
let m = mutator_ref(mutator);
let res = m.normal.alloc_slow(size, align);
m.normal.post_alloc(res, size);
......
......@@ -36,6 +36,8 @@ pub extern crate mu_ast as ast;
#[macro_use]
pub extern crate mu_utils as utils;
pub extern crate mu_gc as gc;
extern crate proc_macro;
pub mod compiler;
pub mod linkutils;
pub mod runtime;
......
......@@ -167,9 +167,9 @@ fn link_executable_internal(
cc.arg(format!(
"-L{}",
get_path_under_zebu(if cfg!(debug_assertions) {
"./target/debug/deps"
"target/debug/deps/"
} else {
"./target/release/deps"
"target/release/deps/"
})
.to_str()
.unwrap()
......@@ -179,6 +179,8 @@ fn link_executable_internal(
// dylibs used for linux
if cfg!(target_os = "linux") {
cc.arg("-lpthread");
cc.arg("-ldl");
cc.arg("-lm");
} else if cfg!(target_os = "macos") {
// TODO macos args need to be updated
cc.arg("-liconv");
......
......@@ -49,11 +49,15 @@ fn get_path_under_zebu(str: &'static str) -> PathBuf {
match env::var("MU_ZEBU") {
Ok(v) => {
let mut ret = PathBuf::from(v);
trace!("MU_ZEBU is set to: -{}-", v);
let mut ret = PathBuf::from(v.clone());
ret.push(str);
ret
}
Err(_) => PathBuf::from(str),
Err(e) => {
trace!("MU_ZEBU error: -{}-", e);
PathBuf::from(str)
}
}
}
......
This diff is collapsed.
......@@ -43,7 +43,8 @@ impl RuntimeEntrypoint {
}
}
// decl: _thread.rs
// decl: thread/mod.rs
#[cfg(not(feature = "realtime"))]
lazy_static! {
// impl: runtime_ARCH_OS.c
pub static ref GET_THREAD_LOCAL : RuntimeEntrypoint = RuntimeEntrypoint::new(
......@@ -59,7 +60,7 @@ lazy_static! {
vec![ADDRESS_TYPE.clone()],
vec![]);
// impl: _thread.rs
// impl: thread/mod.rs
pub static ref NEW_STACK: RuntimeEntrypoint = RuntimeEntrypoint::new(
"muentry_new_stack",
vec![ADDRESS_TYPE.clone(), ADDRESS_TYPE.clone()],
......@@ -82,6 +83,46 @@ lazy_static! {
vec![THREADREF_TYPE.clone()]);
}
// decl: thread/mod.rs
#[cfg(feature = "realtime")]
lazy_static! {
// impl: runtime_ARCH_OS.c
pub static ref GET_THREAD_LOCAL : RuntimeEntrypoint = RuntimeEntrypoint::new(
"muentry_get_thread_local",
vec![],
vec![ADDRESS_TYPE.clone()]);
pub static ref SET_RETVAL : RuntimeEntrypoint = RuntimeEntrypoint::new(
"muentry_set_retval",
vec![UINT32_TYPE.clone()],