diff --git a/build.rs b/build.rs index e2a63a6..753978d 100644 --- a/build.rs +++ b/build.rs @@ -129,6 +129,16 @@ mod tests { Divsf3, Divdf3, + // int/addsub.rs + AddU128, + AddI128, + AddoU128, + AddoI128, + SubU128, + SubI128, + SuboU128, + SuboI128, + // int/mul.rs Muldi3, Mulodi4, @@ -341,6 +351,242 @@ fn addsf3() { } } + #[derive(Eq, Hash, PartialEq)] + pub struct AddU128 { + a: u128, + b: u128, + c: u128, + } + + impl TestCase for AddU128 { + fn name() -> &'static str { + "u128_add" + } + + fn generate(rng: &mut R) -> Option + where + R: Rng, + Self: Sized, + { + let a = gen_u128(rng); + let b = gen_u128(rng); + let c = a.wrapping_add(b); + + Some(AddU128 { a, b, c }) + } + + fn to_string(&self, buffer: &mut String) { + writeln!( + buffer, + "(({a}, {b}), {c}),", + a = self.a, + b = self.b, + c = self.c + ) + .unwrap(); + } + + fn prologue() -> &'static str { + " +use compiler_builtins::int::addsub::rust_u128_add; + +static TEST_CASES: &[((u128, u128), u128)] = &[ +" + } + + fn epilogue() -> &'static str { + " +]; + +#[test] +fn u128_add() { + for &((a, b), c) in TEST_CASES { + let c_ = rust_u128_add(a, b); + assert_eq!(((a, b), c), ((a, b), c_)); + } +} +" + } + } + + #[derive(Eq, Hash, PartialEq)] + pub struct AddI128 { + a: i128, + b: i128, + c: i128, + } + + impl TestCase for AddI128 { + fn name() -> &'static str { + "i128_add" + } + + fn generate(rng: &mut R) -> Option + where + R: Rng, + Self: Sized, + { + let a = gen_i128(rng); + let b = gen_i128(rng); + let c = a.wrapping_add(b); + + Some(AddI128 { a, b, c }) + } + + fn to_string(&self, buffer: &mut String) { + writeln!( + buffer, + "(({a}, {b}), {c}),", + a = self.a, + b = self.b, + c = self.c + ) + .unwrap(); + } + + fn prologue() -> &'static str { + " +use compiler_builtins::int::addsub::rust_i128_add; + +static TEST_CASES: &[((i128, i128), i128)] = &[ +" + } + + fn epilogue() -> &'static str { + " +]; + +#[test] +fn i128_add() { + for &((a, b), c) in TEST_CASES { + let c_ = rust_i128_add(a, b); + assert_eq!(((a, b), c), ((a, b), c_)); + } +} +" + } + } + + #[derive(Eq, Hash, PartialEq)] + pub struct AddoU128 { + a: u128, + b: u128, + c: u128, + d: bool, + } + + impl TestCase for AddoU128 { + fn name() -> &'static str { + "u128_addo" + } + + fn generate(rng: &mut R) -> Option + where + R: Rng, + Self: Sized, + { + let a = gen_u128(rng); + let b = gen_u128(rng); + let (c, d) = a.overflowing_add(b); + + Some(AddoU128 { a, b, c, d }) + } + + fn to_string(&self, buffer: &mut String) { + writeln!( + buffer, + "(({a}, {b}), ({c}, {d})),", + a = self.a, + b = self.b, + c = self.c, + d = self.d + ) + .unwrap(); + } + + fn prologue() -> &'static str { + " +use compiler_builtins::int::addsub::rust_u128_addo; + +static TEST_CASES: &[((u128, u128), (u128, bool))] = &[ +" + } + + fn epilogue() -> &'static str { + " +]; + +#[test] +fn u128_addo() { + for &((a, b), (c, d)) in TEST_CASES { + let (c_, d_) = rust_u128_addo(a, b); + assert_eq!(((a, b), (c, d)), ((a, b), (c_, d_))); + } +} +" + } + } + + #[derive(Eq, Hash, PartialEq)] + pub struct AddoI128 { + a: i128, + b: i128, + c: i128, + d: bool, + } + + impl TestCase for AddoI128 { + fn name() -> &'static str { + "i128_addo" + } + + fn generate(rng: &mut R) -> Option + where + R: Rng, + Self: Sized, + { + let a = gen_i128(rng); + let b = gen_i128(rng); + let (c, d) = a.overflowing_add(b); + + Some(AddoI128 { a, b, c, d }) + } + + fn to_string(&self, buffer: &mut String) { + writeln!( + buffer, + "(({a}, {b}), ({c}, {d})),", + a = self.a, + b = self.b, + c = self.c, + d = self.d + ) + .unwrap(); + } + + fn prologue() -> &'static str { + " +use compiler_builtins::int::addsub::rust_i128_addo; + +static TEST_CASES: &[((i128, i128), (i128, bool))] = &[ +" + } + + fn epilogue() -> &'static str { + " +]; + +#[test] +fn i128_addo() { + for &((a, b), (c, d)) in TEST_CASES { + let (c_, d_) = rust_i128_addo(a, b); + assert_eq!(((a, b), (c, d)), ((a, b), (c_, d_))); + } +} +" + } + } + #[derive(Eq, Hash, PartialEq)] pub struct Ashldi3 { a: u64, @@ -3229,6 +3475,242 @@ fn subsf3() { } } + #[derive(Eq, Hash, PartialEq)] + pub struct SubU128 { + a: u128, + b: u128, + c: u128, + } + + impl TestCase for SubU128 { + fn name() -> &'static str { + "u128_sub" + } + + fn generate(rng: &mut R) -> Option + where + R: Rng, + Self: Sized, + { + let a = gen_u128(rng); + let b = gen_u128(rng); + let c = a.wrapping_sub(b); + + Some(SubU128 { a, b, c }) + } + + fn to_string(&self, buffer: &mut String) { + writeln!( + buffer, + "(({a}, {b}), {c}),", + a = self.a, + b = self.b, + c = self.c + ) + .unwrap(); + } + + fn prologue() -> &'static str { + " +use compiler_builtins::int::addsub::rust_u128_sub; + +static TEST_CASES: &[((u128, u128), u128)] = &[ +" + } + + fn epilogue() -> &'static str { + " +]; + +#[test] +fn u128_sub() { + for &((a, b), c) in TEST_CASES { + let c_ = rust_u128_sub(a, b); + assert_eq!(((a, b), c), ((a, b), c_)); + } +} +" + } + } + + #[derive(Eq, Hash, PartialEq)] + pub struct SubI128 { + a: i128, + b: i128, + c: i128, + } + + impl TestCase for SubI128 { + fn name() -> &'static str { + "i128_sub" + } + + fn generate(rng: &mut R) -> Option + where + R: Rng, + Self: Sized, + { + let a = gen_i128(rng); + let b = gen_i128(rng); + let c = a.wrapping_sub(b); + + Some(SubI128 { a, b, c }) + } + + fn to_string(&self, buffer: &mut String) { + writeln!( + buffer, + "(({a}, {b}), {c}),", + a = self.a, + b = self.b, + c = self.c + ) + .unwrap(); + } + + fn prologue() -> &'static str { + " +use compiler_builtins::int::addsub::rust_i128_sub; + +static TEST_CASES: &[((i128, i128), i128)] = &[ +" + } + + fn epilogue() -> &'static str { + " +]; + +#[test] +fn i128_sub() { + for &((a, b), c) in TEST_CASES { + let c_ = rust_i128_sub(a, b); + assert_eq!(((a, b), c), ((a, b), c_)); + } +} +" + } + } + + #[derive(Eq, Hash, PartialEq)] + pub struct SuboU128 { + a: u128, + b: u128, + c: u128, + d: bool, + } + + impl TestCase for SuboU128 { + fn name() -> &'static str { + "u128_subo" + } + + fn generate(rng: &mut R) -> Option + where + R: Rng, + Self: Sized, + { + let a = gen_u128(rng); + let b = gen_u128(rng); + let (c, d) = a.overflowing_sub(b); + + Some(SuboU128 { a, b, c, d }) + } + + fn to_string(&self, buffer: &mut String) { + writeln!( + buffer, + "(({a}, {b}), ({c}, {d})),", + a = self.a, + b = self.b, + c = self.c, + d = self.d + ) + .unwrap(); + } + + fn prologue() -> &'static str { + " +use compiler_builtins::int::addsub::rust_u128_subo; + +static TEST_CASES: &[((u128, u128), (u128, bool))] = &[ +" + } + + fn epilogue() -> &'static str { + " +]; + +#[test] +fn u128_subo() { + for &((a, b), (c, d)) in TEST_CASES { + let (c_, d_) = rust_u128_subo(a, b); + assert_eq!(((a, b), (c, d)), ((a, b), (c_, d_))); + } +} +" + } + } + + #[derive(Eq, Hash, PartialEq)] + pub struct SuboI128 { + a: i128, + b: i128, + c: i128, + d: bool, + } + + impl TestCase for SuboI128 { + fn name() -> &'static str { + "i128_subo" + } + + fn generate(rng: &mut R) -> Option + where + R: Rng, + Self: Sized, + { + let a = gen_i128(rng); + let b = gen_i128(rng); + let (c, d) = a.overflowing_sub(b); + + Some(SuboI128 { a, b, c, d }) + } + + fn to_string(&self, buffer: &mut String) { + writeln!( + buffer, + "(({a}, {b}), ({c}, {d})),", + a = self.a, + b = self.b, + c = self.c, + d = self.d + ) + .unwrap(); + } + + fn prologue() -> &'static str { + " +use compiler_builtins::int::addsub::rust_i128_subo; + +static TEST_CASES: &[((i128, i128), (i128, bool))] = &[ +" + } + + fn epilogue() -> &'static str { + " +]; + +#[test] +fn i128_subo() { + for &((a, b), (c, d)) in TEST_CASES { + let (c_, d_) = rust_i128_subo(a, b); + assert_eq!(((a, b), (c, d)), ((a, b), (c_, d_))); + } +} +" + } + } + #[derive(Eq, Hash, PartialEq)] pub struct Mulsf3 { a: u32, // f32 diff --git a/src/int/addsub.rs b/src/int/addsub.rs new file mode 100644 index 0000000..7564d89 --- /dev/null +++ b/src/int/addsub.rs @@ -0,0 +1,122 @@ +use int::LargeInt; +use int::Int; + +trait UAddSub: LargeInt { + fn uadd(self, other: Self) -> Self { + let (low, carry) = self.low().overflowing_add(other.low()); + let high = self.high().wrapping_add(other.high()); + let carry = if carry { Self::HighHalf::ONE } else { Self::HighHalf::ZERO }; + Self::from_parts(low, high.wrapping_add(carry)) + } + fn uadd_one(self) -> Self { + let (low, carry) = self.low().overflowing_add(Self::LowHalf::ONE); + let carry = if carry { Self::HighHalf::ONE } else { Self::HighHalf::ZERO }; + Self::from_parts(low, self.high().wrapping_add(carry)) + } + fn usub(self, other: Self) -> Self { + let uneg = (!other).uadd_one(); + self.uadd(uneg) + } +} + +impl UAddSub for u128 {} + +trait AddSub: Int + where ::UnsignedInt: UAddSub +{ + fn add(self, other: Self) -> Self { + Self::from_unsigned(self.unsigned().uadd(other.unsigned())) + } + fn sub(self, other: Self) -> Self { + Self::from_unsigned(self.unsigned().usub(other.unsigned())) + } +} + +impl AddSub for u128 {} +impl AddSub for i128 {} + +trait Addo: AddSub + where ::UnsignedInt: UAddSub +{ + fn addo(self, other: Self, overflow: &mut i32) -> Self { + *overflow = 0; + let result = AddSub::add(self, other); + if other >= Self::ZERO { + if result < self { + *overflow = 1; + } + } else { + if result >= self { + *overflow = 1; + } + } + result + } +} + +impl Addo for i128 {} +impl Addo for u128 {} + +trait Subo: AddSub + where ::UnsignedInt: UAddSub +{ + fn subo(self, other: Self, overflow: &mut i32) -> Self { + *overflow = 0; + let result = AddSub::sub(self, other); + if other >= Self::ZERO { + if result > self { + *overflow = 1; + } + } else { + if result <= self { + *overflow = 1; + } + } + result + } +} + +impl Subo for i128 {} +impl Subo for u128 {} + +#[cfg_attr(not(stage0), lang = "i128_add")] +pub fn rust_i128_add(a: i128, b: i128) -> i128 { + rust_u128_add(a as _, b as _) as _ +} +#[cfg_attr(not(stage0), lang = "i128_addo")] +pub fn rust_i128_addo(a: i128, b: i128) -> (i128, bool) { + let mut oflow = 0; + let r = a.addo(b, &mut oflow); + (r, oflow != 0) +} +#[cfg_attr(not(stage0), lang = "u128_add")] +pub fn rust_u128_add(a: u128, b: u128) -> u128 { + a.add(b) +} +#[cfg_attr(not(stage0), lang = "u128_addo")] +pub fn rust_u128_addo(a: u128, b: u128) -> (u128, bool) { + let mut oflow = 0; + let r = a.addo(b, &mut oflow); + (r, oflow != 0) +} + +#[cfg_attr(not(stage0), lang = "i128_sub")] +pub fn rust_i128_sub(a: i128, b: i128) -> i128 { + rust_u128_sub(a as _, b as _) as _ +} +#[cfg_attr(not(stage0), lang = "i128_subo")] +pub fn rust_i128_subo(a: i128, b: i128) -> (i128, bool) { + let mut oflow = 0; + let r = a.subo(b, &mut oflow); + (r, oflow != 0) +} +#[cfg_attr(not(stage0), lang = "u128_sub")] +pub fn rust_u128_sub(a: u128, b: u128) -> u128 { + a.sub(b) +} +#[cfg_attr(not(stage0), lang = "u128_subo")] +pub fn rust_u128_subo(a: u128, b: u128) -> (u128, bool) { + let mut oflow = 0; + let r = a.subo(b, &mut oflow); + (r, oflow != 0) +} diff --git a/src/int/mod.rs b/src/int/mod.rs index 37dac8c..b645b21 100644 --- a/src/int/mod.rs +++ b/src/int/mod.rs @@ -12,6 +12,7 @@ macro_rules! os_ty { } } +pub mod addsub; pub mod mul; pub mod sdiv; pub mod shift; @@ -72,6 +73,7 @@ pub trait Int: fn wrapping_mul(self, other: Self) -> Self; fn wrapping_sub(self, other: Self) -> Self; fn wrapping_shl(self, other: u32) -> Self; + fn overflowing_add(self, other: Self) -> (Self, bool); fn aborting_div(self, other: Self) -> Self; fn aborting_rem(self, other: Self) -> Self; fn leading_zeros(self) -> u32; @@ -119,6 +121,10 @@ macro_rules! int_impl_common { ::wrapping_shl(self, other) } + fn overflowing_add(self, other: Self) -> (Self, bool) { + ::overflowing_add(self, other) + } + fn aborting_div(self, other: Self) -> Self { unwrap(::checked_div(self, other)) } diff --git a/src/int/mul.rs b/src/int/mul.rs index a4b2ebd..61c379c 100644 --- a/src/int/mul.rs +++ b/src/int/mul.rs @@ -70,6 +70,18 @@ impl Mulo for i32 {} impl Mulo for i64 {} impl Mulo for i128 {} +trait UMulo : Int { + fn mulo(self, other: Self, overflow: &mut i32) -> Self { + *overflow = 0; + let result = self.wrapping_mul(other); + if self > Self::max_value().aborting_div(other) { + *overflow = 1; + } + result + } +} +impl UMulo for u128 {} + intrinsics! { #[use_c_shim_if(all(target_arch = "x86", not(target_env = "msvc")))] #[arm_aeabi_alias = __aeabi_lmul] @@ -95,3 +107,24 @@ intrinsics! { a.mulo(b, oflow) } } + +#[cfg_attr(not(stage0), lang = "i128_mul")] +pub fn rust_i128_mul(a: i128, b: i128) -> i128 { + __multi3(a, b) +} +#[cfg_attr(not(stage0), lang = "i128_mulo")] +pub fn rust_i128_mulo(a: i128, b: i128) -> (i128, bool) { + let mut oflow = 0; + let r = __muloti4(a, b, &mut oflow); + (r, oflow != 0) +} +#[cfg_attr(not(stage0), lang = "u128_mul")] +pub fn rust_u128_mul(a: u128, b: u128) -> u128 { + __multi3(a as _, b as _) as _ +} +#[cfg_attr(not(stage0), lang = "u128_mulo")] +pub fn rust_u128_mulo(a: u128, b: u128) -> (u128, bool) { + let mut oflow = 0; + let r = a.mulo(b, &mut oflow); + (r, oflow != 0) +} diff --git a/src/int/sdiv.rs b/src/int/sdiv.rs index ff8fa61..0308664 100644 --- a/src/int/sdiv.rs +++ b/src/int/sdiv.rs @@ -97,3 +97,12 @@ intrinsics! { a.divmod(b, rem, |a, b| __divdi3(a, b)) } } + +#[cfg_attr(not(stage0), lang = "i128_div")] +pub fn rust_i128_div(a: i128, b: i128) -> i128 { + __divti3(a, b) +} +#[cfg_attr(not(stage0), lang = "i128_rem")] +pub fn rust_i128_rem(a: i128, b: i128) -> i128 { + __modti3(a, b) +} diff --git a/src/int/shift.rs b/src/int/shift.rs index 805d705..f8faad8 100644 --- a/src/int/shift.rs +++ b/src/int/shift.rs @@ -95,3 +95,37 @@ intrinsics! { a.lshr(b) } } + +#[cfg_attr(not(stage0), lang = "i128_shl")] +pub fn rust_i128_shl(a: i128, b: u32) -> i128 { + __ashlti3(a as _, b) as _ +} +#[cfg_attr(not(stage0), lang = "i128_shlo")] +pub fn rust_i128_shlo(a: i128, b: u128) -> (i128, bool) { + (rust_i128_shl(a, b as _), b >= 128) +} +#[cfg_attr(not(stage0), lang = "u128_shl")] +pub fn rust_u128_shl(a: u128, b: u32) -> u128 { + __ashlti3(a, b) +} +#[cfg_attr(not(stage0), lang = "u128_shlo")] +pub fn rust_u128_shlo(a: u128, b: u128) -> (u128, bool) { + (rust_u128_shl(a, b as _), b >= 128) +} + +#[cfg_attr(not(stage0), lang = "i128_shr")] +pub fn rust_i128_shr(a: i128, b: u32) -> i128 { + __ashrti3(a, b) +} +#[cfg_attr(not(stage0), lang = "i128_shro")] +pub fn rust_i128_shro(a: i128, b: u128) -> (i128, bool) { + (rust_i128_shr(a, b as _), b >= 128) +} +#[cfg_attr(not(stage0), lang = "u128_shr")] +pub fn rust_u128_shr(a: u128, b: u32) -> u128 { + __lshrti3(a, b) +} +#[cfg_attr(not(stage0), lang = "u128_shro")] +pub fn rust_u128_shro(a: u128, b: u128) -> (u128, bool) { + (rust_u128_shr(a, b as _), b >= 128) +} diff --git a/src/int/udiv.rs b/src/int/udiv.rs index 74a2ac3..6c100f3 100644 --- a/src/int/udiv.rs +++ b/src/int/udiv.rs @@ -269,3 +269,12 @@ intrinsics! { udivmod_inner!(n, d, rem, u128) } } + +#[cfg_attr(not(stage0), lang = "u128_div")] +pub fn rust_u128_div(a: u128, b: u128) -> u128 { + __udivti3(a, b) +} +#[cfg_attr(not(stage0), lang = "u128_rem")] +pub fn rust_u128_rem(a: u128, b: u128) -> u128 { + __umodti3(a, b) +} diff --git a/src/lib.rs b/src/lib.rs index 8bf3230..ff7885b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,6 +17,7 @@ #![feature(repr_simd)] #![feature(abi_unadjusted)] #![feature(linkage)] +#![feature(lang_items)] #![allow(unused_features)] #![no_builtins] #![unstable(feature = "compiler_builtins_lib", diff --git a/tests/i128_add.rs b/tests/i128_add.rs new file mode 100644 index 0000000..13af050 --- /dev/null +++ b/tests/i128_add.rs @@ -0,0 +1,8 @@ +#![feature(compiler_builtins_lib)] +#![feature(i128_type)] +#![cfg_attr(all(target_arch = "arm", + not(any(target_env = "gnu", target_env = "musl")), + target_os = "linux", + test), no_std)] + +include!(concat!(env!("OUT_DIR"), "/i128_add.rs")); diff --git a/tests/i128_addo.rs b/tests/i128_addo.rs new file mode 100644 index 0000000..306ffac --- /dev/null +++ b/tests/i128_addo.rs @@ -0,0 +1,8 @@ +#![feature(compiler_builtins_lib)] +#![feature(i128_type)] +#![cfg_attr(all(target_arch = "arm", + not(any(target_env = "gnu", target_env = "musl")), + target_os = "linux", + test), no_std)] + +include!(concat!(env!("OUT_DIR"), "/i128_addo.rs")); diff --git a/tests/i128_sub.rs b/tests/i128_sub.rs new file mode 100644 index 0000000..933fe4f --- /dev/null +++ b/tests/i128_sub.rs @@ -0,0 +1,8 @@ +#![feature(compiler_builtins_lib)] +#![feature(i128_type)] +#![cfg_attr(all(target_arch = "arm", + not(any(target_env = "gnu", target_env = "musl")), + target_os = "linux", + test), no_std)] + +include!(concat!(env!("OUT_DIR"), "/i128_sub.rs")); diff --git a/tests/i128_subo.rs b/tests/i128_subo.rs new file mode 100644 index 0000000..0c2b67c --- /dev/null +++ b/tests/i128_subo.rs @@ -0,0 +1,8 @@ +#![feature(compiler_builtins_lib)] +#![feature(i128_type)] +#![cfg_attr(all(target_arch = "arm", + not(any(target_env = "gnu", target_env = "musl")), + target_os = "linux", + test), no_std)] + +include!(concat!(env!("OUT_DIR"), "/i128_subo.rs")); diff --git a/tests/u128_add.rs b/tests/u128_add.rs new file mode 100644 index 0000000..d4b8f2c --- /dev/null +++ b/tests/u128_add.rs @@ -0,0 +1,8 @@ +#![feature(compiler_builtins_lib)] +#![feature(i128_type)] +#![cfg_attr(all(target_arch = "arm", + not(any(target_env = "gnu", target_env = "musl")), + target_os = "linux", + test), no_std)] + +include!(concat!(env!("OUT_DIR"), "/u128_add.rs")); diff --git a/tests/u128_addo.rs b/tests/u128_addo.rs new file mode 100644 index 0000000..caff682 --- /dev/null +++ b/tests/u128_addo.rs @@ -0,0 +1,8 @@ +#![feature(compiler_builtins_lib)] +#![feature(i128_type)] +#![cfg_attr(all(target_arch = "arm", + not(any(target_env = "gnu", target_env = "musl")), + target_os = "linux", + test), no_std)] + +include!(concat!(env!("OUT_DIR"), "/u128_addo.rs")); diff --git a/tests/u128_sub.rs b/tests/u128_sub.rs new file mode 100644 index 0000000..024266b --- /dev/null +++ b/tests/u128_sub.rs @@ -0,0 +1,8 @@ +#![feature(compiler_builtins_lib)] +#![feature(i128_type)] +#![cfg_attr(all(target_arch = "arm", + not(any(target_env = "gnu", target_env = "musl")), + target_os = "linux", + test), no_std)] + +include!(concat!(env!("OUT_DIR"), "/u128_sub.rs")); diff --git a/tests/u128_subo.rs b/tests/u128_subo.rs new file mode 100644 index 0000000..b3fb4b8 --- /dev/null +++ b/tests/u128_subo.rs @@ -0,0 +1,8 @@ +#![feature(compiler_builtins_lib)] +#![feature(i128_type)] +#![cfg_attr(all(target_arch = "arm", + not(any(target_env = "gnu", target_env = "musl")), + target_os = "linux", + test), no_std)] + +include!(concat!(env!("OUT_DIR"), "/u128_subo.rs"));