...
 
Commits (7)
......@@ -22,7 +22,7 @@ use utils::vec_utils;
use std::fmt;
/// Instruction represents a Mu instruction
#[derive(Debug)] // this implements Clone and Display
#[derive(Debug, Clone)] // this implements Display
pub struct Instruction {
pub hdr: MuEntityHeader,
/// the values this instruction holds
......@@ -38,17 +38,6 @@ pub struct Instruction {
// Instruction implements MuEntity
impl_mu_entity!(Instruction);
impl Clone for Instruction {
fn clone(&self) -> Self {
Instruction {
hdr: self.hdr.clone(),
value: self.value.clone(),
ops: self.ops.clone(),
v: self.v.clone()
}
}
}
impl Instruction {
pub fn clone_with_id(&self, new_id: MuID) -> Instruction {
let mut clone = self.clone();
......
......@@ -298,8 +298,8 @@ impl MuFunctionVersion {
})
}
pub fn new_inst(&mut self, v: Instruction) -> Box<TreeNode> {
Box::new(TreeNode {
pub fn new_inst(&mut self, v: Instruction) -> P<TreeNode> {
P(TreeNode {
v: TreeNode_::Instruction(v)
})
}
......@@ -714,7 +714,7 @@ pub enum EdgeKind {
pub struct BlockContent {
pub args: Vec<P<Value>>,
pub exn_arg: Option<P<Value>>,
pub body: Vec<Box<TreeNode>>,
pub body: Vec<P<TreeNode>>,
pub keepalives: Option<Vec<P<Value>>>
}
......@@ -852,13 +852,6 @@ impl TreeNode {
})
}
/// creates an owned Instruction TreeNode
pub fn new_boxed_inst(v: Instruction) -> Box<TreeNode> {
Box::new(TreeNode {
v: TreeNode_::Instruction(v)
})
}
/// creates a sharable Value TreeNode
pub fn new_value(v: P<Value>) -> P<TreeNode> {
P(TreeNode {
......@@ -923,7 +916,7 @@ impl TreeNode {
}
/// consumes the TreeNode, returns the instruction in it (or None if it is not an instruction)
pub fn as_inst_ref(&self) -> &Instruction {
pub fn as_inst(&self) -> &Instruction {
match &self.v {
&TreeNode_::Instruction(ref inst) => inst,
_ => panic!("expected inst")
......@@ -1171,7 +1164,10 @@ pub struct SSAVarEntry {
expr: Option<Instruction>,
// some ssa vars (such as int128) needs to be split into smaller vars
split: Option<Vec<P<Value>>>
split: Option<Vec<P<Value>>>,
// which instruction defines this value
def: Option<P<TreeNode>>
}
impl SSAVarEntry {
......@@ -1180,7 +1176,8 @@ impl SSAVarEntry {
val: val,
use_count: ATOMIC_USIZE_INIT,
expr: None,
split: None
split: None,
def: None
};
ret.use_count.store(0, Ordering::SeqCst);
......@@ -1226,6 +1223,16 @@ impl SSAVarEntry {
pub fn get_split(&self) -> &Option<Vec<P<Value>>> {
&self.split
}
pub fn has_def(&self) -> bool {
self.def.is_some()
}
pub fn set_def(&mut self, d: P<TreeNode>) {
self.def = Some(d);
}
pub fn get_def(&self) -> &Option<P<TreeNode>> {
&self.def
}
}
impl fmt::Display for SSAVarEntry {
......
......@@ -194,6 +194,15 @@ impl MuType {
}
}
/// is this type an integer type of certain width
pub fn is_int_n(&self, n: usize) -> bool {
if let Some(width) = self.get_int_length() {
width == n
} else {
false
}
}
/// is this type a floating point type? (float/double)
pub fn is_fp(&self) -> bool {
match self.v {
......
......@@ -123,6 +123,20 @@ lazy_static! {
});
}
macro_rules! match_inst {
($op: expr, $inst_pat: pat) => {
match $op.v {
TreeNode_::Value(_) => false,
TreeNode_::Instruction(ref inst) => {
match inst.v {
$inst_pat => true,
_ => false
}
}
}
};
}
/// for some IR instructions, we need a call into runtime
/// for efficiency, we may emit runtime fastpath directly in assembly
// FIXME: we should have a separate pass to rewrite the instruction into a fastpath (in IR),
......@@ -234,7 +248,57 @@ impl<'a> InstructionSelection {
let branch_target = f_content.get_block(branch_dest.target.id()).name();
let ref cond = ops[cond];
if self.match_cmp_res(cond) {
// if branch2 based on a comparison, and both operands for comparison are
// constants, we can reduce this branch2 to a branch
// FIXME: we should do a proper contant propagation to remove this pattern
// or the client should generate better code
if match_inst!(cond, Instruction_::CmpOp { .. }) &&
{
let cmp_inst = cond.as_inst();
let (cmp_op1, cmp_op2) = match cmp_inst.v {
Instruction_::CmpOp(_, op1, op2) => (
&cmp_inst.ops[op1],
&cmp_inst.ops[op2]
),
_ => unreachable!()
};
self.match_iimm(cmp_op1) && self.match_iimm(cmp_op2)
} {
let cmp_inst = cond.as_inst();
let (cmp_op, cmp_op1, cmp_op2) = match cmp_inst.v {
Instruction_::CmpOp(op, op1, op2) => (
op,
&cmp_inst.ops[op1],
&cmp_inst.ops[op2]
),
_ => unreachable!()
};
let c1 = self.node_iimm_to_i32(cmp_op1);
let c1u = c1 as u32;
let c2 = self.node_iimm_to_i32(cmp_op2);
let c2u = c2 as u32;
let is_true = match cmp_op {
CmpOp::EQ => c1 == c2,
CmpOp::NE => c1 != c2,
CmpOp::SGE => c1 >= c2,
CmpOp::SGT => c1 > c2,
CmpOp::SLE => c1 <= c2,
CmpOp::SLT => c1 < c2,
CmpOp::UGE => c1u >= c2u,
CmpOp::UGT => c1u > c2u,
CmpOp::ULE => c1u <= c2u,
CmpOp::ULT => c1u < c2u,
_ => unreachable!()
};
if is_true {
self.backend
.emit_jmp(f_content.get_block(true_dest.target.id()).name());
} else {
self.backend
.emit_jmp(f_content.get_block(false_dest.target.id()).name());
}
} else if self.match_cmp_res(cond) {
// this branch2's cond is from a comparison result
trace!("emit cmp_res-branch2");
match self.emit_cmp_res(cond, f_content, f_context, vm) {
......@@ -5105,7 +5169,29 @@ impl<'a> InstructionSelection {
let op2 = &ops[op2];
if op.is_int_cmp() {
if self.match_iimm(op1) && self.match_iimm(op2) {
// trying to match a pattern of
// EQ (ZEXT (CMP x y)) 1
if op == CmpOp::EQ &&
match_inst!(op1,
Instruction_::ConvOp { operation: ConvOp::ZEXT, .. }) &&
{
let inst_op1 = op1.as_inst();
let child: &P<TreeNode> = match inst_op1.v {
Instruction_::ConvOp { operand, .. } => {
&inst_op1.ops[operand]
}
_ => unreachable!()
};
match_inst!(child, Instruction_::CmpOp { .. })
} && self.match_iconst_one(op2)
{
let inst_op1 = op1.as_inst();
let child = match inst_op1.v {
Instruction_::ConvOp { operand, .. } => &inst_op1.ops[operand],
_ => unreachable!()
};
self.emit_cmp_res(child, f_content, f_context, vm)
} else if self.match_iimm(op1) && self.match_iimm(op2) {
// comparing two immediate numbers
let ty = op1.as_value().ty.clone();
......
......@@ -217,7 +217,6 @@ fn compute_immediate_dominators(dominators: &LinkedMultiMap<MuName, MuName>)
}
}
assert_eq!(immediate_doms.len(), dominators.len() - 1); // entry block does not have idom
immediate_doms
}
......
......@@ -21,7 +21,6 @@ use compiler::backend::reg_alloc::graph_coloring;
use compiler::backend::reg_alloc::graph_coloring::liveness::InterferenceGraph;
use compiler::machine_code::CompiledFunction;
use vm::VM;
use utils::vec_utils;
use utils::LinkedHashSet;
use utils::LinkedHashMap;
use compiler::backend::reg_alloc::graph_coloring::liveness::Move;
......@@ -171,20 +170,6 @@ impl<'a> GraphColoring<'a> {
coloring.regalloc()
}
/// returns formatted string for an ID
fn display_id(&self, id: MuID) -> String {
self.func.context.get_temp_display(id)
}
/// returns formatted string for a move
fn display_move(&self, m: Move) -> String {
format!(
"Move: {} -> {}",
self.display_id(m.from),
self.display_id(m.to)
)
}
/// does coloring register allocation
fn regalloc(mut self) -> GraphColoring<'a> {
trace!("---InterenceGraph---");
......
......@@ -15,7 +15,6 @@
use ast::ir::*;
use ast::ptr::*;
use vm::VM;
use compiler::CompilerPass;
use std::any::Any;
......@@ -41,26 +40,36 @@ impl CompilerPass for DefUse {
}
#[allow(unused_variables)]
fn start_block(&mut self, vm: &VM, func_context: &mut FunctionContext, block: &mut Block) {
fn start_block(&mut self, vm: &VM, func_context: &mut FunctionContext, block: &mut Block) {}
#[allow(unused_variables)]
fn finish_block(&mut self, vm: &VM, func_context: &mut FunctionContext, block: &mut Block) {
// if an SSA appears in keepalives, its use count increases
let ref mut keepalives = block.content.as_mut().unwrap().keepalives;
if keepalives.is_some() {
for op in keepalives.as_mut().unwrap().iter_mut() {
let ref keepalives = block.content.as_ref().unwrap().keepalives;
if let &Some(ref keepalives) = keepalives {
for op in keepalives.iter() {
use_value(op, func_context);
}
}
}
#[allow(unused_variables)]
fn visit_inst(&mut self, vm: &VM, func_context: &mut FunctionContext, node: &TreeNode) {
// if an SSA appears in operands of instrs, its use count increases
match node.v {
TreeNode_::Instruction(ref inst) => {
for op in inst.ops.iter() {
use_op(op, func_context);
}
fn visit_inst(&mut self, vm: &VM, func_context: &mut FunctionContext, node: &P<TreeNode>) {
let inst = node.as_inst();
if let &Some(ref vals) = &inst.value {
for val in vals {
let id = val.extract_ssa_id().unwrap();
func_context
.get_value_mut(id)
.unwrap()
.set_def(node.clone());
}
_ => panic!("expected instruction node in visit_inst()")
}
// if an SSA appears in operands of instrs, its use count increases
for op in inst.ops.iter() {
use_op(op, func_context);
}
}
......
......@@ -126,7 +126,7 @@ fn emit_muir_dot_inner(file: &mut File, f_name: MuName, f_content: &FunctionCont
write!(
file,
" {}\\l",
escape_string(format!("{}", inst.as_inst_ref()))
escape_string(format!("{}", inst.as_inst()))
).unwrap();
}
......
......@@ -77,14 +77,14 @@ impl CompilerPass for GenMovPhi {
trace!("last instruction is {}", node);
let last_inst = node.clone();
match last_inst.v {
TreeNode_::Instruction(inst) => {
TreeNode_::Instruction(ref inst) => {
let ref ops = inst.ops;
let inst_name = inst.name().clone();
match inst.v {
Instruction_::Branch2 {
cond,
true_dest,
false_dest,
ref true_dest,
ref false_dest,
true_prob
} => {
// check and insert intermediate blocks for true/false dest
......@@ -121,9 +121,12 @@ impl CompilerPass for GenMovPhi {
trace!("rewrite to {}", new_inst);
new_body.push(new_inst);
}
Instruction_::Call { data, resume } => {
Instruction_::Call {
ref data,
ref resume
} => {
let norm_dest = process_dest(
resume.normal_dest,
&resume.normal_dest,
&mut new_blocks_to_insert,
&ops,
vm,
......@@ -131,7 +134,7 @@ impl CompilerPass for GenMovPhi {
"norm"
);
let exn_dest = process_dest(
resume.exn_dest,
&resume.exn_dest,
&mut new_blocks_to_insert,
&ops,
vm,
......@@ -155,9 +158,12 @@ impl CompilerPass for GenMovPhi {
trace!("rewrite to {}", new_inst);
new_body.push(new_inst);
}
Instruction_::CCall { data, resume } => {
Instruction_::CCall {
ref data,
ref resume
} => {
let norm_dest = process_dest(
resume.normal_dest,
&resume.normal_dest,
&mut new_blocks_to_insert,
&ops,
vm,
......@@ -165,7 +171,7 @@ impl CompilerPass for GenMovPhi {
"norm"
);
let exn_dest = process_dest(
resume.exn_dest,
&resume.exn_dest,
&mut new_blocks_to_insert,
&ops,
vm,
......@@ -191,8 +197,8 @@ impl CompilerPass for GenMovPhi {
}
Instruction_::Switch {
cond,
default,
mut branches
ref default,
ref branches
} => {
let default_dest = process_dest(
default,
......@@ -204,10 +210,10 @@ impl CompilerPass for GenMovPhi {
);
let new_branches = branches
.drain(..)
.iter()
.map(|pair| {
let dest = process_dest(
pair.1,
&pair.1,
&mut new_blocks_to_insert,
&ops,
vm,
......@@ -237,11 +243,11 @@ impl CompilerPass for GenMovPhi {
Instruction_::SwapStackExc {
stack,
is_exception,
args,
resume
ref args,
ref resume
} => {
let norm_dest = process_dest(
resume.normal_dest,
&resume.normal_dest,
&mut new_blocks_to_insert,
&ops,
vm,
......@@ -249,7 +255,7 @@ impl CompilerPass for GenMovPhi {
"norm"
);
let exn_dest = process_dest(
resume.exn_dest,
&resume.exn_dest,
&mut new_blocks_to_insert,
&ops,
vm,
......@@ -264,7 +270,7 @@ impl CompilerPass for GenMovPhi {
v: Instruction_::SwapStackExc {
stack: stack,
is_exception: is_exception,
args: args,
args: args.clone(),
resume: ResumptionData {
normal_dest: norm_dest,
exn_dest: exn_dest
......@@ -376,7 +382,7 @@ impl CompilerPass for GenMovPhi {
/// the intermediate block as destination. Otherwise, return
/// the original destination
fn process_dest(
dest: Destination,
dest: &Destination,
blocks_to_insert: &mut Vec<IntermediateBlockInfo>,
ops: &Vec<P<TreeNode>>,
vm: &VM,
......@@ -384,9 +390,9 @@ fn process_dest(
label: &str
) -> Destination {
if dest.args.is_empty() {
dest
dest.clone()
} else {
let target = dest.target;
let ref target = dest.target;
let mut from_args = vec![];
for arg in dest.args.iter() {
......
......@@ -182,7 +182,7 @@ impl Inlining {
trace!("inserting inlined function at {}", inst);
// from TreeNode into Inst (we do not need old TreeNode)
let inst = inst.into_inst().unwrap();
let inst = inst.as_inst();
// inline expansion starts here
......@@ -225,7 +225,7 @@ impl Inlining {
data.args.iter().map(|x| ops[*x].clone()).collect();
let arg_indices: Vec<OpIndex> = (0..arg_nodes.len()).collect();
let branch = TreeNode::new_boxed_inst(Instruction {
let branch = TreeNode::new_inst(Instruction {
hdr: inst.hdr.clone(),
value: None,
ops: arg_nodes.clone(),
......@@ -259,7 +259,7 @@ impl Inlining {
if inst.value.is_none() {
vec![]
} else {
inst.value.unwrap()
inst.value.as_ref().unwrap().clone()
}
},
exn_arg: None,
......@@ -305,7 +305,7 @@ impl Inlining {
.as_mut()
.unwrap()
.body
.push(TreeNode::new_boxed_inst(branch));
.push(TreeNode::new_inst(branch));
// next block
let mut next_block = resume.normal_dest.target.clone();
......@@ -348,7 +348,7 @@ impl Inlining {
}
},
exn_arg: None,
body: vec![TreeNode::new_boxed_inst(branch)],
body: vec![TreeNode::new_inst(branch)],
keepalives: None
});
......@@ -452,7 +452,7 @@ fn copy_inline_blocks(
for i in 0..old_block_content.body.len() - 1 {
block_content.body.push(match old_block_content.body[i].v {
TreeNode_::Instruction(ref inst) => {
TreeNode::new_boxed_inst(inst.clone_with_id(vm.next_id()))
TreeNode::new_inst(inst.clone_with_id(vm.next_id()))
}
_ => panic!("expect instruction as block body")
});
......@@ -464,7 +464,7 @@ fn copy_inline_blocks(
let inst_new_id = vm.next_id();
let last_inst_clone = match last_inst.v {
TreeNode_::Instruction(ref inst) => {
TreeNode::new_boxed_inst(inst.clone_with_id(inst_new_id))
TreeNode::new_inst(inst.clone_with_id(inst_new_id))
}
_ => panic!("expect instruction as block body")
};
......@@ -492,7 +492,7 @@ fn copy_inline_blocks(
};
trace!("rewrite to: {}", branch);
block_content.body.push(TreeNode::new_boxed_inst(branch));
block_content.body.push(TreeNode::new_inst(branch));
}
// fix destination
......@@ -505,7 +505,7 @@ fn copy_inline_blocks(
};
trace!("rewrite to: {}", branch);
block_content.body.push(TreeNode::new_boxed_inst(branch));
block_content.body.push(TreeNode::new_inst(branch));
}
&Instruction_::Branch2 {
ref cond,
......@@ -526,7 +526,7 @@ fn copy_inline_blocks(
};
trace!("rewrite to: {}", branch2);
block_content.body.push(TreeNode::new_boxed_inst(branch2));
block_content.body.push(TreeNode::new_inst(branch2));
}
&Instruction_::Call {
ref data,
......@@ -543,7 +543,7 @@ fn copy_inline_blocks(
};
trace!("rewrite to: {}", call);
block_content.body.push(TreeNode::new_boxed_inst(call));
block_content.body.push(TreeNode::new_inst(call));
}
&Instruction_::CCall {
ref data,
......@@ -560,7 +560,7 @@ fn copy_inline_blocks(
};
trace!("rewrite to: {}", call);
block_content.body.push(TreeNode::new_boxed_inst(call));
block_content.body.push(TreeNode::new_inst(call));
}
&Instruction_::Switch {
ref cond,
......@@ -584,7 +584,7 @@ fn copy_inline_blocks(
};
trace!("rewrite to: {}", switch);
block_content.body.push(TreeNode::new_boxed_inst(switch));
block_content.body.push(TreeNode::new_inst(switch));
}
&Instruction_::SwapStackExc {
......@@ -606,7 +606,7 @@ fn copy_inline_blocks(
};
trace!("rewrite to: {}", swapstack);
block_content.body.push(TreeNode::new_boxed_inst(swapstack));
block_content.body.push(TreeNode::new_inst(swapstack));
}
&Instruction_::Watchpoint { .. } |
&Instruction_::WPBranch { .. } |
......
......@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use ast::ptr::*;
use ast::ir::*;
use vm::VM;
use std::any::Any;
......@@ -26,8 +27,7 @@ pub use compiler::passes::inlining::Inlining;
mod ret_sink;
pub use compiler::passes::ret_sink::RetSink;
/// A Def-Use pass. Getting use info and count for SSA variables in the IR (we are not collecting
/// define info)
/// A Def-Use pass. Getting use info and count for SSA variables in the IR
mod def_use;
pub use compiler::passes::def_use::DefUse;
......@@ -112,5 +112,5 @@ pub trait CompilerPass {
fn start_block(&mut self, vm: &VM, func_context: &mut FunctionContext, block: &mut Block) {}
fn finish_block(&mut self, vm: &VM, func_context: &mut FunctionContext, block: &mut Block) {}
fn visit_inst(&mut self, vm: &VM, func_context: &mut FunctionContext, node: &TreeNode) {}
fn visit_inst(&mut self, vm: &VM, func_context: &mut FunctionContext, node: &P<TreeNode>) {}
}
......@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use ast::ptr::*;
use ast::ir::*;
use ast::inst::*;
......@@ -102,6 +103,15 @@ fn is_suitable_child(inst: &Instruction) -> bool {
}
}
fn should_always_move(inst: &Instruction) -> bool {
// for instructions that yields int<1>, we always move the instruction
if let Some(ref vals) = inst.value {
if vals.len() == 1 && vals[0].ty.is_int_n(1) {
return true;
}
}
false
}
impl CompilerPass for TreeGen {
fn name(&self) -> &'static str {
......@@ -129,100 +139,22 @@ impl CompilerPass for TreeGen {
let ref mut func_content = func.content;
let ref mut context = func.context;
for (label, ref mut block) in func_content.as_mut().unwrap().blocks.iter_mut() {
trace!("check block {}", label);
trace!("");
// take its content, we will need to put it back
let mut content = block.content.take().unwrap();
let body = content.body;
let mut new_body = vec![];
trace!("check block {}", label);
trace!("");
for i in 0..body.len() {
let mut node = body[i].clone();
trace!("check inst: {}", node);
match &mut node.v {
&mut TreeNode_::Instruction(ref mut inst) => {
// check whether any operands (SSA) can be replaced by expression
{
trace!("check if we can replace any operand with inst");
let ref mut ops = inst.ops;
for index in 0..ops.len() {
let possible_ssa_id = ops[index].extract_ssa_id();
if possible_ssa_id.is_some() {
let entry_value = context
.get_value_mut(possible_ssa_id.unwrap())
.unwrap();
if entry_value.has_expr() {
// replace the node with its expr
let expr = entry_value.take_expr();
trace!("{} replaced by {}", ops[index], expr);
ops[index] = TreeNode::new_inst(expr);
}
} else {
trace!("{} cant be replaced", ops[index]);
}
}
}
// check whether the instruction generates an SSA that is used only once
// An instruction can replace its value if
// * it generates only one value
// * the value is used only once
// * the instruction is movable
// * the value is used in the next instruction
trace!("check if we should fold the inst");
if inst.value.is_some() {
let left = inst.value.as_ref().unwrap();
// if left is _one_ variable that is used once
// we can put the expression as a child node to its use
if left.len() == 1 {
let ref val_lhs = left[0];
let lhs = context
.get_value_mut(left[0].extract_ssa_id().unwrap())
.unwrap();
if lhs.use_count() == 1 {
let next_inst_uses_lhs = {
if i != body.len() - 1 {
let ref next_inst = body[i + 1].as_inst_ref();
next_inst
.ops
.iter()
.any(|x| x.as_value() == val_lhs)
} else {
false
}
};
if is_movable(&inst) && next_inst_uses_lhs {
// FIXME: should be able to move the inst here
lhs.assign_expr(inst.clone());
trace!("yes");
trace!("");
continue;
} else {
trace!("no, not movable or not used by next inst");
}
} else {
trace!("no, use count more than 1");
}
} else {
trace!("no, yields more than 1 SSA var");
}
} else {
trace!("no, no value yielded");
}
}
_ => panic!("expected an instruction node here")
let node = body[i].clone();
let new_node = insert_inst_as_child(node, context);
if !is_child_inst(&new_node, i, &body, context) {
new_body.push(new_node);
}
trace!("add {} back to block {}", node, label);
trace!("");
new_body.push(node);
}
content.body = new_body;
......@@ -252,3 +184,92 @@ impl CompilerPass for TreeGen {
}
}
}
fn is_child_inst(
node: &P<TreeNode>,
cur_index: usize,
body: &Vec<P<TreeNode>>,
context: &mut FunctionContext
) -> bool {
trace!("is child inst: {}", node);
match node.v {
TreeNode_::Instruction(ref inst) => {
// check whether the instruction generates an SSA that is used only once
// An instruction can replace its value if
// * it generates only one value
// * the value is used only once
// * the instruction is movable
// * the value is used in the next instruction
if inst.value.is_some() {
let left = inst.value.as_ref().unwrap();
// if left is _one_ variable that is used once
// we can put the expression as a child node to its use
if left.len() == 1 {
let ref val_lhs = left[0];
let lhs = context
.get_value_mut(left[0].extract_ssa_id().unwrap())
.unwrap();
if lhs.use_count() == 1 {
let next_inst_uses_lhs = {
if cur_index != body.len() - 1 {
let ref next_inst = body[cur_index + 1].as_inst();
next_inst.ops.iter().any(|x| x.as_value() == val_lhs)
} else {
false
}
};
if should_always_move(&inst) || (is_movable(&inst) && next_inst_uses_lhs) {
// FIXME: should be able to move the inst here
lhs.assign_expr(inst.clone());
trace!(" yes, extract the inst");
return true;
} else {
trace!(" no, not movable or not used by next inst");
}
} else {
trace!(" no, use count more than 1");
}
} else {
trace!(" no, yields more than 1 SSA var");
}
} else {
trace!(" no, no value yielded");
}
false
}
_ => panic!("expected an instruction node here")
}
}
fn insert_inst_as_child(node: P<TreeNode>, context: &mut FunctionContext) -> P<TreeNode> {
trace!("insert child for: {}", node);
let mut inst = node.as_inst().clone();
// check whether any operands (SSA) can be replaced by expression
{
let ref mut ops = inst.ops;
for index in 0..ops.len() {
let ssa_id = ops[index].extract_ssa_id();
if ssa_id.is_some() {
let entry_value = context.get_value_mut(ssa_id.unwrap()).unwrap();
if entry_value.has_expr() {
// replace the node with its expr
let expr = entry_value.take_expr();
trace!(" {} replaced by {}", ops[index], expr);
ops[index] = TreeNode::new_inst(expr);
}
} else {
trace!(" {} cant be replaced", ops[index]);
}
}
}
let new_node = TreeNode::new_inst(inst);
trace!(" rewrite node as {}", new_node);
new_node
}
......@@ -120,7 +120,7 @@ fn emit_uir_inner(file: &mut File, func_name: MuName, func_ver: &MuFunctionVersi
// all the instructions
for inst in block_content.body.iter() {
writeln!(file, "\t\t{}", inst.as_inst_ref()).unwrap();
writeln!(file, "\t\t{}", inst.as_inst()).unwrap();
}
// "];
......
......@@ -2348,8 +2348,8 @@ impl<'lb, 'lvm> BundleLoader<'lb, 'lvm> {
tn
}
pub fn new_inst(&self, v: Instruction) -> Box<TreeNode> {
Box::new(TreeNode {
pub fn new_inst(&self, v: Instruction) -> P<TreeNode> {
P(TreeNode {
v: TreeNode_::Instruction(v)
})
}
......@@ -2416,7 +2416,7 @@ impl<'lb, 'lvm> BundleLoader<'lb, 'lvm> {
fcb: &mut FuncCtxBuilder,
id: MuID,
blocks: &LinkedHashMap<MuID, Block>
) -> Vec<Box<TreeNode>> {
) -> Vec<P<TreeNode>> {
let res = self.b
.bundle
.bbs
......@@ -2430,10 +2430,10 @@ impl<'lb, 'lvm> BundleLoader<'lb, 'lvm> {
let n = res.len();
for i in 0..(n - 1) {
// None of the internal instruction should be a terminator
assert_ir!(!res[i].as_inst_ref().is_terminal_inst());
assert_ir!(!res[i].as_inst().is_terminal_inst());
}
// The last instruction should be a terminator
assert_ir!(res[n - 1].as_inst_ref().is_terminal_inst());
assert_ir!(res[n - 1].as_inst().is_terminal_inst());
res
}
......@@ -2442,7 +2442,7 @@ impl<'lb, 'lvm> BundleLoader<'lb, 'lvm> {
fcb: &mut FuncCtxBuilder,
id: MuID,
blocks: &LinkedHashMap<MuID, Block>
) -> Box<TreeNode> {
) -> P<TreeNode> {
let inst = self.b.bundle.insts.get(&id).unwrap();
trace!("Building instruction {} {:?}", id, inst);
......
......@@ -69,11 +69,7 @@ fn test_ccall_exit() {
assert!(ret_code == 10);
}
pub fn gen_ccall_exit(
arg: P<TreeNode>,
func_ver: &mut MuFunctionVersion,
vm: &VM
) -> Box<TreeNode> {
pub fn gen_ccall_exit(arg: P<TreeNode>, func_ver: &mut MuFunctionVersion, vm: &VM) -> P<TreeNode> {
typedef!((vm) int64 = mu_int(64));
funcsig!((vm) exit_sig = (int64) -> ());
typedef!((vm) ufp_exit = mu_ufuncptr(exit_sig));
......
......@@ -156,3 +156,47 @@ def test_eq_f_zero():
assert(eq_zero(ctypes.c_double(0)) == 1)
assert(eq_zero(ctypes.c_double(1)) == 0)
assert(eq_zero(ctypes.c_double(-1)) == 0)
def test_cmp_pattern1():
lib = load_bundle(
"""
.funcdef test_cmp_pattern1 <(int<64> int<64>) -> (int<64>)>
{
entry(<int<64>> x <int<64>> y):
cond = EQ <int<64>> x y
cond_ = ZEXT <int<1> int<8>> cond
actual_cond = EQ <int<8>> cond_ <int<8>> 1
sum = ADD <int<64>> x y
BRANCH2 actual_cond ret_true(sum) ret_false(sum)
ret_true(<int<64>> sum):
RET <int<64>> 1
ret_false(<int<64>> sum):
RET <int<64>> 0
}
""", "test_cmp_pattern1"
)
eq = get_function(lib.test_cmp_pattern1, [ctypes.c_int64, ctypes.c_int64], ctypes.c_int64)
assert(eq(1, 1) == 1)
assert(eq(1, 0) == 0)
def test_cmp_pattern2():
lib = load_bundle(
"""
.funcdef test_cmp_pattern2 <() -> (int<64>)>
{
entry():
cond = EQ <int<64>> <int<64>> 0 <int<64>> 1
BRANCH2 cond ret_true() ret_false()
ret_true():
RET <int<64>> 1
ret_false():
RET <int<64>> 0
}
""", "test_cmp_pattern2"
)
cmp_pattern2 = get_function(lib.test_cmp_pattern2, [], ctypes.c_int64)
assert(cmp_pattern2() == 0)
\ No newline at end of file