From 7ce36226e6156e7aaf6da77833fd80cd83d8d623 Mon Sep 17 00:00:00 2001
From: Ralf Jung <post@ralfj.de>
Date: Sun, 16 Jun 2019 11:10:14 +0200
Subject: [PATCH] implement and test unchecked_{add,sub,mul} intrinsics

---
 src/intrinsic.rs                     | 16 +++++++++++
 tests/compile-fail/unchecked_add1.rs |  5 ++++
 tests/compile-fail/unchecked_add2.rs |  5 ++++
 tests/compile-fail/unchecked_mul1.rs |  5 ++++
 tests/compile-fail/unchecked_mul2.rs |  5 ++++
 tests/compile-fail/unchecked_sub1.rs |  5 ++++
 tests/compile-fail/unchecked_sub2.rs |  5 ++++
 tests/run-pass/intrinsics-integer.rs | 40 ++++++++++++++++++----------
 8 files changed, 72 insertions(+), 14 deletions(-)
 create mode 100644 tests/compile-fail/unchecked_add1.rs
 create mode 100644 tests/compile-fail/unchecked_add2.rs
 create mode 100644 tests/compile-fail/unchecked_mul1.rs
 create mode 100644 tests/compile-fail/unchecked_mul2.rs
 create mode 100644 tests/compile-fail/unchecked_sub1.rs
 create mode 100644 tests/compile-fail/unchecked_sub2.rs

diff --git a/src/intrinsic.rs b/src/intrinsic.rs
index cf2afe0a811..0ecccd02d7e 100644
--- a/src/intrinsic.rs
+++ b/src/intrinsic.rs
@@ -465,6 +465,22 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
                 )?;
             }
 
+            "unchecked_add" | "unchecked_sub" | "unchecked_mul" => {
+                let l = this.read_immediate(args[0])?;
+                let r = this.read_immediate(args[1])?;
+                let op = match intrinsic_name.get() {
+                    "unchecked_add" => mir::BinOp::Add,
+                    "unchecked_sub" => mir::BinOp::Sub,
+                    "unchecked_mul" => mir::BinOp::Mul,
+                    _ => bug!(),
+                };
+                let (res, overflowed) = this.binary_op(op, l, r)?;
+                if overflowed {
+                    return err!(Intrinsic(format!("Overflowing arithmetic in {}", intrinsic_name.get())));
+                }
+                this.write_scalar(res, dest)?;
+            }
+
             "uninit" => {
                 // Check fast path: we don't want to force an allocation in case the destination is a simple value,
                 // but we also do not want to create a new allocation with 0s and then copy that over.
diff --git a/tests/compile-fail/unchecked_add1.rs b/tests/compile-fail/unchecked_add1.rs
new file mode 100644
index 00000000000..2447c8ba4a8
--- /dev/null
+++ b/tests/compile-fail/unchecked_add1.rs
@@ -0,0 +1,5 @@
+#![feature(core_intrinsics)]
+fn main() {
+    // MAX overflow
+    unsafe { std::intrinsics::unchecked_add(40000u16, 30000); } //~ ERROR Overflowing arithmetic in unchecked_add
+}
diff --git a/tests/compile-fail/unchecked_add2.rs b/tests/compile-fail/unchecked_add2.rs
new file mode 100644
index 00000000000..e292cdf6d96
--- /dev/null
+++ b/tests/compile-fail/unchecked_add2.rs
@@ -0,0 +1,5 @@
+#![feature(core_intrinsics)]
+fn main() {
+    // MIN overflow
+    unsafe { std::intrinsics::unchecked_add(-30000i16, -8000); } //~ ERROR Overflowing arithmetic in unchecked_add
+}
diff --git a/tests/compile-fail/unchecked_mul1.rs b/tests/compile-fail/unchecked_mul1.rs
new file mode 100644
index 00000000000..57bfaf124c2
--- /dev/null
+++ b/tests/compile-fail/unchecked_mul1.rs
@@ -0,0 +1,5 @@
+#![feature(core_intrinsics)]
+fn main() {
+    // MAX overflow
+    unsafe { std::intrinsics::unchecked_mul(300u16, 250u16); } //~ ERROR Overflowing arithmetic in unchecked_mul
+}
diff --git a/tests/compile-fail/unchecked_mul2.rs b/tests/compile-fail/unchecked_mul2.rs
new file mode 100644
index 00000000000..690f2dc7628
--- /dev/null
+++ b/tests/compile-fail/unchecked_mul2.rs
@@ -0,0 +1,5 @@
+#![feature(core_intrinsics)]
+fn main() {
+    // MIN overflow
+    unsafe { std::intrinsics::unchecked_mul(1_000_000_000i32, -4); } //~ ERROR Overflowing arithmetic in unchecked_mul
+}
diff --git a/tests/compile-fail/unchecked_sub1.rs b/tests/compile-fail/unchecked_sub1.rs
new file mode 100644
index 00000000000..0be8afa2c34
--- /dev/null
+++ b/tests/compile-fail/unchecked_sub1.rs
@@ -0,0 +1,5 @@
+#![feature(core_intrinsics)]
+fn main() {
+    // MIN overflow
+    unsafe { std::intrinsics::unchecked_sub(14u32, 22); } //~ ERROR Overflowing arithmetic in unchecked_sub
+}
diff --git a/tests/compile-fail/unchecked_sub2.rs b/tests/compile-fail/unchecked_sub2.rs
new file mode 100644
index 00000000000..bc23fa37c36
--- /dev/null
+++ b/tests/compile-fail/unchecked_sub2.rs
@@ -0,0 +1,5 @@
+#![feature(core_intrinsics)]
+fn main() {
+    // MAX overflow
+    unsafe { std::intrinsics::unchecked_sub(30000i16, -7000); } //~ ERROR Overflowing arithmetic in unchecked_sub
+}
diff --git a/tests/run-pass/intrinsics-integer.rs b/tests/run-pass/intrinsics-integer.rs
index de59314eff5..af3517af6f7 100644
--- a/tests/run-pass/intrinsics-integer.rs
+++ b/tests/run-pass/intrinsics-integer.rs
@@ -8,23 +8,11 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![feature(intrinsics)]
-
-mod rusti {
-    extern "rust-intrinsic" {
-        pub fn ctpop<T>(x: T) -> T;
-        pub fn ctlz<T>(x: T) -> T;
-        pub fn ctlz_nonzero<T>(x: T) -> T;
-        pub fn cttz<T>(x: T) -> T;
-        pub fn cttz_nonzero<T>(x: T) -> T;
-        pub fn bswap<T>(x: T) -> T;
-    }
-}
+#![feature(core_intrinsics)]
+use std::intrinsics::*;
 
 pub fn main() {
     unsafe {
-        use crate::rusti::*;
-
         assert_eq!(ctpop(0u8), 0); assert_eq!(ctpop(0i8), 0);
         assert_eq!(ctpop(0u16), 0); assert_eq!(ctpop(0i16), 0);
         assert_eq!(ctpop(0u32), 0); assert_eq!(ctpop(0i32), 0);
@@ -138,5 +126,29 @@ pub fn main() {
         assert_eq!(bswap(0x0ABBCC0Di32), 0x0DCCBB0A);
         assert_eq!(bswap(0x0122334455667708u64), 0x0877665544332201);
         assert_eq!(bswap(0x0122334455667708i64), 0x0877665544332201);
+
+        assert_eq!(exact_div(9*9u32, 3), 27);
+        assert_eq!(exact_div(-9*9i32, 3), -27);
+        assert_eq!(exact_div(9*9i8, -3), -27);
+        assert_eq!(exact_div(-9*9i64, -3), 27);
+
+        assert_eq!(unchecked_div(9*9u32, 2), 40);
+        assert_eq!(unchecked_div(-9*9i32, 2), -40);
+        assert_eq!(unchecked_div(9*9i8, -2), -40);
+        assert_eq!(unchecked_div(-9*9i64, -2), 40);
+
+        assert_eq!(unchecked_rem(9*9u32, 2), 1);
+        assert_eq!(unchecked_rem(-9*9i32, 2), -1);
+        assert_eq!(unchecked_rem(9*9i8, -2), 1);
+        assert_eq!(unchecked_rem(-9*9i64, -2), -1);
+
+        assert_eq!(unchecked_add(23u8, 19), 42);
+        assert_eq!(unchecked_add(5, -10), -5);
+
+        assert_eq!(unchecked_sub(23u8, 19), 4);
+        assert_eq!(unchecked_sub(-17, -27), 10);
+
+        assert_eq!(unchecked_mul(6u8, 7), 42);
+        assert_eq!(unchecked_mul(13, -5), -65);
     }
 }