From 641a210ff688d8101688b32650d09c270d381b02 Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Sun, 7 Jul 2019 18:08:38 +0200 Subject: [PATCH] Implement most 128bit binops --- example/std_example.rs | 20 +----- src/abi.rs | 2 +- src/base.rs | 151 +++++++++++++++++++---------------------- src/codegen_i128.rs | 107 +++++++++++++++++++++++++++++ src/lib.rs | 1 + 5 files changed, 178 insertions(+), 103 deletions(-) create mode 100644 src/codegen_i128.rs diff --git a/example/std_example.rs b/example/std_example.rs index ae77616a83d..c0ec0bb5b73 100644 --- a/example/std_example.rs +++ b/example/std_example.rs @@ -3,23 +3,5 @@ use std::io::Write; fn main() { - assert_eq!((1u128 + 2) as u16, 3); -} - -#[derive(PartialEq)] -enum LoopState { - Continue(()), - Break(()) -} - -pub enum Instruction { - Increment, - Loop, -} - -fn map(a: Option<(u8, Box)>) -> Option> { - match a { - None => None, - Some((_, instr)) => Some(instr), - } + assert_eq!(1u128 + 2, 3); } diff --git a/src/abi.rs b/src/abi.rs index b7e7d0a830a..c5dbe6dffa9 100644 --- a/src/abi.rs +++ b/src/abi.rs @@ -846,7 +846,7 @@ pub fn codegen_drop<'a, 'tcx: 'a>( ); drop_place.write_place_ref(fx, arg_place); let arg_value = arg_place.to_cvalue(fx); - crate::abi::codegen_call_inner( + codegen_call_inner( fx, None, drop_fn_ty, diff --git a/src/base.rs b/src/base.rs index 88964a34ed1..8821a629519 100644 --- a/src/base.rs +++ b/src/base.rs @@ -831,18 +831,8 @@ pub fn trans_int_binop<'a, 'tcx: 'a>( ); } - if lhs.layout().ty == fx.tcx.types.u128 || lhs.layout().ty == fx.tcx.types.i128 { - if out_ty == fx.tcx.types.bool { - let layout = fx.layout_of(fx.tcx.types.bool); - let val = fx.bcx.ins().iconst(types::I8, 0); - return CValue::by_val(val, layout); - } else { - let layout = fx.layout_of(out_ty); - let a = fx.bcx.ins().iconst(types::I64, 42); - let b = fx.bcx.ins().iconst(types::I64, 0); - let val = fx.bcx.ins().iconcat(a, b); - return CValue::by_val(val, layout); - } + if let Some(res) = crate::codegen_i128::maybe_codegen(fx, bin_op, false, signed, lhs, rhs, out_ty) { + return res; } binop_match! { @@ -894,79 +884,74 @@ pub fn trans_checked_int_binop<'a, 'tcx: 'a>( let lhs = in_lhs.load_scalar(fx); let rhs = in_rhs.load_scalar(fx); - let (res, has_overflow) = if in_lhs.layout().ty == fx.tcx.types.u128 || in_lhs.layout().ty == fx.tcx.types.i128 { - match (bin_op, signed) { - _ => { - let a = fx.bcx.ins().iconst(types::I64, 42); - let b = fx.bcx.ins().iconst(types::I64, 0); - (fx.bcx.ins().iconcat(a, b), fx.bcx.ins().bconst(types::B1, false)) - } + + if let Some(res) = crate::codegen_i128::maybe_codegen(fx, bin_op, true, signed, in_lhs, in_rhs, out_ty) { + return res; + } + + let (res, has_overflow) = match bin_op { + BinOp::Add => { + /*let (val, c_out) = fx.bcx.ins().iadd_cout(lhs, rhs); + (val, c_out)*/ + // FIXME(CraneStation/cranelift#849) legalize iadd_cout for i8 and i16 + let val = fx.bcx.ins().iadd(lhs, rhs); + let has_overflow = if !signed { + fx.bcx.ins().icmp(IntCC::UnsignedLessThan, val, lhs) + } else { + let rhs_is_negative = fx.bcx.ins().icmp_imm(IntCC::SignedLessThan, rhs, 0); + let slt = fx.bcx.ins().icmp(IntCC::SignedLessThan, val, lhs); + fx.bcx.ins().bxor(rhs_is_negative, slt) + }; + (val, has_overflow) } - } else { - match bin_op { - BinOp::Add => { - /*let (val, c_out) = fx.bcx.ins().iadd_cout(lhs, rhs); - (val, c_out)*/ - // FIXME(CraneStation/cranelift#849) legalize iadd_cout for i8 and i16 - let val = fx.bcx.ins().iadd(lhs, rhs); - let has_overflow = if !signed { - fx.bcx.ins().icmp(IntCC::UnsignedLessThan, val, lhs) - } else { - let rhs_is_negative = fx.bcx.ins().icmp_imm(IntCC::SignedLessThan, rhs, 0); - let slt = fx.bcx.ins().icmp(IntCC::SignedLessThan, val, lhs); - fx.bcx.ins().bxor(rhs_is_negative, slt) - }; - (val, has_overflow) - } - BinOp::Sub => { - /*let (val, b_out) = fx.bcx.ins().isub_bout(lhs, rhs); - (val, b_out)*/ - // FIXME(CraneStation/cranelift#849) legalize isub_bout for i8 and i16 - let val = fx.bcx.ins().isub(lhs, rhs); - let has_overflow = if !signed { - fx.bcx.ins().icmp(IntCC::UnsignedGreaterThan, val, lhs) - } else { - let rhs_is_negative = fx.bcx.ins().icmp_imm(IntCC::SignedLessThan, rhs, 0); - let sgt = fx.bcx.ins().icmp(IntCC::SignedGreaterThan, val, lhs); - fx.bcx.ins().bxor(rhs_is_negative, sgt) - }; - (val, has_overflow) - } - BinOp::Mul => { - let val = fx.bcx.ins().imul(lhs, rhs); - /*let val_hi = if !signed { - fx.bcx.ins().umulhi(lhs, rhs) - } else { - fx.bcx.ins().smulhi(lhs, rhs) - }; - let has_overflow = fx.bcx.ins().icmp_imm(IntCC::NotEqual, val_hi, 0);*/ - // TODO: check for overflow - let has_overflow = fx.bcx.ins().bconst(types::B1, false); - (val, has_overflow) - } - BinOp::Shl => { - let val = fx.bcx.ins().ishl(lhs, rhs); - // TODO: check for overflow - let has_overflow = fx.bcx.ins().bconst(types::B1, false); - (val, has_overflow) - } - BinOp::Shr => { - let val = if !signed { - fx.bcx.ins().ushr(lhs, rhs) - } else { - fx.bcx.ins().sshr(lhs, rhs) - }; - // TODO: check for overflow - let has_overflow = fx.bcx.ins().bconst(types::B1, false); - (val, has_overflow) - } - _ => bug!( - "binop {:?} on checked int/uint lhs: {:?} rhs: {:?}", - bin_op, - in_lhs, - in_rhs - ), + BinOp::Sub => { + /*let (val, b_out) = fx.bcx.ins().isub_bout(lhs, rhs); + (val, b_out)*/ + // FIXME(CraneStation/cranelift#849) legalize isub_bout for i8 and i16 + let val = fx.bcx.ins().isub(lhs, rhs); + let has_overflow = if !signed { + fx.bcx.ins().icmp(IntCC::UnsignedGreaterThan, val, lhs) + } else { + let rhs_is_negative = fx.bcx.ins().icmp_imm(IntCC::SignedLessThan, rhs, 0); + let sgt = fx.bcx.ins().icmp(IntCC::SignedGreaterThan, val, lhs); + fx.bcx.ins().bxor(rhs_is_negative, sgt) + }; + (val, has_overflow) } + BinOp::Mul => { + let val = fx.bcx.ins().imul(lhs, rhs); + /*let val_hi = if !signed { + fx.bcx.ins().umulhi(lhs, rhs) + } else { + fx.bcx.ins().smulhi(lhs, rhs) + }; + let has_overflow = fx.bcx.ins().icmp_imm(IntCC::NotEqual, val_hi, 0);*/ + // TODO: check for overflow + let has_overflow = fx.bcx.ins().bconst(types::B1, false); + (val, has_overflow) + } + BinOp::Shl => { + let val = fx.bcx.ins().ishl(lhs, rhs); + // TODO: check for overflow + let has_overflow = fx.bcx.ins().bconst(types::B1, false); + (val, has_overflow) + } + BinOp::Shr => { + let val = if !signed { + fx.bcx.ins().ushr(lhs, rhs) + } else { + fx.bcx.ins().sshr(lhs, rhs) + }; + // TODO: check for overflow + let has_overflow = fx.bcx.ins().bconst(types::B1, false); + (val, has_overflow) + } + _ => bug!( + "binop {:?} on checked int/uint lhs: {:?} rhs: {:?}", + bin_op, + in_lhs, + in_rhs + ), }; let has_overflow = fx.bcx.ins().bint(types::I8, has_overflow); diff --git a/src/codegen_i128.rs b/src/codegen_i128.rs new file mode 100644 index 00000000000..ded6b597f26 --- /dev/null +++ b/src/codegen_i128.rs @@ -0,0 +1,107 @@ +//! Replaces 128-bit operators with lang item calls + +use crate::prelude::*; + +pub fn maybe_codegen<'a, 'tcx>( + fx: &mut FunctionCx<'a, 'tcx, impl Backend>, + bin_op: BinOp, + checked: bool, + is_signed: bool, + lhs: CValue<'tcx>, + rhs: CValue<'tcx>, + out_ty: Ty<'tcx>, +) -> Option> { + if lhs.layout().ty != fx.tcx.types.u128 && lhs.layout().ty != fx.tcx.types.i128 { + return None; + } + + let lhs_val = lhs.load_scalar(fx); + let rhs_val = rhs.load_scalar(fx); + + match bin_op { + BinOp::Add | BinOp::Sub | BinOp::BitAnd | BinOp::BitOr | BinOp::BitXor => return None, + BinOp::Offset => unreachable!("offset should only be used on pointers, not 128bit ints"), + BinOp::Mul => { + let res = if checked { + if is_signed { + let oflow_place = CPlace::new_stack_slot(fx, fx.tcx.types.i32); + let oflow_addr = oflow_place.to_addr(fx); + let oflow_addr = CValue::by_val(oflow_addr, fx.layout_of(fx.tcx.mk_mut_ptr(fx.tcx.types.i32))); + let val = fx.easy_call("__muloti4", &[lhs, rhs, oflow_addr], fx.tcx.types.i128); + let val = val.load_scalar(fx); + let oflow = oflow_place.to_cvalue(fx).load_scalar(fx); + let oflow = fx.bcx.ins().icmp_imm(IntCC::NotEqual, oflow, 0); + let oflow = fx.bcx.ins().bint(types::I8, oflow); + CValue::by_val_pair(val, oflow, fx.layout_of(out_ty)) + } else { + // FIXME implement it + let out_layout = fx.layout_of(out_ty); + return Some(crate::trap::trap_unreachable_ret_value(fx, out_layout, format!("unimplemented 128bit checked binop unsigned mul"))); + } + } else { + let val_ty = if is_signed { fx.tcx.types.i128 } else { fx.tcx.types.u128 }; + fx.easy_call("__multi3", &[lhs, rhs], val_ty) + }; + return Some(res); + } + BinOp::Div => { + let res = if checked { + // FIXME implement it + let out_layout = fx.layout_of(out_ty); + return Some(crate::trap::trap_unreachable_ret_value(fx, out_layout, format!("unimplemented 128bit checked binop div"))); + } else { + if is_signed { + fx.easy_call("__divti3", &[lhs, rhs], fx.tcx.types.i128) + } else { + fx.easy_call("__udivti3", &[lhs, rhs], fx.tcx.types.u128) + } + }; + return Some(res); + } + BinOp::Rem => { + let res = if checked { + // FIXME implement it + let out_layout = fx.layout_of(out_ty); + return Some(crate::trap::trap_unreachable_ret_value(fx, out_layout, format!("unimplemented 128bit checked binop rem"))); + } else { + if is_signed { + fx.easy_call("__modti3", &[lhs, rhs], fx.tcx.types.i128) + } else { + fx.easy_call("__umodti3", &[lhs, rhs], fx.tcx.types.u128) + } + }; + return Some(res); + } + BinOp::Lt | BinOp::Le | BinOp::Eq | BinOp::Ge | BinOp::Gt | BinOp::Ne => { + assert!(!checked); + let (lhs_lsb, lhs_msb) = fx.bcx.ins().isplit(lhs_val); + let (rhs_lsb, rhs_msb) = fx.bcx.ins().isplit(rhs_val); + let res = match (bin_op, is_signed) { + (BinOp::Eq, _) => { + let lsb_eq = fx.bcx.ins().icmp(IntCC::Equal, lhs_lsb, rhs_lsb); + let msb_eq = fx.bcx.ins().icmp(IntCC::Equal, lhs_msb, rhs_msb); + fx.bcx.ins().band(lsb_eq, msb_eq) + } + (BinOp::Ne, _) => { + let lsb_ne = fx.bcx.ins().icmp(IntCC::NotEqual, lhs_lsb, rhs_lsb); + let msb_ne = fx.bcx.ins().icmp(IntCC::NotEqual, lhs_msb, rhs_msb); + fx.bcx.ins().bor(lsb_ne, msb_ne) + } + _ => { + // FIXME implement it + let out_layout = fx.layout_of(out_ty); + return Some(crate::trap::trap_unreachable_ret_value(fx, out_layout, format!("unimplemented 128bit binop {:?}", bin_op))); + }, + }; + + let res = fx.bcx.ins().bint(types::I8, res); + let res = CValue::by_val(res, fx.layout_of(fx.tcx.types.bool)); + return Some(res); + } + BinOp::Shl | BinOp::Shr => { + // FIXME implement it + let out_layout = fx.layout_of(out_ty); + return Some(crate::trap::trap_unreachable_ret_value(fx, out_layout, format!("unimplemented 128bit binop {:?}", bin_op))); + } + } +} diff --git a/src/lib.rs b/src/lib.rs index c6f77954a86..9bd985e3dc9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -34,6 +34,7 @@ mod analyze; mod archive; mod base; +mod codegen_i128; mod common; mod constant; mod debuginfo;