GitLab will be upgraded on 31 Jan 2023 from 2.00 pm (AEDT) to 3.00 pm (AEDT). During the update, GitLab and Mattermost services will not be available. If you have any concerns with this, please talk to us at N110 (b) CSIT building.

Commit 8166d516 authored by qinsoon's avatar qinsoon
Browse files

[wip] uitofp works for u64. u32/16/8 may have simpler implementation

parent 6648dbf0
......@@ -885,7 +885,9 @@ pub enum Constant {
Vector(Vec<Constant>),
//Pointer(Address),
NullRef,
ExternSym(CName)
ExternSym(CName),
List(Vec<P<Value>>) // a composite type of several constants
}
impl fmt::Display for Constant {
......@@ -907,7 +909,15 @@ impl fmt::Display for Constant {
write!(f, "]")
}
&Constant::NullRef => write!(f, "NullRef"),
&Constant::ExternSym(ref name) => write!(f, "ExternSym({})", name)
&Constant::ExternSym(ref name) => write!(f, "ExternSym({})", name),
&Constant::List(ref vec) => {
write!(f, "List(").unwrap();
for val in vec.iter() {
write!(f, "{}, ", val).unwrap();
}
write!(f, ")")
}
}
}
}
......@@ -922,7 +932,8 @@ pub enum MemoryLocation {
},
Symbolic{
base: Option<P<Value>>,
label: MuName
label: MuName,
is_global: bool
}
}
......@@ -942,7 +953,7 @@ impl fmt::Display for MemoryLocation {
}
write!(f, "]")
}
&MemoryLocation::Symbolic{ref base, ref label} => {
&MemoryLocation::Symbolic{ref base, ref label, ..} => {
if base.is_some() {
write!(f, "{}({})", label, base.as_ref().unwrap())
} else {
......
......@@ -43,6 +43,7 @@ lazy_static! {
pub static ref INTERNAL_TYPES : Vec<P<MuType>> = vec![
ADDRESS_TYPE.clone(),
UINT1_TYPE.clone(),
UINT8_TYPE.clone(),
UINT16_TYPE.clone(),
UINT32_TYPE.clone(),
......
......@@ -1025,8 +1025,8 @@ impl ASMCodeGen {
result_str.push(')');
loc_cursor += 1;
},
Value_::Memory(MemoryLocation::Symbolic{ref base, ref label}) => {
if base.is_some() && base.as_ref().unwrap().id() == x86_64::RIP.id() {
Value_::Memory(MemoryLocation::Symbolic{ref base, ref label, is_global}) => {
if base.is_some() && base.as_ref().unwrap().id() == x86_64::RIP.id() && is_global {
// pc relative address
let pic_symbol = pic_symbol(label.clone());
// let pic_symbol = symbol(label.clone()); // not sure if we need this
......@@ -2649,6 +2649,75 @@ impl CodeGenerator for ASMCodeGen {
false
)
}
// unpack low data - interleave low byte
fn emit_punpckldq_f64_mem128(&mut self, dest: Reg, src: Mem) {
trace!("emit: punpckldq {} {} -> {}", src, dest, dest);
let (mem, mut uses) = self.prepare_mem(src, 9 + 1);
let (reg, id2, loc2) = self.prepare_fpreg(dest, 9 + 1 + mem.len() + 1);
let asm = format!("punpckldq {},{}", mem, reg);
// memory op won't use a fpreg, we insert the use of fpreg
uses.insert(id2, vec![loc2.clone()]);
self.add_asm_inst(
asm,
linked_hashmap!{
id2 => vec![loc2]
},
uses,
true
)
}
// substract packed double-fp
fn emit_subpd_f64_mem128 (&mut self, dest: Reg, src: Mem) {
trace!("emit: subpd {} {} -> {}", src, dest, dest);
let (mem, mut uses) = self.prepare_mem(src, 5 + 1);
let (reg, id2, loc2) = self.prepare_fpreg(dest, 5 + 1 + mem.len() + 1);
let asm = format!("subpd {},{}", mem, reg);
uses.insert(id2, vec![loc2.clone()]);
self.add_asm_inst(
asm,
linked_hashmap!{
id2 => vec![loc2]
},
uses,
true
)
}
// packed double-fp horizontal add
fn emit_haddpd_f64_f64 (&mut self, op1: Reg, op2: Reg) {
trace!("emit: haddpd {} {} -> {}", op2, op1, op1);
let (reg1, id1, loc1) = self.prepare_fpreg(op1, 6 + 1);
let (reg2, id2, loc2) = self.prepare_fpreg(op2, 6 + 1 + reg1.len() + 1);
let asm = format!("haddpd {},{}", reg1, reg2);
self.add_asm_inst(
asm,
linked_hashmap!{
id2 => vec![loc2.clone()]
},
{
if id1 == id2 {
linked_hashmap!{id1 => vec![loc1, loc2]}
} else {
linked_hashmap!{
id1 => vec![loc1],
id2 => vec![loc2]
}
}
},
false
)
}
}
fn create_emit_directory(vm: &VM) {
......@@ -2671,8 +2740,6 @@ pub fn emit_code(fv: &mut MuFunctionVersion, vm: &VM) {
let compiled_funcs = vm.compiled_funcs().read().unwrap();
let cf = compiled_funcs.get(&fv.id()).unwrap().read().unwrap();
let code = cf.mc.as_ref().unwrap().emit();
// create 'emit' directory
create_emit_directory(vm);
......@@ -2684,12 +2751,85 @@ pub fn emit_code(fv: &mut MuFunctionVersion, vm: &VM) {
Ok(file) => file
};
// constants in text section
file.write("\t.text\n".as_bytes()).unwrap();
file.write("\t.align 8\n".as_bytes()).unwrap();
for (id, constant) in cf.consts.iter() {
let mem = cf.const_mem.get(id).unwrap();
write_const(&mut file, constant.clone(), mem.clone());
}
// write code
let code = cf.mc.as_ref().unwrap().emit();
match file.write_all(code.as_slice()) {
Err(why) => panic!("couldn'd write to file {}: {}", file_path.to_str().unwrap(), why),
Ok(_) => info!("emit code to {}", file_path.to_str().unwrap())
}
}
fn write_const(f: &mut File, constant: P<Value>, loc: P<Value>) {
use std::io::Write;
// label
let label = match loc.v {
Value_::Memory(MemoryLocation::Symbolic{ref label, ..}) => label.clone(),
_ => panic!("expecing a symbolic memory location for constant {}, found {}", constant, loc)
};
f.write_fmt(format_args!("{}:\n", symbol(label))).unwrap();
write_const_value(f, constant);
}
fn write_const_value(f: &mut File, constant: P<Value>) {
use std::mem;
use std::io::Write;
let ref ty = constant.ty;
let inner = match constant.v {
Value_::Constant(ref c) => c,
_ => panic!("expected constant, found {}", constant)
};
match inner {
&Constant::Int(val) => {
let len = ty.get_int_length().unwrap();
match len {
8 => f.write_fmt(format_args!("\t.byte {}\n", val as u8 )).unwrap(),
16 => f.write_fmt(format_args!("\t.word {}\n", val as u16)).unwrap(),
32 => f.write_fmt(format_args!("\t.long {}\n", val as u32)).unwrap(),
64 => f.write_fmt(format_args!("\t.quad {}\n", val as u64)).unwrap(),
_ => panic!("unimplemented int length: {}", len)
}
}
&Constant::Float(val) => {
let bytes: [u8; 4] = unsafe {mem::transmute(val)};
f.write("\t.long ".as_bytes()).unwrap();
f.write(&bytes).unwrap();
f.write("\n".as_bytes()).unwrap();
}
&Constant::Double(val) => {
let bytes: [u8; 8] = unsafe {mem::transmute(val)};
f.write("\t.quad ".as_bytes()).unwrap();
f.write(&bytes).unwrap();
f.write("\n".as_bytes()).unwrap();
}
&Constant::NullRef => {
f.write_fmt(format_args!("\t.quad 0\n")).unwrap()
}
&Constant::ExternSym(ref name) => {
f.write_fmt(format_args!("\t.quad {}\n", name)).unwrap()
}
&Constant::List(ref vals) => {
for val in vals {
write_const_value(f, val.clone())
}
},
_ => unimplemented!()
}
}
pub fn emit_context(vm: &VM) {
use std::path;
use std::io::prelude::*;
......
......@@ -184,4 +184,13 @@ pub trait CodeGenerator {
// fp conversion
fn emit_cvtsi2sd_f64_r (&mut self, dest: Reg, src: Reg);
fn emit_cvtsd2si_r_f64 (&mut self, dest: Reg, src: Reg);
// used for unsigned int to fp conversion
// unpack low data - interleave low byte
fn emit_punpckldq_f64_mem128(&mut self, dest: Reg, src: Mem);
// substract packed double-fp
fn emit_subpd_f64_mem128 (&mut self, dest: Reg, src: Mem);
// packed double-fp horizontal add
fn emit_haddpd_f64_f64 (&mut self, dest: Reg, src: Reg);
}
......@@ -24,6 +24,60 @@ use compiler::frame::Frame;
use std::collections::HashMap;
use std::any::Any;
lazy_static! {
pub static ref LONG_4_TYPE : P<MuType> = P(
MuType::new(new_internal_id(), MuType_::mustruct(Mu("long_4"), vec![UINT32_TYPE.clone(); 4]))
);
pub static ref UITOFP_C0 : P<Value> = P(Value{
hdr: MuEntityHeader::named(new_internal_id(), Mu("UITOFP_C0")),
ty : LONG_4_TYPE.clone(),
v : Value_::Constant(Constant::List(vec![
P(Value{
hdr: MuEntityHeader::unnamed(new_internal_id()),
ty: UINT32_TYPE.clone(),
v : Value_::Constant(Constant::Int(1127219200u64))
}),
P(Value{
hdr: MuEntityHeader::unnamed(new_internal_id()),
ty: UINT32_TYPE.clone(),
v : Value_::Constant(Constant::Int(1160773632u64))
}),
P(Value{
hdr: MuEntityHeader::unnamed(new_internal_id()),
ty: UINT32_TYPE.clone(),
v : Value_::Constant(Constant::Int(0u64))
}),
P(Value{
hdr: MuEntityHeader::unnamed(new_internal_id()),
ty: UINT32_TYPE.clone(),
v : Value_::Constant(Constant::Int(0u64))
})
]))
});
pub static ref QUAD_2_TYPE : P<MuType> = P(
MuType::new(new_internal_id(), MuType_::mustruct(Mu("quad_2"), vec![UINT64_TYPE.clone(); 2]))
);
pub static ref UITOFP_C1 : P<Value> = P(Value{
hdr: MuEntityHeader::named(new_internal_id(), Mu("UITOFP_C1")),
ty : QUAD_2_TYPE.clone(),
v : Value_::Constant(Constant::List(vec![
P(Value{
hdr: MuEntityHeader::unnamed(new_internal_id()),
ty: UINT64_TYPE.clone(),
v : Value_::Constant(Constant::Int(4841369599423283200u64))
}),
P(Value{
hdr: MuEntityHeader::unnamed(new_internal_id()),
ty: UINT64_TYPE.clone(),
v : Value_::Constant(Constant::Int(4985484787499139072u64))
})
]))
});
}
pub struct InstructionSelection {
name: &'static str,
backend: Box<CodeGenerator>,
......@@ -35,7 +89,10 @@ pub struct InstructionSelection {
// key: block id, val: callsite that names the block as exception block
current_exn_callsites: HashMap<MuID, Vec<ValueLocation>>,
// key: block id, val: block location
current_exn_blocks: HashMap<MuID, ValueLocation>
current_exn_blocks: HashMap<MuID, ValueLocation>,
current_constants: HashMap<MuID, P<Value>>,
current_constants_locs: HashMap<MuID, P<Value>>
}
impl <'a> InstructionSelection {
......@@ -51,7 +108,10 @@ impl <'a> InstructionSelection {
current_func_start: None,
// key: block id, val: callsite that names the block as exception block
current_exn_callsites: HashMap::new(),
current_exn_blocks: HashMap::new()
current_exn_blocks: HashMap::new(),
current_constants: HashMap::new(),
current_constants_locs: HashMap::new()
}
}
......@@ -1173,7 +1233,30 @@ impl <'a> InstructionSelection {
panic!("unexpected op (expected fpreg): {}", op)
}
}
op::ConvOp::UITOFP => {
let tmp_res = self.get_result_value(node);
if self.match_ireg(op) {
let tmp_op = self.emit_ireg(op, f_content, f_context, vm);
// movd/movq op -> res
self.backend.emit_mov_fpr_r64(&tmp_res, &tmp_op);
// punpckldq UITOFP_C0, tmp_res -> tmp_res
// (interleaving low bytes: xmm = xmm[0] mem[0] xmm[1] mem[1]
let mem_c0 = self.get_mem_for_const(UITOFP_C0.clone(), vm);
self.backend.emit_punpckldq_f64_mem128(&tmp_res, &mem_c0);
// subpd UITOFP_C1, tmp_res -> tmp_res
let mem_c1 = self.get_mem_for_const(UITOFP_C1.clone(), vm);
self.backend.emit_subpd_f64_mem128(&tmp_res, &mem_c1);
// haddpd tmp_res, tmp_res -> tmp_res
self.backend.emit_haddpd_f64_f64(&tmp_res, &tmp_res);
} else {
panic!("unexpected op (expected ireg): {}", op)
}
}
_ => unimplemented!()
}
}
......@@ -2715,7 +2798,8 @@ impl <'a> InstructionSelection {
ty: types::get_referent_ty(&pv.ty).unwrap(),
v: Value_::Memory(MemoryLocation::Symbolic {
base: Some(x86_64::RIP.clone()),
label: pv.name().unwrap()
label: pv.name().unwrap(),
is_global: true,
})
})
} else if cfg!(target_os = "linux") {
......@@ -2727,7 +2811,8 @@ impl <'a> InstructionSelection {
ty: pv.ty.clone(),
v: Value_::Memory(MemoryLocation::Symbolic {
base: Some(x86_64::RIP.clone()),
label: pv.name().unwrap()
label: pv.name().unwrap(),
is_global: true
})
});
......@@ -3244,6 +3329,36 @@ impl <'a> InstructionSelection {
self.current_callsite_id += 1;
ret
}
fn get_mem_for_const(&mut self, val: P<Value>, vm: &VM) -> P<Value> {
let id = val.id();
if self.current_constants.contains_key(&id) {
self.current_constants.get(&id).unwrap().clone()
} else {
let const_value_loc = vm.allocate_const(val.clone());
let const_mem_val = match const_value_loc {
ValueLocation::Relocatable(_, ref name) => {
P(Value {
hdr: MuEntityHeader::unnamed(vm.next_id()),
ty : ADDRESS_TYPE.clone(),
v : Value_::Memory(MemoryLocation::Symbolic {
base: Some(x86_64::RIP.clone()),
label: name.clone(),
is_global: false
})
})
}
_ => panic!("expecting relocatable location, found {}", const_value_loc)
};
self.current_constants.insert(id, val.clone());
self.current_constants_locs.insert(id, const_mem_val.clone());
const_mem_val
}
}
}
impl CompilerPass for InstructionSelection {
......@@ -3268,6 +3383,9 @@ impl CompilerPass for InstructionSelection {
self.current_callsite_id = 0;
self.current_exn_callsites.clear();
self.current_exn_blocks.clear();
self.current_constants.clear();
self.current_constants_locs.clear();
// prologue (get arguments from entry block first)
let entry_block = func_ver.content.as_ref().unwrap().get_entry_block();
......@@ -3347,15 +3465,9 @@ impl CompilerPass for InstructionSelection {
}
}
let compiled_func = CompiledFunction {
func_id: func.func_id,
func_ver_id: func.id(),
temps: HashMap::new(),
mc: Some(mc),
frame: frame,
start: self.current_func_start.take().unwrap(),
end: func_end
};
let compiled_func = CompiledFunction::new(func.func_id, func.id(), mc,
self.current_constants.clone(), self.current_constants_locs.clone(),
frame, self.current_func_start.take().unwrap(), func_end);
vm.add_compiled_func(compiled_func);
}
......
use ast::ir::*;
use ast::ptr::*;
use compiler::frame::*;
use runtime::ValueLocation;
......@@ -12,7 +13,10 @@ pub struct CompiledFunction {
pub func_ver_id: MuID,
// assumes one temporary maps to one register
pub temps: HashMap<MuID, MuID>,
pub temps : HashMap<MuID, MuID>,
pub consts: HashMap<MuID, P<Value>>,
pub const_mem: HashMap<MuID, P<Value>>,
// not emitting this
pub mc: Option<Box<MachineCode + Send + Sync>>,
......@@ -27,19 +31,39 @@ const CF_SERIALIZE_FIELDS : usize = 6;
impl Encodable for CompiledFunction {
fn encode<S: Encoder> (&self, s: &mut S) -> Result<(), S::Error> {
s.emit_struct("CompiledFunction", CF_SERIALIZE_FIELDS, |s| {
let mut i = 0;
trace!("......serializing func_id");
try!(s.emit_struct_field("func_id", 0, |s| self.func_id.encode(s)));
try!(s.emit_struct_field("func_id", i, |s| self.func_id.encode(s)));
i += 1;
trace!("......serializing func_ver_id");
try!(s.emit_struct_field("func_ver_id", 1, |s| self.func_ver_id.encode(s)));
try!(s.emit_struct_field("func_ver_id", i, |s| self.func_ver_id.encode(s)));
i += 1;
trace!("......serializing temps");
try!(s.emit_struct_field("temps", 2, |s| self.temps.encode(s)));
try!(s.emit_struct_field("temps", i, |s| self.temps.encode(s)));
i += 1;
trace!("......serializing consts");
try!(s.emit_struct_field("consts", i, |s| self.consts.encode(s)));
i += 1;
trace!("......serializing const_mem");
try!(s.emit_struct_field("const_mem", i, |s| self.const_mem.encode(s)));
i += 1;
trace!("......serializing frame");
trace!("{}", self.frame);
try!(s.emit_struct_field("frame", 3, |s| self.frame.encode(s)));
try!(s.emit_struct_field("frame", i, |s| self.frame.encode(s)));
i += 1;
trace!("......serializing start");
try!(s.emit_struct_field("start", 4, |s| self.start.encode(s)));
try!(s.emit_struct_field("start", i, |s| self.start.encode(s)));
i += 1;
trace!("......serializing end");
try!(s.emit_struct_field("end", 5, |s| self.end.encode(s)));
try!(s.emit_struct_field("end", i, |s| self.end.encode(s)));
Ok(())
})
......@@ -49,23 +73,38 @@ impl Encodable for CompiledFunction {
impl Decodable for CompiledFunction {
fn decode<D: Decoder>(d: &mut D) -> Result<CompiledFunction, D::Error> {
d.read_struct("CompiledFunction", CF_SERIALIZE_FIELDS, |d| {
let mut i = 0;
let func_id =
try!(d.read_struct_field("func_id", 0, |d| Decodable::decode(d)));
try!(d.read_struct_field("func_id", i, |d| Decodable::decode(d)));
i += 1;
let func_ver_id =
try!(d.read_struct_field("func_ver_id", 1, |d| Decodable::decode(d)));
try!(d.read_struct_field("func_ver_id", i, |d| Decodable::decode(d)));
i += 1;
let temps =
try!(d.read_struct_field("temps", 2, |d| Decodable::decode(d)));
try!(d.read_struct_field("temps", i, |d| Decodable::decode(d)));
i += 1;
let consts =
try!(d.read_struct_field("consts", i, |d| Decodable::decode(d)));
i += 1;
let const_mem =
try!(d.read_struct_field("const_mem", i, |d| Decodable::decode(d)));
i += 1;
let frame =
try!(d.read_struct_field("frame", 3, |d| Decodable::decode(d)));
try!(d.read_struct_field("frame", i, |d| Decodable::decode(d)));
i += 1;
let start =
try!(d.read_struct_field("start", 4, |d| Decodable::decode(d)));
try!(d.read_struct_field("start", i, |d| Decodable::decode(d)));
i += 1;
let end =
try!(d.read_struct_field("end", 5, |d| Decodable::decode(d)));
try!(d.read_struct_field("end", i, |d| Decodable::decode(d)));
Ok(CompiledFunction{
func_id: func_id,
func_ver_id: func_ver_id,
temps: temps,
consts: consts,
const_mem: const_mem,
mc: None,
frame: frame,
start: start,
......@@ -76,6 +115,22 @@ impl Decodable for CompiledFunction {
}
impl CompiledFunction {
pub fn new(func_id: MuID, fv_id: MuID, mc: Box<MachineCode + Send + Sync>,
constants: HashMap<MuID, P<Value>>, constant_locs: HashMap<MuID, P<Value>>,
frame: Frame, start_loc: ValueLocation, end_loc: ValueLocation) -> CompiledFunction {
CompiledFunction {
func_id: func_id,
func_ver_id: fv_id,
temps: HashMap::new(),
consts: constants,
const_mem: constant_locs,
mc: Some(mc),
frame: frame,
start: start_loc,
end: end_loc
}
}
pub fn mc(&self) -> &Box<MachineCode + Send + Sync> {
match self.mc {
Some(ref mc) => mc,
......
......@@ -175,6 +175,13 @@ impl ValueLocation {
&ValueLocation::Relocatable(_, ref symbol) => resolve_symbol(symbol.clone())
}
}
pub fn to_relocatable(&self) -> MuName {
match self {
&ValueLocation::Relocatable(_, ref name) => name.clone(),
_ => panic!("expecting Relocatable location, found {}", self)
}
}