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 b7c2a4f7 authored by qinsoon's avatar qinsoon
Browse files

inject fastpath as Mu IR

parent 22c3a1b0
......@@ -108,6 +108,7 @@ impl Instruction {
Move(_) |
PrintHex(_) |
SetRetval(_) |
GetVMThreadLocal |
KillStack(_) |
CurrentStack |
SwapStackExpr { .. } => false
......@@ -185,7 +186,8 @@ impl Instruction {
CommonInst_Tr64ToRef(_) |
CommonInst_Tr64ToTag(_) |
Move(_) |
CurrentStack => false
CurrentStack |
GetVMThreadLocal => false
}
}
......@@ -250,6 +252,7 @@ impl Instruction {
Move(_) |
PrintHex(_) |
SetRetval(_) |
GetVMThreadLocal |
KillStack(_) |
CurrentStack |
SwapStackExpr { .. } |
......@@ -322,6 +325,7 @@ impl Instruction {
Move(_) |
PrintHex(_) |
SetRetval(_) |
GetVMThreadLocal |
KillStack(_) |
CurrentStack |
SwapStackExpr { .. } |
......@@ -395,6 +399,7 @@ impl Instruction {
Move(_) |
PrintHex(_) |
SetRetval(_) |
GetVMThreadLocal |
KillStack(_) |
CurrentStack |
SwapStackKill { .. } => false,
......@@ -820,7 +825,9 @@ impl Instruction {
// print hex
&Instruction_::PrintHex(i) => format!("PRINTHEX<{}> {}", ops[i].ty(), ops[i]),
// set retval
&Instruction_::SetRetval(val) => format!("SETRETVAL {}", ops[val])
&Instruction_::SetRetval(val) => format!("SETRETVAL {}", ops[val]),
// get vm thread local
&Instruction_::GetVMThreadLocal => format!("GETVMTHREADLOCAL")
}
}
}
......@@ -1118,7 +1125,9 @@ pub enum Instruction_ {
/// internal use: print op as hex value
PrintHex(OpIndex),
/// internal use: set return value for main
SetRetval(OpIndex)
SetRetval(OpIndex),
/// internal use: get zebu thread local
GetVMThreadLocal
}
fn format_value_types(value: &Option<Vec<P<Value>>>) -> String {
......
......@@ -566,6 +566,14 @@ impl Block {
}
}
pub fn clear_insts(&mut self) {
self.content.as_mut().unwrap().body.clear();
}
pub fn append_inst(&mut self, inst: P<TreeNode>) {
self.content.as_mut().unwrap().body.push(inst);
}
/// does this block have an exception arguments?
pub fn is_receiving_exception_arg(&self) -> bool {
return self.content.as_ref().unwrap().exn_arg.is_some();
......@@ -859,6 +867,30 @@ impl TreeNode {
})
}
/// is instruction
pub fn is_inst(&self) -> bool {
match self.v {
TreeNode_::Instruction(_) => true,
_ => false
}
}
/// is value
pub fn is_value(&self) -> bool {
match self.v {
TreeNode_::Value(_) => true,
_ => false
}
}
/// is constant value
pub fn is_const_value(&self) -> bool {
match self.v {
TreeNode_::Value(ref val) => val.is_const(),
_ => false
}
}
/// extracts the MuID of an SSA TreeNode
/// if the node is not an SSA, returns None
pub fn extract_ssa_id(&self) -> Option<MuID> {
......@@ -975,10 +1007,14 @@ rodal_struct!(Value { hdr, ty, v });
impl Value {
/// creates an int constant value
pub fn make_int_const(id: MuID, val: u64) -> P<Value> {
pub fn make_int32_const(id: MuID, val: u64) -> P<Value> {
Value::make_int_const_ty(id, UINT32_TYPE.clone(), val)
}
pub fn make_int64_const(id: MuID, val: u64) -> P<Value> {
Value::make_int_const_ty(id, UINT64_TYPE.clone(), val)
}
pub fn make_int_const_ty(id: MuID, ty: P<MuType>, val: u64) -> P<Value> {
P(Value {
hdr: MuEntityHeader::unnamed(id),
......
......@@ -75,6 +75,14 @@ lazy_static! {
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 STACKREF_TYPE : P<MuType> = P(
MuType::new(new_internal_id(), MuType_::StackRef)
);
......@@ -99,6 +107,8 @@ lazy_static! {
IREF_VOID_TYPE.clone(),
STACKREF_TYPE.clone(),
THREADREF_TYPE.clone(),
UPTR_U8_TYPE.clone(),
UPTR_U64_TYPE.clone()
];
}
......
......@@ -1604,6 +1604,12 @@ impl<'a> InstructionSelection {
);
}
Instruction_::GetVMThreadLocal => {
trace!("instsel on GETVMTHREADLOCAL");
let tl = self.emit_get_threadlocal(Some(node), f_content, f_context, vm);
let tmp_res = self.get_result_value(node);
self.backend.emit_mov_r_r(&tmp_res, &tl);
}
Instruction_::CommonInst_GetThreadLocal => {
trace!("instsel on GETTHREADLOCAL");
// get thread local
......@@ -3493,7 +3499,7 @@ impl<'a> InstructionSelection {
f_context: &mut FunctionContext,
vm: &VM
) -> P<Value> {
let size = math::align_up(size + OBJECT_HEADER_SIZE, POINTER_SIZE);
let size = math::align_up(mm::check_size(size), POINTER_SIZE);
let encode = mm::gen_object_encode(backend_ty, size, vm);
let tmp_res = self.get_result_value(node);
......
......@@ -218,7 +218,7 @@ impl FrameSlot {
ty: ty.clone(),
v: Value_::Memory(MemoryLocation::Address {
base: x86_64::RBP.clone(),
offset: Some(Value::make_int_const(vm.next_id(), self.offset as u64)),
offset: Some(Value::make_int32_const(vm.next_id(), self.offset as u64)),
index: None,
scale: None
})
......@@ -234,7 +234,7 @@ impl FrameSlot {
ty: ty.clone(),
v: Value_::Memory(MemoryLocation::VirtualAddress {
base: aarch64::FP.clone(),
offset: Some(Value::make_int_const(vm.next_id(), self.offset as u64)),
offset: Some(Value::make_int32_const(vm.next_id(), self.offset as u64)),
scale: 1,
signed: true
})
......
......@@ -104,6 +104,7 @@ impl Default for CompilerPolicy {
// ir level passes
passes.push(Box::new(passes::RetSink::new()));
passes.push(Box::new(passes::Inlining::new()));
passes.push(Box::new(passes::InjectRuntime::new()));
passes.push(Box::new(passes::DefUse::new()));
passes.push(Box::new(passes::TreeGen::new()));
passes.push(Box::new(passes::GenMovPhi::new()));
......
// Copyright 2017 The Australian National University
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use ast::ir::*;
use ast::ptr::*;
use ast::inst::*;
use ast::op::*;
use ast::types::*;
use vm::VM;
use compiler::CompilerPass;
use runtime::mm;
use runtime::mm::*;
use runtime::entrypoints;
use runtime::thread;
use utils::*;
use utils::math;
use std::any::Any;
pub struct InjectRuntime {
name: &'static str
}
impl InjectRuntime {
pub fn new() -> InjectRuntime {
InjectRuntime {
name: "Inject Runtime Code"
}
}
}
impl CompilerPass for InjectRuntime {
fn name(&self) -> &'static str {
self.name
}
fn as_any(&self) -> &Any {
self
}
fn finish_function(&mut self, vm: &VM, func: &mut MuFunctionVersion) {
debug!("after inject runtime: ");
debug!("{:?}", func);
}
fn visit_function(&mut self, vm: &VM, func: &mut MuFunctionVersion) {
// make a clone of the blocks
let blocks = func.content.as_mut().unwrap().blocks.clone();
let func_context = &mut func.context;
let mut new_blocks = vec![];
for (_, mut block) in blocks.into_iter() {
// get all the instructions of this block, so we can iterate through them
let body_copy = block.content.as_ref().unwrap().body.clone();
// set cur block as current block
// we may change cur block to some newly generated block, so cur block is mutable
let mut cur_block = block;
// clear the body of current block
cur_block.clear_insts();
for node in body_copy {
let inst: &Instruction = node.as_inst();
trace!("check instruction: {:?}", inst);
match inst.v {
Instruction_::New(ref ty) => {
let ty_info = vm.get_backend_type_info(ty.id());
let size = math::align_up(mm::check_size(ty_info.size), POINTER_SIZE);
let align = mm::check_alignment(ty_info.alignment);
if size <= MAX_MEDIUM_OBJECT {
let block_after = gen_allocation_sequence(
size,
align,
&node,
&mut cur_block,
&mut new_blocks,
func_context,
vm
);
// alloc_end as cur_block
new_blocks.push(cur_block);
cur_block = block_after;
} else {
// large object allocation - keep the NEW inst
cur_block.append_inst(node.clone());
}
}
Instruction_::NewHybrid(ref ty, len_index)
if inst.ops[len_index].is_const_value() => {
let len = inst.ops[len_index].as_value().extract_int_const().unwrap();
let ty_info = vm.get_backend_type_info(ty.id());
let size = ty_info.size + ty_info.elem_size.unwrap() * (len as usize);
let size = math::align_up(mm::check_hybrid_size(size), POINTER_SIZE);
let align = mm::check_alignment(ty_info.alignment);
if size <= MAX_MEDIUM_OBJECT {
let block_after = gen_allocation_sequence(
size,
align,
&node,
&mut cur_block,
&mut new_blocks,
func_context,
vm
);
// alloc_end as cur_block
new_blocks.push(cur_block);
cur_block = block_after;
} else {
// large object allocation - keep the NEW inst
cur_block.append_inst(node.clone());
}
}
_ => {
cur_block.append_inst(node.clone());
}
}
}
new_blocks.push(cur_block);
}
// insert new blocks to the function
let f_content = func.content.as_mut().unwrap();
f_content.blocks.clear();
for block in new_blocks.drain(..) {
f_content.blocks.insert(block.id(), block);
}
}
}
// returns new block after the allocation
fn gen_allocation_sequence(
size: ByteSize,
align: ByteSize,
node: &P<TreeNode>,
cur_block: &mut Block,
new_blocks: &mut Vec<Block>,
func_context: &mut FunctionContext,
vm: &VM
) -> Block {
use runtime::mm::heap::immix::*;
// are we allocating tiny object? otherwise allocate small/medium into normal
// immix space
let is_alloc_tiny = size <= MAX_TINY_OBJECT;
// tl = GETVMTHREADLOCAL
let tmp_tl = func_context.make_temporary(vm.next_id(), UPTR_U8_TYPE.clone());
cur_block.append_inst(TreeNode::new_inst(Instruction {
hdr: MuEntityHeader::unnamed(vm.next_id()),
value: Some(vec![tmp_tl.clone_value()]),
ops: vec![],
v: Instruction_::GetVMThreadLocal
}));
// cursor_loc = SHIFTIREF tl CURSOR_OFFSET
let cursor_offset = if is_alloc_tiny {
*thread::ALLOCATOR_OFFSET + *TINY_ALLOCATOR_OFFSET + *CURSOR_OFFSET
} else if size <= MAX_MEDIUM_OBJECT {
*thread::ALLOCATOR_OFFSET + *NORMAL_ALLOCATOR_OFFSET + *CURSOR_OFFSET
} else {
unreachable!()
};
let tmp_cursor_loc = func_context.make_temporary(vm.next_id(), UPTR_U8_TYPE.clone());
cur_block.append_inst(TreeNode::new_inst(Instruction {
hdr: MuEntityHeader::unnamed(vm.next_id()),
value: Some(vec![tmp_cursor_loc.clone_value()]),
ops: vec![
tmp_tl.clone(),
TreeNode::new_value(Value::make_int64_const(vm.next_id(), cursor_offset as u64)),
],
v: Instruction_::ShiftIRef {
is_ptr: true,
base: 0,
offset: 1
}
}));
// cursor_loc_u64 = PTRCAST cursor_loc
let tmp_cursor_loc_u64 = func_context.make_temporary(vm.next_id(), UPTR_U64_TYPE.clone());
cur_block.append_inst(TreeNode::new_inst(Instruction {
hdr: MuEntityHeader::unnamed(vm.next_id()),
value: Some(vec![tmp_cursor_loc_u64.clone_value()]),
ops: vec![tmp_cursor_loc],
v: Instruction_::ConvOp {
operation: ConvOp::PTRCAST,
from_ty: UPTR_U8_TYPE.clone(),
to_ty: UPTR_U64_TYPE.clone(),
operand: 0
}
}));
// cursor = LOAD cursor_loc_u64
let tmp_cursor = func_context.make_temporary(vm.next_id(), UINT64_TYPE.clone());
cur_block.append_inst(TreeNode::new_inst(Instruction {
hdr: MuEntityHeader::unnamed(vm.next_id()),
value: Some(vec![tmp_cursor.clone_value()]),
ops: vec![tmp_cursor_loc_u64.clone()],
v: Instruction_::Load {
is_ptr: true,
order: MemoryOrder::SeqCst,
mem_loc: 0
}
}));
// align up the cursor: (cursor + align - 1) & !(align - 1)
// cursor_t1 = cursor + (align - 1)
let tmp_cursor_t1 = func_context.make_temporary(vm.next_id(), UINT64_TYPE.clone());
let tmp_align_minus_one =
TreeNode::new_value(Value::make_int64_const(vm.next_id(), (align - 1) as u64));
cur_block.append_inst(TreeNode::new_inst(Instruction {
hdr: MuEntityHeader::unnamed(vm.next_id()),
value: Some(vec![tmp_cursor_t1.clone_value()]),
ops: vec![tmp_cursor.clone(), tmp_align_minus_one.clone()],
v: Instruction_::BinOp(BinOp::Add, 0, 1)
}));
// start = cursor_t1 & !(align - 1)
let tmp_start = func_context.make_temporary(vm.next_id(), UINT64_TYPE.clone());
let tmp_not_align_minus_one =
TreeNode::new_value(Value::make_int64_const(vm.next_id(), !(align - 1) as u64));
cur_block.append_inst(TreeNode::new_inst(Instruction {
hdr: MuEntityHeader::unnamed(vm.next_id()),
value: Some(vec![tmp_start.clone_value()]),
ops: vec![tmp_cursor_t1.clone(), tmp_not_align_minus_one],
v: Instruction_::BinOp(BinOp::And, 0, 1)
}));
// end = start + size
let tmp_end = func_context.make_temporary(vm.next_id(), UINT64_TYPE.clone());
let tmp_size = TreeNode::new_value(Value::make_int64_const(vm.next_id(), size as u64));
cur_block.append_inst(TreeNode::new_inst(Instruction {
hdr: MuEntityHeader::unnamed(vm.next_id()),
value: Some(vec![tmp_end.clone_value()]),
ops: vec![tmp_start.clone(), tmp_size.clone()],
v: Instruction_::BinOp(BinOp::Add, 0, 1)
}));
// limit_loc = SHIFTIREF tl LIMIT_OFFSET
let limit_offset = if is_alloc_tiny {
*thread::ALLOCATOR_OFFSET + *TINY_ALLOCATOR_OFFSET + *LIMIT_OFFSET
} else if size <= MAX_MEDIUM_OBJECT {
*thread::ALLOCATOR_OFFSET + *NORMAL_ALLOCATOR_OFFSET + *LIMIT_OFFSET
} else {
unreachable!()
};
let tmp_limit_loc = func_context.make_temporary(vm.next_id(), UPTR_U8_TYPE.clone());
cur_block.append_inst(TreeNode::new_inst(Instruction {
hdr: MuEntityHeader::unnamed(vm.next_id()),
value: Some(vec![tmp_limit_loc.clone_value()]),
ops: vec![
tmp_tl.clone(),
TreeNode::new_value(Value::make_int64_const(vm.next_id(), limit_offset as u64)),
],
v: Instruction_::ShiftIRef {
is_ptr: true,
base: 0,
offset: 1
}
}));
// limit_loc_u64 = PTRCAST limit_loc
let tmp_limit_loc_u64 = func_context.make_temporary(vm.next_id(), UPTR_U64_TYPE.clone());
cur_block.append_inst(TreeNode::new_inst(Instruction {
hdr: MuEntityHeader::unnamed(vm.next_id()),
value: Some(vec![tmp_limit_loc_u64.clone_value()]),
ops: vec![tmp_limit_loc],
v: Instruction_::ConvOp {
operation: ConvOp::PTRCAST,
from_ty: UPTR_U8_TYPE.clone(),
to_ty: UPTR_U64_TYPE.clone(),
operand: 0
}
}));
// limit = LOAD limit_loc_u64
let tmp_limit = func_context.make_temporary(vm.next_id(), UINT64_TYPE.clone());
cur_block.append_inst(TreeNode::new_inst(Instruction {
hdr: MuEntityHeader::unnamed(vm.next_id()),
value: Some(vec![tmp_limit.clone_value()]),
ops: vec![tmp_limit_loc_u64.clone()],
v: Instruction_::Load {
is_ptr: true,
order: MemoryOrder::SeqCst,
mem_loc: 0
}
}));
// exceed_limit = UGT tmp_end tmp_limit
let tmp_exceed_limit = func_context.make_temporary(vm.next_id(), UINT1_TYPE.clone());
cur_block.append_inst(TreeNode::new_inst(Instruction {
hdr: MuEntityHeader::unnamed(vm.next_id()),
value: Some(vec![tmp_exceed_limit.clone_value()]),
ops: vec![tmp_end.clone(), tmp_limit.clone()],
v: Instruction_::CmpOp(CmpOp::UGT, 0, 1)
}));
// alloc_end
let alloc_end = {
let block_name = Arc::new(format!("new:{}:end", node.id()));
let mut block = Block::new(MuEntityHeader::named(vm.next_id(), block_name));
block.trace_hint = TraceHint::None;
block.content = Some(BlockContent {
args: vec![],
exn_arg: None,
body: vec![],
keepalives: None
});
block
};
// result of the allocation
let tmp_res = node.as_value().clone();
// fastpath and slowpath - they both jumps to alloc_end
let fastpath = {
let block_name = Arc::new(format!("new:{}:fastpath", node.id()));
let mut block = Block::new(MuEntityHeader::named(vm.next_id(), block_name));
block.trace_hint = TraceHint::FastPath;
block.content = Some(BlockContent {
args: vec![],
exn_arg: None,
body: vec![
// STORE end cursor_loc_u64
TreeNode::new_inst(Instruction {
hdr: MuEntityHeader::unnamed(vm.next_id()),
value: None,
ops: vec![tmp_cursor_loc_u64.clone(), tmp_end.clone()],
v: Instruction_::Store {
is_ptr: true,
order: MemoryOrder::SeqCst,
mem_loc: 0,
value: 1
}
}),
// MOVE tmp_start -> tmp_res
TreeNode::new_inst(Instruction {
hdr: MuEntityHeader::unnamed(vm.next_id()),
value: Some(vec![tmp_res.clone()]),
ops: vec![tmp_start.clone()],
v: Instruction_::Move(0)
}),
// BRANCH alloc_end
TreeNode::new_inst(Instruction {
hdr: MuEntityHeader::unnamed(vm.next_id()),
value: None,
ops: vec![],
v: Instruction_::Branch1(Destination {
target: alloc_end.hdr.clone(),
args: vec![]
})
}),
],
keepalives: None
});
block
};
let slowpath = {
let block_name = Arc::new(format!("new:{}:slowpath", node.id()));
let mut block = Block::new(MuEntityHeader::named(vm.next_id(), block_name));
block.trace_hint = TraceHint::SlowPath;
block.content = Some(BlockContent {
args: vec![],
exn_arg: None,
body: {
let mutator_offset = *thread::ALLOCATOR_OFFSET;
let tmp_mutator_loc =
func_context.make_temporary(vm.next_id(), UPTR_U8_TYPE.clone());
let tmp_align =
TreeNode::new_value(Value::make_int64_const(vm.next_id(), align as u64));
let func: &entrypoints::RuntimeEntrypoint = if is_alloc_tiny {
&entrypoints::ALLOC_TINY_SLOW
} else {
&entrypoints::ALLOC_NORMAL_SLOW
};
let tmp_alloc_slow = TreeNode::new_value(P(Value {
hdr: MuEntityHeader::unnamed(vm.next_id()),
ty: P(MuType::new(
vm.next_id(),
MuType_::UFuncPtr(func.sig.clone())
)),
v: Value_::Constant(Constant::ExternSym(func.aot.to_relocatable()))
}));
vec![