Commit 8166d516 authored by qinsoon's avatar qinsoon

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

parent 6648dbf0
Pipeline #268 failed with stage
in 29 minutes and 53 seconds
......@@ -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)
}
}
}
pub const PRIMORDIAL_ENTRY : &'static str = "src/runtime/main.c";
......
......@@ -601,6 +601,25 @@ impl <'a> VM {
None => panic!("cannot find const #{}", id)
}
}
pub fn get_const_nocheck(&self, id: MuID) -> Option<P<Value>> {
let const_lock = self.constants.read().unwrap();
match const_lock.get(&id) {
Some(ret) => Some(ret.clone()),
None => None
}
}
#[cfg(feature = "aot")]
pub fn allocate_const(&self, val: P<Value>) -> ValueLocation {
let id = val.id();
let name = match val.name() {
Some(name) => format!("CONST_{}_{}", id, name),
None => format!("CONST_{}", id)
};
ValueLocation::Relocatable(backend::RegGroup::GPR, name)
}
pub fn declare_global(&self, id: MuID, ty: P<MuType>) -> P<Value> {
let global = P(Value{
......
......@@ -251,6 +251,55 @@ fn sitofp() -> VM {
vm
}
#[test]
fn test_uitofp() {
let lib = testutil::compile_fnc("uitofp", &uitofp);
unsafe {
let uitofp : libloading::Symbol<unsafe extern fn(u64) -> f64> = lib.get(b"uitofp").unwrap();
let res = uitofp(0u64);
println!("uitofp(0) = {}", res);
assert!(res == 0f64);
let res = uitofp(1u64);
println!("uitofp(1) = {}", res);
assert!(res == 1f64);
}
}
fn uitofp() -> VM {
let vm = VM::new();
typedef! ((vm) int64 = mu_int(64));
typedef! ((vm) double = mu_double);
funcsig! ((vm) sig = (int64) -> (double));
funcdecl! ((vm) <sig> uitofp);
funcdef! ((vm) <sig> uitofp VERSION uitofp_v1);
// blk entry
block! ((vm, uitofp_v1) blk_entry);
ssa! ((vm, uitofp_v1) <int64> x);
ssa! ((vm, uitofp_v1) <double> res);
inst! ((vm, uitofp_v1) blk_entry_conv:
res = CONVOP (ConvOp::UITOFP) <int64 double> x
);
inst! ((vm, uitofp_v1) blk_entry_ret:
RET (res)
);
define_block!((vm, uitofp_v1) blk_entry(x){
blk_entry_conv, blk_entry_ret
});
define_func_ver!((vm) uitofp_v1 (entry: blk_entry) {blk_entry});
vm
}
#[test]
fn test_fp_arraysum() {
use std::os::raw::c_double;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment