Commit 91515294 authored by Isaac Oscar Gariano's avatar Isaac Oscar Gariano

Merge branch 'develop' of gitlab.anu.edu.au:mu/mu-impl-fast into develop

parents 13805e3a d0129c29
......@@ -29,8 +29,9 @@ are not compliant to Mu spec.
## Building
You will need:
* rust version 1.18 (03fc9d622 2017-06-06)
* rust version 1.19 (0ade33941 2017-07-17)
* clang 4.0+
* cmake 3.8+ (we do not depend on cmake, but some Rust crates use it)
* internet connection (as Rust will download dependencies)
To build Zebu with release build,
......
......@@ -139,6 +139,14 @@ impl MuType {
}
}
/// is this type an integer type?
pub fn is_int(&self) -> bool {
match self.v {
MuType_::Int(_) => true,
_ => false
}
}
/// is this type a floating point type? (float/double)
pub fn is_fp(&self) -> bool {
match self.v {
......
......@@ -1896,6 +1896,48 @@ impl ASMCodeGen {
)
}
/// emits a move instruction (reg64/32 -> fpr)
fn internal_mov_bitcast_fpr_r(&mut self, inst: &str, dest: &P<Value>, src: &P<Value>) {
trace!("emit: {} {} -> {}", inst, src, dest);
let (reg1, id1, loc1) = self.prepare_reg(src, inst.len() + 1);
let (reg2, id2, loc2) = self.prepare_fpreg(dest, inst.len() + 1 + reg1.len() + 1);
let asm = format!("{} {},{}", inst, reg1, reg2);
self.add_asm_inst(
asm,
linked_hashmap!{
id2 => vec![loc2]
},
linked_hashmap!{
id1 => vec![loc1]
},
false
)
}
/// emits a move instruction (fpr -> reg64/32)
fn internal_mov_bitcast_r_fpr(&mut self, inst: &str, dest: &P<Value>, src: &P<Value>) {
trace!("emit: {} {} -> {}", inst, src, dest);
let (reg1, id1, loc1) = self.prepare_fpreg(src, inst.len() + 1);
let (reg2, id2, loc2) = self.prepare_reg(dest, inst.len() + 1 + reg1.len() + 1);
let asm = format!("{} {},{}", inst, reg1, reg2);
self.add_asm_inst(
asm,
linked_hashmap!{
id2 => vec![loc2]
},
linked_hashmap!{
id1 => vec![loc1]
},
false
)
}
/// emits a move instruction (reg -> reg)
fn internal_mov_r_r(&mut self, inst: &str, dest: &P<Value>, src: &P<Value>) {
let len = check_op_len(dest);
......@@ -2241,6 +2283,28 @@ impl ASMCodeGen {
)
}
/// emits a truncate instruction (fpreg -> fpreg)
fn internal_fp_trunc(&mut self, inst: &str, dest: Reg, src: Reg) {
let inst = inst.to_string();
trace!("emit: {} {} -> {}", inst, src, dest);
let (reg1, id1, loc1) = self.prepare_fpreg(src, inst.len() + 1);
let (reg2, id2, loc2) = self.prepare_fpreg(dest, inst.len() + 1 + reg1.len() + 1);
let asm = format!("{} {},{}", inst, reg1, reg2);
self.add_asm_inst(
asm,
linked_hashmap!{
id2 => vec![loc2]
},
linked_hashmap!{
id1 => vec![loc1]
},
false
)
}
/// emits a store instruction to store a spilled register
fn emit_spill_store_gpr(&mut self, dest: Mem, src: Reg) {
self.internal_mov_mem_r("mov", dest, src, true, false)
......@@ -2447,23 +2511,19 @@ impl CodeGenerator for ASMCodeGen {
}
fn emit_mov_fpr_r64(&mut self, dest: Reg, src: Reg) {
trace!("emit: movq {} -> {}", src, dest);
self.internal_mov_bitcast_fpr_r("movq", dest, src)
}
let (reg1, id1, loc1) = self.prepare_reg(src, 5);
let (reg2, id2, loc2) = self.prepare_fpreg(dest, 5 + reg1.len() + 1);
fn emit_mov_fpr_r32(&mut self, dest: Reg, src: Reg) {
self.internal_mov_bitcast_fpr_r("movd", dest, src)
}
let asm = format!("movq {},{}", reg1, reg2);
fn emit_mov_r64_fpr(&mut self, dest: Reg, src: Reg) {
self.internal_mov_bitcast_r_fpr("movq", dest, src)
}
self.add_asm_inst(
asm,
linked_hashmap!{
id2 => vec![loc2]
},
linked_hashmap!{
id1 => vec![loc1]
},
false
)
fn emit_mov_r32_fpr(&mut self, dest: Reg, src: Reg) {
self.internal_mov_bitcast_r_fpr("movd", dest, src)
}
fn emit_mov_r_imm(&mut self, dest: &P<Value>, src: i32) {
......@@ -3508,6 +3568,15 @@ impl CodeGenerator for ASMCodeGen {
self.internal_fpr_to_gpr("cvttss2si", dest, src);
}
// convert - fp trunc
fn emit_cvtsd2ss_f32_f64(&mut self, dest: Reg, src: Reg) {
self.internal_fp_trunc("cvtsd2ss", dest, src)
}
fn emit_cvtss2sd_f64_f32(&mut self, dest: Reg, src: Reg) {
self.internal_fp_trunc("cvtss2sd", dest, src)
}
// unpack low data - interleave low byte
fn emit_punpckldq_f64_mem128(&mut self, dest: Reg, src: Mem) {
trace!("emit: punpckldq {} {} -> {}", src, dest, dest);
......
......@@ -70,8 +70,11 @@ pub trait CodeGenerator {
// mov imm64 to r64
fn emit_mov_r64_imm64(&mut self, dest: Reg, src: i64);
// mov r64 to fpr
// bitcast between int and floatpoint of same length
fn emit_mov_fpr_r64(&mut self, dest: Reg, src: Reg);
fn emit_mov_fpr_r32(&mut self, dest: Reg, src: Reg);
fn emit_mov_r64_fpr(&mut self, dest: Reg, src: Reg);
fn emit_mov_r32_fpr(&mut self, dest: Reg, src: Reg);
fn emit_mov_r_imm(&mut self, dest: Reg, src: i32);
fn emit_mov_r_mem(&mut self, dest: Reg, src: Mem); // load
......@@ -302,6 +305,10 @@ pub trait CodeGenerator {
fn emit_cvtsi2ss_f32_r(&mut self, dest: Reg, src: Reg);
fn emit_cvtss2si_r_f32(&mut self, dest: Reg, src: Reg);
// fp trunc
fn emit_cvtsd2ss_f32_f64(&mut self, dest: Reg, src: Reg);
fn emit_cvtss2sd_f64_f32(&mut self, dest: Reg, src: Reg);
// used for unsigned int to fp conversion
fn emit_cvttsd2si_r_f64(&mut self, dest: Reg, src: Reg);
......
......@@ -1349,7 +1349,94 @@ impl<'a> InstructionSelection {
panic!("expect double or float")
}
}
_ => unimplemented!()
op::ConvOp::FPTRUNC => {
let tmp_res = self.get_result_value(node);
assert!(
self.match_fpreg(op),
"unexpected op (expected fpreg): {}",
op
);
let tmp_op = self.emit_fpreg(op, f_content, f_context, vm);
if from_ty.is_double() && to_ty.is_float() {
self.backend.emit_cvtsd2ss_f32_f64(&tmp_res, &tmp_op);
} else {
panic!(
"FPTRUNC from {} to {} is not supported \
(only support FPTRUNC from double to float)",
from_ty,
to_ty
);
}
}
op::ConvOp::FPEXT => {
let tmp_res = self.get_result_value(node);
assert!(
self.match_fpreg(op),
"unexpected op (expected fpreg): {}",
op
);
let tmp_op = self.emit_fpreg(op, f_content, f_context, vm);
if from_ty.is_float() && to_ty.is_double() {
self.backend.emit_cvtss2sd_f64_f32(&tmp_res, &tmp_op);
} else {
panic!(
"FPEXT from {} to {} is not supported\
(only support FPEXT from float to double)",
from_ty,
to_ty
);
}
}
op::ConvOp::BITCAST => {
let tmp_res = self.get_result_value(node);
let tmp_op = if self.match_fpreg(op) {
self.emit_fpreg(op, f_content, f_context, vm)
} else if self.match_ireg(op) {
self.emit_ireg(op, f_content, f_context, vm)
} else {
panic!("expected op for BITCAST (expected ireg/fpreg): {}", op)
};
let ref from_ty = tmp_op.ty;
let ref to_ty = tmp_res.ty;
let from_ty_size = vm.get_backend_type_size(from_ty.id());
let to_ty_size = vm.get_backend_type_size(to_ty.id());
assert!(
from_ty_size == to_ty_size,
"BITCAST only works between int/fp of same length"
);
assert!(
from_ty_size == 8 || from_ty_size == 4,
"BITCAST only works for int32/float or int64/double"
);
if from_ty.is_fp() && to_ty.is_int() {
if from_ty_size == 8 {
self.backend.emit_mov_r64_fpr(&tmp_res, &tmp_op);
} else if from_ty_size == 4 {
self.backend.emit_mov_r32_fpr(&tmp_res, &tmp_op);
} else {
unreachable!()
}
} else if from_ty.is_int() && to_ty.is_fp() {
if from_ty_size == 8 {
self.backend.emit_mov_fpr_r64(&tmp_res, &tmp_op);
} else if from_ty_size == 4 {
self.backend.emit_mov_fpr_r32(&tmp_res, &tmp_op);
} else {
unreachable!()
}
} else {
panic!(
"expected BITCAST between int and fp,\
found {} and {}",
from_ty,
to_ty
)
}
}
}
}
......
......@@ -311,7 +311,7 @@ pub fn start_trace(
lo_space: Arc<FreeListSpace>
) {
// creates root deque
let (mut worker, stealer) = deque();
let (worker, stealer) = deque();
while !work_stack.is_empty() {
worker.push(work_stack.pop().unwrap());
......
......@@ -104,6 +104,17 @@ fn link_executable_internal(
cc.arg("-lm");
cc.arg("-lpthread");
cc.arg("-lz");
} else if cfg!(target_os = "macos") {
cc.arg("-liconv");
cc.arg("-framework");
cc.arg("Security");
cc.arg("-framework");
cc.arg("CoreFoundation");
cc.arg("-lz");
cc.arg("-lSystem");
cc.arg("-lresolv");
cc.arg("-lc");
cc.arg("-lm");
}
// all the source code
......
......@@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#![allow(unused_macros)]
macro_rules! typedef {
// int, floating point
(($vm: expr) $name: ident = mu_int($len: expr)) => {
......
......@@ -114,3 +114,57 @@ fn truncate_then_call() -> VM {
vm
}
#[test]
fn test_bitcast_f32_to_u32() {
let lib = linkutils::aot::compile_fnc("bitcast_f32_to_u32", &bitcast_f32_to_u32);
unsafe {
use std::f32;
let bitcast_f32_to_u32: libloading::Symbol<unsafe extern "C" fn(f32) -> u32> =
lib.get(b"bitcast_f32_to_u32").unwrap();
let res = bitcast_f32_to_u32(f32::MAX);
println!("bitcast_f32_to_u32(f32::MAX) = {}", res);
assert!(res == 2139095039u32);
let res = bitcast_f32_to_u32(3.1415926f32);
println!("bitcast_f32_to_u32(PI) = {}", res);
assert!(res == 1078530010u32);
}
}
fn bitcast_f32_to_u32() -> VM {
let vm = VM::new();
typedef! ((vm) float = mu_float);
typedef! ((vm) u32 = mu_int(32));
funcsig! ((vm) sig = (float) -> (u32));
funcdecl! ((vm) <sig> bitcast_f32_to_u32);
funcdef! ((vm) <sig> bitcast_f32_to_u32 VERSION bitcast_f32_to_u32_v1);
// blk entry
block! ((vm, bitcast_f32_to_u32_v1) blk_entry);
ssa! ((vm, bitcast_f32_to_u32_v1) <float> f);
ssa! ((vm, bitcast_f32_to_u32_v1) <u32> i);
inst! ((vm, bitcast_f32_to_u32_v1) blk_entry_bitcast:
i = CONVOP (ConvOp::BITCAST) <float u32> f
);
inst! ((vm, bitcast_f32_to_u32_v1) blk_entry_ret:
RET (i)
);
define_block!((vm, bitcast_f32_to_u32_v1) blk_entry(f) {
blk_entry_bitcast, blk_entry_ret
});
define_func_ver!((vm) bitcast_f32_to_u32_v1 (entry: blk_entry) {
blk_entry
});
vm
}
\ No newline at end of file
......@@ -875,3 +875,113 @@ fn fp_arraysum() -> VM {
vm
}
#[test]
fn test_double_to_float() {
let lib = linkutils::aot::compile_fnc("double_to_float", &double_to_float);
unsafe {
use std::f64;
let double_to_float: libloading::Symbol<unsafe extern "C" fn(f64) -> f32> =
lib.get(b"double_to_float").unwrap();
let res = double_to_float(0f64);
println!("double_fo_float(0) = {}", res);
assert!(res == 0f32);
let res = double_to_float(1f64);
println!("double_fo_float(1) = {}", res);
assert!(res == 1f32);
let res = double_to_float(f64::MAX);
println!("double_to_float(f64::MAX) = {}", res);
assert!(res.is_infinite());
}
}
fn double_to_float() -> VM {
let vm = VM::new();
typedef! ((vm) double = mu_double);
typedef! ((vm) float = mu_float);
funcsig! ((vm) sig = (double) -> (float));
funcdecl! ((vm) <sig> double_to_float);
funcdef! ((vm) <sig> double_to_float VERSION double_to_float_v1);
// blk entry
block! ((vm, double_to_float_v1) blk_entry);
ssa! ((vm, double_to_float_v1) <double> d);
ssa! ((vm, double_to_float_v1) <float> f);
inst! ((vm, double_to_float_v1) blk_entry_fptrunc:
f = CONVOP (ConvOp::FPTRUNC) <double float> d
);
inst! ((vm, double_to_float_v1) blk_entry_ret:
RET (f)
);
define_block!((vm, double_to_float_v1) blk_entry(d) {
blk_entry_fptrunc, blk_entry_ret
});
define_func_ver!((vm) double_to_float_v1 (entry: blk_entry) {
blk_entry
});
vm
}
#[test]
fn test_float_to_double() {
let lib = linkutils::aot::compile_fnc("float_to_double", &float_to_double);
unsafe {
let float_to_double: libloading::Symbol<unsafe extern "C" fn(f32) -> f64> =
lib.get(b"float_to_double").unwrap();
let res = float_to_double(0f32);
println!("float_to_double(0) = {}", 0);
assert!(res == 0f64);
let res = float_to_double(1f32);
println!("float_to_double(1) = {}", 0);
assert!(res == 1f64);
}
}
fn float_to_double() -> VM {
let vm = VM::new();
typedef! ((vm) double = mu_double);
typedef! ((vm) float = mu_float);
funcsig! ((vm) sig = (float) -> (double));
funcdecl! ((vm) <sig> float_to_double);
funcdef! ((vm) <sig> float_to_double VERSION float_to_double_v1);
// blk entry
block! ((vm, float_to_double_v1) blk_entry);
ssa! ((vm, float_to_double_v1) <double> d);
ssa! ((vm, float_to_double_v1) <float> f);
inst! ((vm, float_to_double_v1) blk_entry_fpext:
d = CONVOP (ConvOp::FPEXT) <float double> f
);
inst! ((vm, float_to_double_v1) blk_entry_ret:
RET (d)
);
define_block!((vm, float_to_double_v1) blk_entry(f) {
blk_entry_fpext, blk_entry_ret
});
define_func_ver!((vm) float_to_double_v1 (entry: blk_entry) {
blk_entry
});
vm
}
......@@ -24,12 +24,6 @@ macro_rules! assert_type (
)
);
macro_rules! println_type (
($test:expr) => (
println!("{}", $test)
)
);
/// create one of each MuType
fn create_types() -> Vec<P<MuType>> {
let mut types = vec![];
......
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