Commit 855383c3 authored by qinsoon's avatar qinsoon

[wip] some refactoring

parent ac147763
...@@ -7,6 +7,11 @@ build = "build.rs" ...@@ -7,6 +7,11 @@ build = "build.rs"
[lib] [lib]
crate-type = ["staticlib", "rlib"] crate-type = ["staticlib", "rlib"]
[features]
default = ["aot"]
aot = []
jit = []
[build-dependencies] [build-dependencies]
gcc = "0.3" gcc = "0.3"
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
</content> </content>
<orderEntry type="inheritedJdk" /> <orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Cargo &lt;mu&gt;" level="project" />
<orderEntry type="library" name="Rust &lt;mu&gt;" level="project" /> <orderEntry type="library" name="Rust &lt;mu&gt;" level="project" />
<orderEntry type="library" name="Cargo &lt;mu&gt;" level="project" />
</component> </component>
</module> </module>
\ No newline at end of file
...@@ -102,6 +102,7 @@ pub struct MuFunctionVersion { ...@@ -102,6 +102,7 @@ pub struct MuFunctionVersion {
pub func_id: MuID, pub func_id: MuID,
pub sig: P<MuFuncSig>, pub sig: P<MuFuncSig>,
pub original_ir: Option<FunctionContent>,
pub content: Option<FunctionContent>, pub content: Option<FunctionContent>,
pub context: FunctionContext, pub context: FunctionContext,
...@@ -138,26 +139,31 @@ impl MuFunctionVersion { ...@@ -138,26 +139,31 @@ impl MuFunctionVersion {
hdr: MuEntityHeader::unnamed(id), hdr: MuEntityHeader::unnamed(id),
func_id: func, func_id: func,
sig: sig, sig: sig,
original_ir: None,
content: None, content: None,
context: FunctionContext::new(), context: FunctionContext::new(),
block_trace: None} block_trace: None
}
} }
pub fn define(&mut self, content: FunctionContent) { pub fn define(&mut self, content: FunctionContent) {
self.content = Some(content) self.original_ir = Some(content.clone());
self.content = Some(content);
} }
pub fn new_ssa(&mut self, id: MuID, ty: P<MuType>) -> P<TreeNode> { pub fn new_ssa(&mut self, id: MuID, ty: P<MuType>) -> P<TreeNode> {
self.context.values.insert(id, SSAVarEntry::new(id, ty.clone())); let val = P(Value{
hdr: MuEntityHeader::unnamed(id),
ty: ty,
v: Value_::SSAVar(id)
});
self.context.values.insert(id, SSAVarEntry::new(val.clone()));
P(TreeNode { P(TreeNode {
hdr: MuEntityHeader::unnamed(id), hdr: MuEntityHeader::unnamed(id),
op: pick_op_code_for_ssa(&ty), op: pick_op_code_for_ssa(&val.ty),
v: TreeNode_::Value(P(Value{ v: TreeNode_::Value(val)
hdr: MuEntityHeader::unnamed(id),
ty: ty,
v: Value_::SSAVar(id)
}))
}) })
} }
...@@ -186,7 +192,7 @@ impl MuFunctionVersion { ...@@ -186,7 +192,7 @@ impl MuFunctionVersion {
} }
} }
#[derive(RustcEncodable, RustcDecodable)] #[derive(RustcEncodable, RustcDecodable, Clone)]
pub struct FunctionContent { pub struct FunctionContent {
pub entry: MuID, pub entry: MuID,
pub blocks: HashMap<MuID, Block> pub blocks: HashMap<MuID, Block>
...@@ -245,16 +251,18 @@ impl FunctionContext { ...@@ -245,16 +251,18 @@ impl FunctionContext {
} }
pub fn make_temporary(&mut self, id: MuID, ty: P<MuType>) -> P<TreeNode> { pub fn make_temporary(&mut self, id: MuID, ty: P<MuType>) -> P<TreeNode> {
self.values.insert(id, SSAVarEntry::new(id, ty.clone())); let val = P(Value{
hdr: MuEntityHeader::unnamed(id),
ty: ty,
v: Value_::SSAVar(id)
});
self.values.insert(id, SSAVarEntry::new(val.clone()));
P(TreeNode { P(TreeNode {
hdr: MuEntityHeader::unnamed(id), hdr: MuEntityHeader::unnamed(id),
op: pick_op_code_for_ssa(&ty), op: pick_op_code_for_ssa(&val.ty),
v: TreeNode_::Value(P(Value{ v: TreeNode_::Value(val)
hdr: MuEntityHeader::unnamed(id),
ty: ty,
v: Value_::SSAVar(id)
}))
}) })
} }
...@@ -267,7 +275,7 @@ impl FunctionContext { ...@@ -267,7 +275,7 @@ impl FunctionContext {
} }
} }
#[derive(RustcEncodable, RustcDecodable)] #[derive(RustcEncodable, RustcDecodable, Clone)]
pub struct Block { pub struct Block {
pub hdr: MuEntityHeader, pub hdr: MuEntityHeader,
pub content: Option<BlockContent>, pub content: Option<BlockContent>,
...@@ -298,7 +306,7 @@ impl Block { ...@@ -298,7 +306,7 @@ impl Block {
} }
} }
#[derive(Debug, RustcEncodable, RustcDecodable)] #[derive(Debug, RustcEncodable, RustcDecodable, Clone)]
pub struct ControlFlow { pub struct ControlFlow {
pub preds : Vec<MuID>, pub preds : Vec<MuID>,
pub succs : Vec<BlockEdge> pub succs : Vec<BlockEdge>
...@@ -356,7 +364,7 @@ pub enum EdgeKind { ...@@ -356,7 +364,7 @@ pub enum EdgeKind {
Forward, Backward Forward, Backward
} }
#[derive(RustcEncodable, RustcDecodable)] #[derive(RustcEncodable, RustcDecodable, Clone)]
pub struct BlockContent { pub struct BlockContent {
pub args: Vec<P<Value>>, pub args: Vec<P<Value>>,
pub exn_arg: Option<P<Value>>, pub exn_arg: Option<P<Value>>,
...@@ -447,7 +455,7 @@ impl BlockContent { ...@@ -447,7 +455,7 @@ impl BlockContent {
} }
} }
#[derive(Debug, RustcEncodable, RustcDecodable)] #[derive(Debug, RustcEncodable, RustcDecodable, Clone)]
/// always use with P<TreeNode> /// always use with P<TreeNode>
pub struct TreeNode { pub struct TreeNode {
pub hdr: MuEntityHeader, pub hdr: MuEntityHeader,
...@@ -511,7 +519,7 @@ impl fmt::Display for TreeNode { ...@@ -511,7 +519,7 @@ impl fmt::Display for TreeNode {
} }
} }
#[derive(Debug, RustcEncodable, RustcDecodable)] #[derive(Debug, RustcEncodable, RustcDecodable, Clone)]
pub enum TreeNode_ { pub enum TreeNode_ {
Value(P<Value>), Value(P<Value>),
Instruction(Instruction) Instruction(Instruction)
...@@ -629,25 +637,23 @@ pub enum Value_ { ...@@ -629,25 +637,23 @@ pub enum Value_ {
#[derive(Debug)] #[derive(Debug)]
pub struct SSAVarEntry { pub struct SSAVarEntry {
id: MuID, val: P<Value>,
pub ty: P<MuType>,
// how many times this entry is used // how many times this entry is used
// availalbe after DefUse pass // availalbe after DefUse pass
pub use_count: AtomicUsize, use_count: AtomicUsize,
// this field is only used during TreeGeneration pass // this field is only used during TreeGeneration pass
pub expr: Option<Instruction> expr: Option<Instruction>
} }
impl Encodable for SSAVarEntry { impl Encodable for SSAVarEntry {
fn encode<S: Encoder> (&self, s: &mut S) -> Result<(), S::Error> { fn encode<S: Encoder> (&self, s: &mut S) -> Result<(), S::Error> {
s.emit_struct("SSAVarEntry", 4, |s| { s.emit_struct("SSAVarEntry", 3, |s| {
try!(s.emit_struct_field("id", 0, |s| self.id.encode(s))); try!(s.emit_struct_field("val", 0, |s| self.val.encode(s)));
try!(s.emit_struct_field("ty", 1, |s| self.ty.encode(s)));
let count = self.use_count.load(Ordering::SeqCst); let count = self.use_count.load(Ordering::SeqCst);
try!(s.emit_struct_field("use_count", 2, |s| s.emit_usize(count))); try!(s.emit_struct_field("use_count", 1, |s| s.emit_usize(count)));
try!(s.emit_struct_field("expr", 3, |s| self.expr.encode(s))); try!(s.emit_struct_field("expr", 2, |s| self.expr.encode(s)));
Ok(()) Ok(())
}) })
} }
...@@ -655,15 +661,13 @@ impl Encodable for SSAVarEntry { ...@@ -655,15 +661,13 @@ impl Encodable for SSAVarEntry {
impl Decodable for SSAVarEntry { impl Decodable for SSAVarEntry {
fn decode<D: Decoder>(d: &mut D) -> Result<SSAVarEntry, D::Error> { fn decode<D: Decoder>(d: &mut D) -> Result<SSAVarEntry, D::Error> {
d.read_struct("SSAVarEntry", 4, |d| { d.read_struct("SSAVarEntry", 3, |d| {
let id = try!(d.read_struct_field("id", 0, |d| Decodable::decode(d))); let val = try!(d.read_struct_field("val", 0, |d| Decodable::decode(d)));
let ty = try!(d.read_struct_field("ty", 1, |d| Decodable::decode(d))); let count = try!(d.read_struct_field("use_count", 1, |d| d.read_usize()));
let count = try!(d.read_struct_field("use_count", 2, |d| d.read_usize())); let expr = try!(d.read_struct_field("expr", 2, |d| Decodable::decode(d)));
let expr = try!(d.read_struct_field("expr", 3, |d| Decodable::decode(d)));
let ret = SSAVarEntry { let ret = SSAVarEntry {
id: id, val: val,
ty: ty,
use_count: ATOMIC_USIZE_INIT, use_count: ATOMIC_USIZE_INIT,
expr: expr expr: expr
}; };
...@@ -676,10 +680,9 @@ impl Decodable for SSAVarEntry { ...@@ -676,10 +680,9 @@ impl Decodable for SSAVarEntry {
} }
impl SSAVarEntry { impl SSAVarEntry {
pub fn new(id: MuID, ty: P<MuType>) -> SSAVarEntry { pub fn new(val: P<Value>) -> SSAVarEntry {
let ret = SSAVarEntry { let ret = SSAVarEntry {
id: id, val: val,
ty: ty,
use_count: ATOMIC_USIZE_INIT, use_count: ATOMIC_USIZE_INIT,
expr: None expr: None
}; };
...@@ -688,14 +691,37 @@ impl SSAVarEntry { ...@@ -688,14 +691,37 @@ impl SSAVarEntry {
ret ret
} }
pub fn ty(&self) -> &P<MuType> {
&self.val.ty
}
pub fn value(&self) -> &P<Value> {
&self.val
}
pub fn use_count(&self) -> usize {
self.use_count.load(Ordering::SeqCst)
}
pub fn increase_use_count(&self) {
self.use_count.fetch_add(1, Ordering::SeqCst);
}
pub fn has_expr(&self) -> bool {
self.expr.is_some()
}
pub fn assign_expr(&mut self, expr: Instruction) { pub fn assign_expr(&mut self, expr: Instruction) {
self.expr = Some(expr) self.expr = Some(expr)
} }
pub fn take_expr(&mut self) -> Instruction {
debug_assert!(self.has_expr());
self.expr.take().unwrap()
}
} }
impl fmt::Display for SSAVarEntry { impl fmt::Display for SSAVarEntry {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} #{}", self.ty, self.id) write!(f, "{}", self.val)
} }
} }
...@@ -775,12 +801,21 @@ impl fmt::Display for MemoryLocation { ...@@ -775,12 +801,21 @@ impl fmt::Display for MemoryLocation {
} }
#[repr(C)] #[repr(C)]
#[derive(Debug)] // Display, PartialEq #[derive(Debug)] // Display, PartialEq, Clone
pub struct MuEntityHeader { pub struct MuEntityHeader {
pub id: MuID, pub id: MuID,
pub name: RwLock<Option<MuName>> pub name: RwLock<Option<MuName>>
} }
impl Clone for MuEntityHeader {
fn clone(&self) -> Self {
MuEntityHeader {
id: self.id,
name: RwLock::new(self.name.read().unwrap().clone())
}
}
}
use rustc_serialize::{Encodable, Encoder, Decodable, Decoder}; use rustc_serialize::{Encodable, Encoder, Decodable, Decoder};
impl Encodable for MuEntityHeader { impl Encodable for MuEntityHeader {
fn encode<S: Encoder> (&self, s: &mut S) -> Result<(), S::Error> { fn encode<S: Encoder> (&self, s: &mut S) -> Result<(), S::Error> {
......
...@@ -11,6 +11,7 @@ use compiler::machine_code::MachineCode; ...@@ -11,6 +11,7 @@ use compiler::machine_code::MachineCode;
use vm::VM; use vm::VM;
use runtime::ValueLocation; use runtime::ValueLocation;
use utils::vec_utils;
use utils::string_utils; use utils::string_utils;
use ast::ptr::P; use ast::ptr::P;
...@@ -112,6 +113,9 @@ impl MachineCode for ASMCode { ...@@ -112,6 +113,9 @@ impl MachineCode for ASMCode {
} }
fn set_inst_nop(&mut self, index: usize) { fn set_inst_nop(&mut self, index: usize) {
// FIXME: need to make sure it is fine that
// we do not update any information about this instruction
// e.g. uses, defines, etc.
self.code.remove(index); self.code.remove(index);
self.code.insert(index, ASM::nop()); self.code.insert(index, ASM::nop());
} }
...@@ -286,11 +290,16 @@ impl ASMCodeGen { ...@@ -286,11 +290,16 @@ impl ASMCodeGen {
} }
fn add_asm_call(&mut self, code: String) { fn add_asm_call(&mut self, code: String) {
// a call instruction will use all the argument registers
let mut uses : Vec<MuID> = self.prepare_machine_regs(x86_64::ARGUMENT_GPRs.iter()); let mut uses : Vec<MuID> = self.prepare_machine_regs(x86_64::ARGUMENT_GPRs.iter());
uses.append(&mut self.prepare_machine_regs(x86_64::ARGUMENT_FPRs.iter())); uses.append(&mut self.prepare_machine_regs(x86_64::ARGUMENT_FPRs.iter()));
// defines: return registers
let mut defines : Vec<MuID> = self.prepare_machine_regs(x86_64::RETURN_GPRs.iter()); let mut defines : Vec<MuID> = self.prepare_machine_regs(x86_64::RETURN_GPRs.iter());
defines.append(&mut self.prepare_machine_regs(x86_64::RETURN_FPRs.iter())); defines.append(&mut self.prepare_machine_regs(x86_64::RETURN_FPRs.iter()));
// defines: caller saved registers
vec_utils::append_unique(&mut defines, &mut self.prepare_machine_regs(x86_64::CALLER_SAVED_GPRs.iter()));
vec_utils::append_unique(&mut defines, &mut self.prepare_machine_regs(x86_64::CALLER_SAVED_FPRs.iter()));
self.add_asm_inst(code, defines, vec![], uses, vec![], false); self.add_asm_inst(code, defines, vec![], uses, vec![], false);
} }
......
...@@ -37,6 +37,7 @@ pub struct InstructionSelection { ...@@ -37,6 +37,7 @@ pub struct InstructionSelection {
} }
impl <'a> InstructionSelection { impl <'a> InstructionSelection {
#[cfg(feature = "aot")]
pub fn new() -> InstructionSelection { pub fn new() -> InstructionSelection {
InstructionSelection{ InstructionSelection{
name: "Instruction Selection (x64)", name: "Instruction Selection (x64)",
...@@ -51,6 +52,11 @@ impl <'a> InstructionSelection { ...@@ -51,6 +52,11 @@ impl <'a> InstructionSelection {
current_exn_blocks: HashMap::new() current_exn_blocks: HashMap::new()
} }
} }
#[cfg(feature = "jit")]
pub fn new() -> InstructionSelection {
unimplemented!()
}
// in this pass, we assume that // in this pass, we assume that
// 1. all temporaries will use 64bit registers // 1. all temporaries will use 64bit registers
......
...@@ -85,6 +85,18 @@ lazy_static! { ...@@ -85,6 +85,18 @@ lazy_static! {
R14.clone(), R14.clone(),
R15.clone() R15.clone()
]; ];
pub static ref CALLER_SAVED_GPRs : [P<Value>; 9] = [
RAX.clone(),
RCX.clone(),
RDX.clone(),
RSI.clone(),
RDI.clone(),
R8.clone(),
R9.clone(),
R10.clone(),
R11.clone()
];
pub static ref ALL_GPRs : [P<Value>; 15] = [ pub static ref ALL_GPRs : [P<Value>; 15] = [
RAX.clone(), RAX.clone(),
...@@ -139,6 +151,25 @@ lazy_static!{ ...@@ -139,6 +151,25 @@ lazy_static!{
]; ];
pub static ref CALLEE_SAVED_FPRs : [P<Value>; 0] = []; pub static ref CALLEE_SAVED_FPRs : [P<Value>; 0] = [];
pub static ref CALLER_SAVED_FPRs : [P<Value>; 16] = [
XMM0.clone(),
XMM1.clone(),
XMM2.clone(),
XMM3.clone(),
XMM4.clone(),
XMM5.clone(),
XMM6.clone(),
XMM7.clone(),
XMM8.clone(),
XMM9.clone(),
XMM10.clone(),
XMM11.clone(),
XMM12.clone(),
XMM13.clone(),
XMM14.clone(),
XMM15.clone(),
];
pub static ref ALL_FPRs : [P<Value>; 16] = [ pub static ref ALL_FPRs : [P<Value>; 16] = [
XMM0.clone(), XMM0.clone(),
...@@ -242,7 +273,7 @@ lazy_static! { ...@@ -242,7 +273,7 @@ lazy_static! {
pub fn init_machine_regs_for_func (func_context: &mut FunctionContext) { pub fn init_machine_regs_for_func (func_context: &mut FunctionContext) {
for reg in ALL_MACHINE_REGs.values() { for reg in ALL_MACHINE_REGs.values() {
let reg_id = reg.extract_ssa_id().unwrap(); let reg_id = reg.extract_ssa_id().unwrap();
let entry = SSAVarEntry::new(reg_id, reg.ty.clone()); let entry = SSAVarEntry::new(reg.clone());
func_context.values.insert(reg_id, entry); func_context.values.insert(reg_id, entry);
} }
......
...@@ -20,7 +20,7 @@ impl PeepholeOptimization { ...@@ -20,7 +20,7 @@ impl PeepholeOptimization {
let src : MuID = { let src : MuID = {
let uses = cf.mc().get_inst_reg_uses(inst); let uses = cf.mc().get_inst_reg_uses(inst);
if uses.len() != 1 { if uses.len() == 0 {
// moving immediate to register, its not redundant // moving immediate to register, its not redundant
return; return;
} }
......
...@@ -55,7 +55,7 @@ impl InterferenceGraph { ...@@ -55,7 +55,7 @@ impl InterferenceGraph {
// add node property // add node property
let group = { let group = {
let ref ty = entry.ty; let ref ty = entry.ty();
if types::is_scalar(ty) { if types::is_scalar(ty) {
if types::is_fp(ty) { if types::is_fp(ty) {
backend::RegGroup::FPR backend::RegGroup::FPR
......
#![allow(dead_code)] #![allow(dead_code)]
use ast::ir::*;
use ast::ptr::*;
use vm::VM;
use compiler; use compiler;
use compiler::CompilerPass; use compiler::CompilerPass;
use compiler::machine_code::CompiledFunction;
use compiler::PassExecutionResult; use compiler::PassExecutionResult;
use ast::ir::*;
use vm::VM;
use compiler::backend::init_machine_regs_for_func; use compiler::backend::init_machine_regs_for_func;
use std::collections::HashMap;
mod graph_coloring; mod graph_coloring;
pub enum RegAllocFailure { pub enum RegAllocFailure {
...@@ -49,9 +52,22 @@ impl RegisterAllocation { ...@@ -49,9 +52,22 @@ impl RegisterAllocation {
let spills = coloring.spills(); let spills = coloring.spills();
if !spills.is_empty() { if !spills.is_empty() {
unimplemented!(); let mut spilled_mem = HashMap::new();
// allocating frame slots for every spilled temp
for reg_id in spills.iter() {
let ssa_entry = match func.context.get_value(*reg_id) {
Some(entry) => entry,
None => panic!("The spilled register {} is not in func context", reg_id)
};
let mem = cf.frame.alloc_slot_for_spilling(ssa_entry.value().clone(), vm);
// return Err(RegAllocFailure::FailedForSpilling); spilled_mem.insert(*reg_id, mem);
}
self.spill_rewrite(&spilled_mem, func, &mut cf, vm);
return Err(RegAllocFailure::FailedForSpilling);
} }
// replace regs // replace regs
...@@ -76,7 +92,18 @@ impl RegisterAllocation { ...@@ -76,7 +92,18 @@ impl RegisterAllocation {
cf.mc().trace_mc(); cf.mc().trace_mc();
Ok(()) Ok(())
} }
#[cfg(feature = "aot")]
fn spill_rewrite(
&mut self,
spills: &HashMap<MuID, P<Value>>,
func: &mut MuFunctionVersion,
compiled_func: &mut CompiledFunction,
vm: &VM)
{
unimplemented!()
}
} }
impl CompilerPass for RegisterAllocation { impl CompilerPass for RegisterAllocation {
......
...@@ -115,7 +115,8 @@ pub trait MachineCode { ...@@ -115,7 +115,8 @@ pub trait MachineCode {
fn get_all_blocks(&self) -> &Vec<MuName>; fn get_all_blocks(&self) -> &Vec<MuName>;
fn get_block_range(&self, block: &str) -> Option<ops::Range<usize>>; fn get_block_range(&self, block: &str) -> Option<ops::Range<usize>>;
// functions for rewrite
fn replace_reg(&mut self, from: MuID, to: MuID); fn replace_reg(&mut self, from: MuID, to: MuID);
fn set_inst_nop(&mut self, index: usize); fn set_inst_nop(&mut self, index: usize);
} }
...@@ -28,7 +28,7 @@ fn use_value(val: &P<Value>, func_context: &mut FunctionContext) { ...@@ -28,7 +28,7 @@ fn use_value(val: &P<Value>, func_context: &mut FunctionContext) {
match val.v { match val.v {
Value_::SSAVar(ref id) => { Value_::SSAVar(ref id) => {
let entry = func_context.values.get_mut(id).unwrap(); let entry = func_context.values.get_mut(id).unwrap();
entry.use_count.fetch_add(1, Ordering::SeqCst); entry.increase_use_count();
}, },
_ => {} // dont worry about constants _ => {} // dont worry about constants
} }
...@@ -68,7 +68,7 @@ impl CompilerPass for DefUse { ...@@ -68,7 +68,7 @@ impl CompilerPass for DefUse {
debug!("check use count for variables"); debug!("check use count for variables");
for entry in func.context.values.values() { for entry in func.context.values.values() {
debug!("{}: {}", entry, entry.use_count.load(Ordering::SeqCst)) debug!("{}: {}", entry, entry.use_count())
} }
} }
} }
...@@ -57,9 +57,9 @@ impl CompilerPass for TreeGen { ...@@ -57,9 +57,9 @@ impl CompilerPass for TreeGen {
if possible_ssa_id.is_some() { if possible_ssa_id.is_some() {
let entry_value = context.get_value_mut(possible_ssa_id.unwrap()).unwrap(); let entry_value = context.get_value_mut(possible_ssa_id.unwrap()).unwrap();
if entry_value.expr.is_some() { if entry_value.has_expr() {
// replace the node with its expr // replace the node with its expr
let expr = entry_value.expr.take().unwrap(); let expr = entry_value.take_expr();
trace!("{} replaced by {}", ops[index], expr); trace!("{} replaced by {}", ops[index], expr);
ops[index] = TreeNode::new_inst(vm.next_id(), expr); ops[index] = TreeNode::new_inst(vm.next_id(), expr);
...@@ -79,9 +79,9 @@ impl CompilerPass for TreeGen { ...@@ -79,9 +79,9 @@ impl CompilerPass for TreeGen {
// we can put the expression as a child node to its use // we can put the expression as a child node to its use
if left.len() == 1 { if left.len() == 1 {
let lhs = context.get_value_mut(left[0].extract_ssa_id().unwrap()).unwrap(); let lhs = context.get_value_mut(left[0].extract_ssa_id().unwrap()).unwrap();
if lhs.use_count.load(Ordering::SeqCst) == 1{ if lhs.use_count() == 1{
if is_movable(&inst.v) { if is_movable(&inst.v) {
lhs.expr = Some(inst.clone()); // FIXME: should be able to move the inst here lhs.assign_expr(inst.clone()); // FIXME: should be able to move the inst here
trace!