From 076b91f8ad9ce5bbebc5690b6ef9a1ff162f5efb Mon Sep 17 00:00:00 2001 From: Daniel Micay Date: Wed, 7 Aug 2013 15:40:09 -0400 Subject: [PATCH] add intrinsics for checked overflow add/sub/mul --- src/librustc/middle/trans/base.rs | 54 +++ src/librustc/middle/trans/build.rs | 8 +- src/librustc/middle/trans/builder.rs | 4 +- src/librustc/middle/trans/common.rs | 2 +- src/librustc/middle/trans/foreign.rs | 49 +++ src/librustc/middle/trans/type_use.rs | 16 + src/librustc/middle/typeck/check/mod.rs | 33 ++ src/libstd/num/num.rs | 449 +++++++++++++++++++++++- src/libstd/prelude.rs | 2 +- src/libstd/unstable/intrinsics.rs | 54 +++ 10 files changed, 663 insertions(+), 8 deletions(-) diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index 762f6953bb2..d1b8da85440 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -2740,6 +2740,60 @@ pub fn declare_intrinsics(llmod: ModuleRef) -> HashMap<&'static str, ValueRef> { ifn!("llvm.bswap.i32",[Type::i32()], Type::i32()); ifn!("llvm.bswap.i64",[Type::i64()], Type::i64()); + ifn!("llvm.sadd.with.overflow.i8", + [Type::i8(), Type::i8()], Type::struct_([Type::i8(), Type::i1()], false)); + ifn!("llvm.sadd.with.overflow.i16", + [Type::i16(), Type::i16()], Type::struct_([Type::i16(), Type::i1()], false)); + ifn!("llvm.sadd.with.overflow.i32", + [Type::i32(), Type::i32()], Type::struct_([Type::i32(), Type::i1()], false)); + ifn!("llvm.sadd.with.overflow.i64", + [Type::i64(), Type::i64()], Type::struct_([Type::i64(), Type::i1()], false)); + + ifn!("llvm.uadd.with.overflow.i8", + [Type::i8(), Type::i8()], Type::struct_([Type::i8(), Type::i1()], false)); + ifn!("llvm.uadd.with.overflow.i16", + [Type::i16(), Type::i16()], Type::struct_([Type::i16(), Type::i1()], false)); + ifn!("llvm.uadd.with.overflow.i32", + [Type::i32(), Type::i32()], Type::struct_([Type::i32(), Type::i1()], false)); + ifn!("llvm.uadd.with.overflow.i64", + [Type::i64(), Type::i64()], Type::struct_([Type::i64(), Type::i1()], false)); + + ifn!("llvm.ssub.with.overflow.i8", + [Type::i8(), Type::i8()], Type::struct_([Type::i8(), Type::i1()], false)); + ifn!("llvm.ssub.with.overflow.i16", + [Type::i16(), Type::i16()], Type::struct_([Type::i16(), Type::i1()], false)); + ifn!("llvm.ssub.with.overflow.i32", + [Type::i32(), Type::i32()], Type::struct_([Type::i32(), Type::i1()], false)); + ifn!("llvm.ssub.with.overflow.i64", + [Type::i64(), Type::i64()], Type::struct_([Type::i64(), Type::i1()], false)); + + ifn!("llvm.usub.with.overflow.i8", + [Type::i8(), Type::i8()], Type::struct_([Type::i8(), Type::i1()], false)); + ifn!("llvm.usub.with.overflow.i16", + [Type::i16(), Type::i16()], Type::struct_([Type::i16(), Type::i1()], false)); + ifn!("llvm.usub.with.overflow.i32", + [Type::i32(), Type::i32()], Type::struct_([Type::i32(), Type::i1()], false)); + ifn!("llvm.usub.with.overflow.i64", + [Type::i64(), Type::i64()], Type::struct_([Type::i64(), Type::i1()], false)); + + ifn!("llvm.smul.with.overflow.i8", + [Type::i8(), Type::i8()], Type::struct_([Type::i8(), Type::i1()], false)); + ifn!("llvm.smul.with.overflow.i16", + [Type::i16(), Type::i16()], Type::struct_([Type::i16(), Type::i1()], false)); + ifn!("llvm.smul.with.overflow.i32", + [Type::i32(), Type::i32()], Type::struct_([Type::i32(), Type::i1()], false)); + ifn!("llvm.smul.with.overflow.i64", + [Type::i64(), Type::i64()], Type::struct_([Type::i64(), Type::i1()], false)); + + ifn!("llvm.umul.with.overflow.i8", + [Type::i8(), Type::i8()], Type::struct_([Type::i8(), Type::i1()], false)); + ifn!("llvm.umul.with.overflow.i16", + [Type::i16(), Type::i16()], Type::struct_([Type::i16(), Type::i1()], false)); + ifn!("llvm.umul.with.overflow.i32", + [Type::i32(), Type::i32()], Type::struct_([Type::i32(), Type::i1()], false)); + ifn!("llvm.umul.with.overflow.i64", + [Type::i64(), Type::i64()], Type::struct_([Type::i64(), Type::i1()], false)); + return intrinsics; } diff --git a/src/librustc/middle/trans/build.rs b/src/librustc/middle/trans/build.rs index c8e2a17c3b5..dab97056921 100644 --- a/src/librustc/middle/trans/build.rs +++ b/src/librustc/middle/trans/build.rs @@ -714,9 +714,11 @@ pub fn ExtractValue(cx: @mut Block, AggVal: ValueRef, Index: uint) -> ValueRef { } } -pub fn InsertValue(cx: @mut Block, AggVal: ValueRef, EltVal: ValueRef, Index: uint) { - if cx.unreachable { return; } - B(cx).insert_value(AggVal, EltVal, Index) +pub fn InsertValue(cx: @mut Block, AggVal: ValueRef, EltVal: ValueRef, Index: uint) -> ValueRef { + unsafe { + if cx.unreachable { return llvm::LLVMGetUndef(Type::nil().to_ref()); } + B(cx).insert_value(AggVal, EltVal, Index) + } } pub fn IsNull(cx: @mut Block, Val: ValueRef) -> ValueRef { diff --git a/src/librustc/middle/trans/builder.rs b/src/librustc/middle/trans/builder.rs index 8f48c00b8d6..5c216b2e143 100644 --- a/src/librustc/middle/trans/builder.rs +++ b/src/librustc/middle/trans/builder.rs @@ -861,11 +861,11 @@ impl Builder { } pub fn insert_value(&self, agg_val: ValueRef, elt: ValueRef, - idx: uint) { + idx: uint) -> ValueRef { self.count_insn("insertvalue"); unsafe { llvm::LLVMBuildInsertValue(self.llbuilder, agg_val, elt, idx as c_uint, - noname()); + noname()) } } diff --git a/src/librustc/middle/trans/common.rs b/src/librustc/middle/trans/common.rs index 40a83eb9770..96616cbe533 100644 --- a/src/librustc/middle/trans/common.rs +++ b/src/librustc/middle/trans/common.rs @@ -238,7 +238,7 @@ impl FunctionContext { } pub fn out_arg_pos(&self) -> uint { - assert!(self.has_immediate_return_value); + assert!(!self.has_immediate_return_value); 0u } diff --git a/src/librustc/middle/trans/foreign.rs b/src/librustc/middle/trans/foreign.rs index b1e600b9d73..2bf483ee3a0 100644 --- a/src/librustc/middle/trans/foreign.rs +++ b/src/librustc/middle/trans/foreign.rs @@ -550,6 +550,24 @@ pub fn trans_intrinsic(ccx: @mut CrateContext, Ret(bcx, Call(bcx, llfn, args.slice(0, num_args))); } + fn with_overflow_instrinsic(bcx: @mut Block, name: &'static str) { + let first_real_arg = bcx.fcx.arg_pos(0u); + let a = get_param(bcx.fcx.llfn, first_real_arg); + let b = get_param(bcx.fcx.llfn, first_real_arg + 1); + let llfn = bcx.ccx().intrinsics.get_copy(&name); + + // convert `i1` to a `bool`, and write to the out parameter + let val = Call(bcx, llfn, [a, b]); + let result = ExtractValue(bcx, val, 0); + let overflow = ZExt(bcx, ExtractValue(bcx, val, 1), Type::bool()); + let retptr = get_param(bcx.fcx.llfn, bcx.fcx.out_arg_pos()); + let ret = Load(bcx, retptr); + let ret = InsertValue(bcx, ret, result, 0); + let ret = InsertValue(bcx, ret, overflow, 1); + Store(bcx, ret, retptr); + RetVoid(bcx) + } + fn memcpy_intrinsic(bcx: @mut Block, name: &'static str, tp_ty: ty::t, sizebits: u8) { let ccx = bcx.ccx(); let lltp_ty = type_of::type_of(ccx, tp_ty); @@ -944,6 +962,37 @@ pub fn trans_intrinsic(ccx: @mut CrateContext, "bswap16" => simple_llvm_intrinsic(bcx, "llvm.bswap.i16", 1), "bswap32" => simple_llvm_intrinsic(bcx, "llvm.bswap.i32", 1), "bswap64" => simple_llvm_intrinsic(bcx, "llvm.bswap.i64", 1), + + "i8_add_with_overflow" => with_overflow_instrinsic(bcx, "llvm.sadd.with.overflow.i8"), + "i16_add_with_overflow" => with_overflow_instrinsic(bcx, "llvm.sadd.with.overflow.i16"), + "i32_add_with_overflow" => with_overflow_instrinsic(bcx, "llvm.sadd.with.overflow.i32"), + "i64_add_with_overflow" => with_overflow_instrinsic(bcx, "llvm.sadd.with.overflow.i64"), + + "u8_add_with_overflow" => with_overflow_instrinsic(bcx, "llvm.uadd.with.overflow.i8"), + "u16_add_with_overflow" => with_overflow_instrinsic(bcx, "llvm.uadd.with.overflow.i16"), + "u32_add_with_overflow" => with_overflow_instrinsic(bcx, "llvm.uadd.with.overflow.i32"), + "u64_add_with_overflow" => with_overflow_instrinsic(bcx, "llvm.uadd.with.overflow.i64"), + + "i8_sub_with_overflow" => with_overflow_instrinsic(bcx, "llvm.ssub.with.overflow.i8"), + "i16_sub_with_overflow" => with_overflow_instrinsic(bcx, "llvm.ssub.with.overflow.i16"), + "i32_sub_with_overflow" => with_overflow_instrinsic(bcx, "llvm.ssub.with.overflow.i32"), + "i64_sub_with_overflow" => with_overflow_instrinsic(bcx, "llvm.ssub.with.overflow.i64"), + + "u8_sub_with_overflow" => with_overflow_instrinsic(bcx, "llvm.usub.with.overflow.i8"), + "u16_sub_with_overflow" => with_overflow_instrinsic(bcx, "llvm.usub.with.overflow.i16"), + "u32_sub_with_overflow" => with_overflow_instrinsic(bcx, "llvm.usub.with.overflow.i32"), + "u64_sub_with_overflow" => with_overflow_instrinsic(bcx, "llvm.usub.with.overflow.i64"), + + "i8_mul_with_overflow" => with_overflow_instrinsic(bcx, "llvm.smul.with.overflow.i8"), + "i16_mul_with_overflow" => with_overflow_instrinsic(bcx, "llvm.smul.with.overflow.i16"), + "i32_mul_with_overflow" => with_overflow_instrinsic(bcx, "llvm.smul.with.overflow.i32"), + "i64_mul_with_overflow" => with_overflow_instrinsic(bcx, "llvm.smul.with.overflow.i64"), + + "u8_mul_with_overflow" => with_overflow_instrinsic(bcx, "llvm.umul.with.overflow.i8"), + "u16_mul_with_overflow" => with_overflow_instrinsic(bcx, "llvm.umul.with.overflow.i16"), + "u32_mul_with_overflow" => with_overflow_instrinsic(bcx, "llvm.umul.with.overflow.i32"), + "u64_mul_with_overflow" => with_overflow_instrinsic(bcx, "llvm.umul.with.overflow.i64"), + _ => { // Could we make this an enum rather than a string? does it get // checked earlier? diff --git a/src/librustc/middle/trans/type_use.rs b/src/librustc/middle/trans/type_use.rs index f25bf011f5d..74661098348 100644 --- a/src/librustc/middle/trans/type_use.rs +++ b/src/librustc/middle/trans/type_use.rs @@ -168,6 +168,22 @@ pub fn type_uses_for(ccx: @mut CrateContext, fn_id: def_id, n_tps: uint) "bswap16" | "bswap32" | "bswap64" => 0, + + "i8_add_with_overflow" | "u8_add_with_overflow" | + "i16_add_with_overflow" | "u16_add_with_overflow" | + "i32_add_with_overflow" | "u32_add_with_overflow" | + "i64_add_with_overflow" | "u64_add_with_overflow" => 0, + + "i8_sub_with_overflow" | "u8_sub_with_overflow" | + "i16_sub_with_overflow" | "u16_sub_with_overflow" | + "i32_sub_with_overflow" | "u32_sub_with_overflow" | + "i64_sub_with_overflow" | "u64_sub_with_overflow" => 0, + + "i8_mul_with_overflow" | "u8_mul_with_overflow" | + "i16_mul_with_overflow" | "u16_mul_with_overflow" | + "i32_mul_with_overflow" | "u32_mul_with_overflow" | + "i64_mul_with_overflow" | "u64_mul_with_overflow" => 0, + // would be cool to make these an enum instead of // strings! _ => fail!("unknown intrinsic in type_use") diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index 8bc32412568..1b4d3ccbb34 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -3647,6 +3647,39 @@ pub fn check_intrinsic_type(ccx: @mut CrateCtxt, it: @ast::foreign_item) { "bswap16" => (0, ~[ ty::mk_i16() ], ty::mk_i16()), "bswap32" => (0, ~[ ty::mk_i32() ], ty::mk_i32()), "bswap64" => (0, ~[ ty::mk_i64() ], ty::mk_i64()), + + "i8_add_with_overflow" | "i8_sub_with_overflow" | "i8_mul_with_overflow" => + (0, ~[ty::mk_i8(), ty::mk_i8()], + ty::mk_tup(tcx, ~[ty::mk_i8(), ty::mk_bool()])), + + "i16_add_with_overflow" | "i16_sub_with_overflow" | "i16_mul_with_overflow" => + (0, ~[ty::mk_i16(), ty::mk_i16()], + ty::mk_tup(tcx, ~[ty::mk_i16(), ty::mk_bool()])), + + "i32_add_with_overflow" | "i32_sub_with_overflow" | "i32_mul_with_overflow" => + (0, ~[ty::mk_i32(), ty::mk_i32()], + ty::mk_tup(tcx, ~[ty::mk_i32(), ty::mk_bool()])), + + "i64_add_with_overflow" | "i64_sub_with_overflow" | "i64_mul_with_overflow" => + (0, ~[ty::mk_i64(), ty::mk_i64()], + ty::mk_tup(tcx, ~[ty::mk_i64(), ty::mk_bool()])), + + "u8_add_with_overflow" | "u8_sub_with_overflow" | "u8_mul_with_overflow" => + (0, ~[ty::mk_u8(), ty::mk_u8()], + ty::mk_tup(tcx, ~[ty::mk_u8(), ty::mk_bool()])), + + "u16_add_with_overflow" | "u16_sub_with_overflow" | "u16_mul_with_overflow" => + (0, ~[ty::mk_u16(), ty::mk_u16()], + ty::mk_tup(tcx, ~[ty::mk_u16(), ty::mk_bool()])), + + "u32_add_with_overflow" | "u32_sub_with_overflow" | "u32_mul_with_overflow"=> + (0, ~[ty::mk_u32(), ty::mk_u32()], + ty::mk_tup(tcx, ~[ty::mk_u32(), ty::mk_bool()])), + + "u64_add_with_overflow" | "u64_sub_with_overflow" | "u64_mul_with_overflow" => + (0, ~[ty::mk_u64(), ty::mk_u64()], + ty::mk_tup(tcx, ~[ty::mk_u64(), ty::mk_bool()])), + ref other => { tcx.sess.span_err(it.span, fmt!("unrecognized intrinsic function: `%s`", diff --git a/src/libstd/num/num.rs b/src/libstd/num/num.rs index bbadf1caca2..f0c432ce2eb 100644 --- a/src/libstd/num/num.rs +++ b/src/libstd/num/num.rs @@ -18,7 +18,9 @@ use cmp::{Eq, ApproxEq, Ord}; use ops::{Add, Sub, Mul, Div, Rem, Neg}; use ops::{Not, BitAnd, BitOr, BitXor, Shl, Shr}; -use option::Option; +use option::{Option, Some, None}; +#[cfg(not(stage0))] +use unstable::intrinsics; pub mod strconv; @@ -516,6 +518,414 @@ impl Saturating for u16 {} impl Saturating for u32 {} impl Saturating for u64 {} +pub trait CheckedAdd: Add { + fn checked_add(&self, v: &Self) -> Option; +} + +#[cfg(not(stage0))] +impl CheckedAdd for i8 { + #[inline] + fn checked_add(&self, v: &i8) -> Option { + unsafe { + let (x, y) = intrinsics::i8_add_with_overflow(*self, *v); + if y { None } else { Some(x) } + } + } +} + +#[cfg(not(stage0))] +impl CheckedAdd for i16 { + #[inline] + fn checked_add(&self, v: &i16) -> Option { + unsafe { + let (x, y) = intrinsics::i16_add_with_overflow(*self, *v); + if y { None } else { Some(x) } + } + } +} + +#[cfg(not(stage0))] +impl CheckedAdd for i32 { + #[inline] + fn checked_add(&self, v: &i32) -> Option { + unsafe { + let (x, y) = intrinsics::i32_add_with_overflow(*self, *v); + if y { None } else { Some(x) } + } + } +} + +#[cfg(not(stage0))] +impl CheckedAdd for i64 { + #[inline] + fn checked_add(&self, v: &i64) -> Option { + unsafe { + let (x, y) = intrinsics::i64_add_with_overflow(*self, *v); + if y { None } else { Some(x) } + } + } +} + +#[cfg(not(stage0), target_word_size = "32")] +impl CheckedAdd for int { + #[inline] + fn checked_add(&self, v: &int) -> Option { + unsafe { + let (x, y) = intrinsics::i32_add_with_overflow(*self as i32, *v as i32); + if y { None } else { Some(x as int) } + } + } +} + +#[cfg(not(stage0), target_word_size = "64")] +impl CheckedAdd for int { + #[inline] + fn checked_add(&self, v: &int) -> Option { + unsafe { + let (x, y) = intrinsics::i64_add_with_overflow(*self as i64, *v as i64); + if y { None } else { Some(x as int) } + } + } +} + +#[cfg(not(stage0))] +impl CheckedAdd for u8 { + #[inline] + fn checked_add(&self, v: &u8) -> Option { + unsafe { + let (x, y) = intrinsics::u8_add_with_overflow(*self, *v); + if y { None } else { Some(x) } + } + } +} + +#[cfg(not(stage0))] +impl CheckedAdd for u16 { + #[inline] + fn checked_add(&self, v: &u16) -> Option { + unsafe { + let (x, y) = intrinsics::u16_add_with_overflow(*self, *v); + if y { None } else { Some(x) } + } + } +} + +#[cfg(not(stage0))] +impl CheckedAdd for u32 { + #[inline] + fn checked_add(&self, v: &u32) -> Option { + unsafe { + let (x, y) = intrinsics::u32_add_with_overflow(*self, *v); + if y { None } else { Some(x) } + } + } +} + +#[cfg(not(stage0))] +impl CheckedAdd for u64 { + #[inline] + fn checked_add(&self, v: &u64) -> Option { + unsafe { + let (x, y) = intrinsics::u64_add_with_overflow(*self, *v); + if y { None } else { Some(x) } + } + } +} + +#[cfg(not(stage0), target_word_size = "32")] +impl CheckedAdd for uint { + #[inline] + fn checked_add(&self, v: &uint) -> Option { + unsafe { + let (x, y) = intrinsics::u32_add_with_overflow(*self as u32, *v as u32); + if y { None } else { Some(x as uint) } + } + } +} + +#[cfg(not(stage0), target_word_size = "64")] +impl CheckedAdd for uint { + #[inline] + fn checked_add(&self, v: &uint) -> Option { + unsafe { + let (x, y) = intrinsics::u64_add_with_overflow(*self as u64, *v as u64); + if y { None } else { Some(x as uint) } + } + } +} + +pub trait CheckedSub: Sub { + fn checked_sub(&self, v: &Self) -> Option; +} + +#[cfg(not(stage0))] +impl CheckedSub for i8 { + #[inline] + fn checked_sub(&self, v: &i8) -> Option { + unsafe { + let (x, y) = intrinsics::i8_sub_with_overflow(*self, *v); + if y { None } else { Some(x) } + } + } +} + +#[cfg(not(stage0))] +impl CheckedSub for i16 { + #[inline] + fn checked_sub(&self, v: &i16) -> Option { + unsafe { + let (x, y) = intrinsics::i16_sub_with_overflow(*self, *v); + if y { None } else { Some(x) } + } + } +} + +#[cfg(not(stage0))] +impl CheckedSub for i32 { + #[inline] + fn checked_sub(&self, v: &i32) -> Option { + unsafe { + let (x, y) = intrinsics::i32_sub_with_overflow(*self, *v); + if y { None } else { Some(x) } + } + } +} + +#[cfg(not(stage0))] +impl CheckedSub for i64 { + #[inline] + fn checked_sub(&self, v: &i64) -> Option { + unsafe { + let (x, y) = intrinsics::i64_sub_with_overflow(*self, *v); + if y { None } else { Some(x) } + } + } +} + +#[cfg(not(stage0), target_word_size = "32")] +impl CheckedSub for int { + #[inline] + fn checked_sub(&self, v: &int) -> Option { + unsafe { + let (x, y) = intrinsics::i32_sub_with_overflow(*self as i32, *v as i32); + if y { None } else { Some(x as int) } + } + } +} + +#[cfg(not(stage0), target_word_size = "64")] +impl CheckedSub for int { + #[inline] + fn checked_sub(&self, v: &int) -> Option { + unsafe { + let (x, y) = intrinsics::i64_sub_with_overflow(*self as i64, *v as i64); + if y { None } else { Some(x as int) } + } + } +} + +#[cfg(not(stage0))] +impl CheckedSub for u8 { + #[inline] + fn checked_sub(&self, v: &u8) -> Option { + unsafe { + let (x, y) = intrinsics::u8_sub_with_overflow(*self, *v); + if y { None } else { Some(x) } + } + } +} + +#[cfg(not(stage0))] +impl CheckedSub for u16 { + #[inline] + fn checked_sub(&self, v: &u16) -> Option { + unsafe { + let (x, y) = intrinsics::u16_sub_with_overflow(*self, *v); + if y { None } else { Some(x) } + } + } +} + +#[cfg(not(stage0))] +impl CheckedSub for u32 { + #[inline] + fn checked_sub(&self, v: &u32) -> Option { + unsafe { + let (x, y) = intrinsics::u32_sub_with_overflow(*self, *v); + if y { None } else { Some(x) } + } + } +} + +#[cfg(not(stage0))] +impl CheckedSub for u64 { + #[inline] + fn checked_sub(&self, v: &u64) -> Option { + unsafe { + let (x, y) = intrinsics::u64_sub_with_overflow(*self, *v); + if y { None } else { Some(x) } + } + } +} + +#[cfg(not(stage0), target_word_size = "32")] +impl CheckedSub for uint { + #[inline] + fn checked_sub(&self, v: &uint) -> Option { + unsafe { + let (x, y) = intrinsics::u32_sub_with_overflow(*self as u32, *v as u32); + if y { None } else { Some(x as uint) } + } + } +} + +#[cfg(not(stage0), target_word_size = "64")] +impl CheckedSub for uint { + #[inline] + fn checked_sub(&self, v: &uint) -> Option { + unsafe { + let (x, y) = intrinsics::u64_sub_with_overflow(*self as u64, *v as u64); + if y { None } else { Some(x as uint) } + } + } +} + +pub trait CheckedMul: Mul { + fn checked_mul(&self, v: &Self) -> Option; +} + +#[cfg(not(stage0))] +impl CheckedMul for i8 { + #[inline] + fn checked_mul(&self, v: &i8) -> Option { + unsafe { + let (x, y) = intrinsics::i8_mul_with_overflow(*self, *v); + if y { None } else { Some(x) } + } + } +} + +#[cfg(not(stage0))] +impl CheckedMul for i16 { + #[inline] + fn checked_mul(&self, v: &i16) -> Option { + unsafe { + let (x, y) = intrinsics::i16_mul_with_overflow(*self, *v); + if y { None } else { Some(x) } + } + } +} + +#[cfg(not(stage0))] +impl CheckedMul for i32 { + #[inline] + fn checked_mul(&self, v: &i32) -> Option { + unsafe { + let (x, y) = intrinsics::i32_mul_with_overflow(*self, *v); + if y { None } else { Some(x) } + } + } +} + +#[cfg(not(stage0))] +impl CheckedMul for i64 { + #[inline] + fn checked_mul(&self, v: &i64) -> Option { + unsafe { + let (x, y) = intrinsics::i64_mul_with_overflow(*self, *v); + if y { None } else { Some(x) } + } + } +} + +#[cfg(not(stage0), target_word_size = "32")] +impl CheckedMul for int { + #[inline] + fn checked_mul(&self, v: &int) -> Option { + unsafe { + let (x, y) = intrinsics::i32_mul_with_overflow(*self as i32, *v as i32); + if y { None } else { Some(x as int) } + } + } +} + +#[cfg(not(stage0), target_word_size = "64")] +impl CheckedMul for int { + #[inline] + fn checked_mul(&self, v: &int) -> Option { + unsafe { + let (x, y) = intrinsics::i64_mul_with_overflow(*self as i64, *v as i64); + if y { None } else { Some(x as int) } + } + } +} + +#[cfg(not(stage0))] +impl CheckedMul for u8 { + #[inline] + fn checked_mul(&self, v: &u8) -> Option { + unsafe { + let (x, y) = intrinsics::u8_mul_with_overflow(*self, *v); + if y { None } else { Some(x) } + } + } +} + +#[cfg(not(stage0))] +impl CheckedMul for u16 { + #[inline] + fn checked_mul(&self, v: &u16) -> Option { + unsafe { + let (x, y) = intrinsics::u16_mul_with_overflow(*self, *v); + if y { None } else { Some(x) } + } + } +} + +#[cfg(not(stage0))] +impl CheckedMul for u32 { + #[inline] + fn checked_mul(&self, v: &u32) -> Option { + unsafe { + let (x, y) = intrinsics::u32_mul_with_overflow(*self, *v); + if y { None } else { Some(x) } + } + } +} + +#[cfg(not(stage0))] +impl CheckedMul for u64 { + #[inline] + fn checked_mul(&self, v: &u64) -> Option { + unsafe { + let (x, y) = intrinsics::u64_mul_with_overflow(*self, *v); + if y { None } else { Some(x) } + } + } +} + +#[cfg(not(stage0), target_word_size = "32")] +impl CheckedMul for uint { + #[inline] + fn checked_mul(&self, v: &uint) -> Option { + unsafe { + let (x, y) = intrinsics::u32_mul_with_overflow(*self as u32, *v as u32); + if y { None } else { Some(x as uint) } + } + } +} + +#[cfg(not(stage0), target_word_size = "64")] +impl CheckedMul for uint { + #[inline] + fn checked_mul(&self, v: &uint) -> Option { + unsafe { + let (x, y) = intrinsics::u64_mul_with_overflow(*self as u64, *v as u64); + if y { None } else { Some(x as uint) } + } + } +} + /// Helper function for testing numeric operations #[cfg(test)] pub fn test_num(ten: T, two: T) { @@ -534,6 +944,8 @@ pub fn test_num(ten: T, two: T) { #[cfg(test)] mod tests { + use prelude::*; + use uint; use super::*; macro_rules! test_cast_20( @@ -639,4 +1051,39 @@ mod tests { assert_eq!(max_value.saturating_sub(-max_value), max_value); assert_eq!((max_value-2).saturating_sub(-1), max_value-1); } + + #[test] + fn test_checked_add() { + let five_less = uint::max_value - 5; + assert_eq!(five_less.checked_add(&0), Some(uint::max_value - 5)); + assert_eq!(five_less.checked_add(&1), Some(uint::max_value - 4)); + assert_eq!(five_less.checked_add(&2), Some(uint::max_value - 3)); + assert_eq!(five_less.checked_add(&3), Some(uint::max_value - 2)); + assert_eq!(five_less.checked_add(&4), Some(uint::max_value - 1)); + assert_eq!(five_less.checked_add(&5), Some(uint::max_value)); + assert_eq!(five_less.checked_add(&6), None); + assert_eq!(five_less.checked_add(&7), None); + } + + #[test] + fn test_checked_sub() { + assert_eq!(5u.checked_sub(&0), Some(5)); + assert_eq!(5u.checked_sub(&1), Some(4)); + assert_eq!(5u.checked_sub(&2), Some(3)); + assert_eq!(5u.checked_sub(&3), Some(2)); + assert_eq!(5u.checked_sub(&4), Some(1)); + assert_eq!(5u.checked_sub(&5), Some(0)); + assert_eq!(5u.checked_sub(&6), None); + assert_eq!(5u.checked_sub(&7), None); + } + + #[test] + fn test_checked_mul() { + let third = uint::max_value / 3; + assert_eq!(third.checked_mul(&0), Some(0)); + assert_eq!(third.checked_mul(&1), Some(third)); + assert_eq!(third.checked_mul(&2), Some(third * 2)); + assert_eq!(third.checked_mul(&3), Some(third * 3)); + assert_eq!(third.checked_mul(&4), None); + } } diff --git a/src/libstd/prelude.rs b/src/libstd/prelude.rs index 9a8737f4dee..93e14be582b 100644 --- a/src/libstd/prelude.rs +++ b/src/libstd/prelude.rs @@ -53,7 +53,7 @@ pub use iter::Times; pub use iterator::Extendable; pub use iterator::{Iterator, DoubleEndedIterator}; pub use iterator::{ClonableIterator, OrdIterator}; -pub use num::{Num, NumCast}; +pub use num::{Num, NumCast, CheckedAdd, CheckedSub, CheckedMul}; pub use num::{Orderable, Signed, Unsigned, Round}; pub use num::{Algebraic, Trigonometric, Exponential, Hyperbolic}; pub use num::{Integer, Fractional, Real, RealExt}; diff --git a/src/libstd/unstable/intrinsics.rs b/src/libstd/unstable/intrinsics.rs index c60edad3dbd..9f69ee47e9b 100644 --- a/src/libstd/unstable/intrinsics.rs +++ b/src/libstd/unstable/intrinsics.rs @@ -428,6 +428,60 @@ extern "rust-intrinsic" { pub fn bswap16(x: i16) -> i16; pub fn bswap32(x: i32) -> i32; pub fn bswap64(x: i64) -> i64; + + #[cfg(not(stage0))] + pub fn i8_add_with_overflow(x: i8, y: i8) -> (i8, bool); + #[cfg(not(stage0))] + pub fn i16_add_with_overflow(x: i16, y: i16) -> (i16, bool); + #[cfg(not(stage0))] + pub fn i32_add_with_overflow(x: i32, y: i32) -> (i32, bool); + #[cfg(not(stage0))] + pub fn i64_add_with_overflow(x: i64, y: i64) -> (i64, bool); + + #[cfg(not(stage0))] + pub fn u8_add_with_overflow(x: u8, y: u8) -> (u8, bool); + #[cfg(not(stage0))] + pub fn u16_add_with_overflow(x: u16, y: u16) -> (u16, bool); + #[cfg(not(stage0))] + pub fn u32_add_with_overflow(x: u32, y: u32) -> (u32, bool); + #[cfg(not(stage0))] + pub fn u64_add_with_overflow(x: u64, y: u64) -> (u64, bool); + + #[cfg(not(stage0))] + pub fn i8_sub_with_overflow(x: i8, y: i8) -> (i8, bool); + #[cfg(not(stage0))] + pub fn i16_sub_with_overflow(x: i16, y: i16) -> (i16, bool); + #[cfg(not(stage0))] + pub fn i32_sub_with_overflow(x: i32, y: i32) -> (i32, bool); + #[cfg(not(stage0))] + pub fn i64_sub_with_overflow(x: i64, y: i64) -> (i64, bool); + + #[cfg(not(stage0))] + pub fn u8_sub_with_overflow(x: u8, y: u8) -> (u8, bool); + #[cfg(not(stage0))] + pub fn u16_sub_with_overflow(x: u16, y: u16) -> (u16, bool); + #[cfg(not(stage0))] + pub fn u32_sub_with_overflow(x: u32, y: u32) -> (u32, bool); + #[cfg(not(stage0))] + pub fn u64_sub_with_overflow(x: u64, y: u64) -> (u64, bool); + + #[cfg(not(stage0))] + pub fn i8_mul_with_overflow(x: i8, y: i8) -> (i8, bool); + #[cfg(not(stage0))] + pub fn i16_mul_with_overflow(x: i16, y: i16) -> (i16, bool); + #[cfg(not(stage0))] + pub fn i32_mul_with_overflow(x: i32, y: i32) -> (i32, bool); + #[cfg(not(stage0))] + pub fn i64_mul_with_overflow(x: i64, y: i64) -> (i64, bool); + + #[cfg(not(stage0))] + pub fn u8_mul_with_overflow(x: u8, y: u8) -> (u8, bool); + #[cfg(not(stage0))] + pub fn u16_mul_with_overflow(x: u16, y: u16) -> (u16, bool); + #[cfg(not(stage0))] + pub fn u32_mul_with_overflow(x: u32, y: u32) -> (u32, bool); + #[cfg(not(stage0))] + pub fn u64_mul_with_overflow(x: u64, y: u64) -> (u64, bool); } #[cfg(target_endian = "little")] pub fn to_le16(x: i16) -> i16 { x }