aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorPhilip Herron <herron.philip@googlemail.com>2023-03-01 11:42:36 +0000
committerPhilip Herron <philip.herron@embecosm.com>2023-03-01 21:23:44 +0000
commit23befb042f90f5c84a59c0901b147c24a704847b (patch)
tree00ab5e2a018762be7bb8a5382e47a9b97550d6c9 /gcc
parent7394a6893dd9fc2bc34822e002b53eb200ff51d5 (diff)
downloadgcc-23befb042f90f5c84a59c0901b147c24a704847b.zip
gcc-23befb042f90f5c84a59c0901b147c24a704847b.tar.gz
gcc-23befb042f90f5c84a59c0901b147c24a704847b.tar.bz2
gccrs: add {add,sub,mul}_with_overflow intrinsics
Fixes #1898 Signed-off-by: Philip Herron <herron.philip@googlemail.com> gcc/rust/ChangeLog: * backend/rust-compile-intrinsic.cc (op_with_overflow_inner): wraps op_with_overflow (std::function<tree): likewise (op_with_overflow): generate the intrinsic based on the tree_code op gcc/testsuite/ChangeLog: * rust/compile/torture/intrinsics-8.rs: New test.
Diffstat (limited to 'gcc')
-rw-r--r--gcc/rust/backend/rust-compile-intrinsic.cc104
-rw-r--r--gcc/testsuite/rust/compile/torture/intrinsics-8.rs38
2 files changed, 142 insertions, 0 deletions
diff --git a/gcc/rust/backend/rust-compile-intrinsic.cc b/gcc/rust/backend/rust-compile-intrinsic.cc
index 5522211..04b0d3a 100644
--- a/gcc/rust/backend/rust-compile-intrinsic.cc
+++ b/gcc/rust/backend/rust-compile-intrinsic.cc
@@ -25,6 +25,7 @@
#include "rust-constexpr.h"
#include "rust-tree.h"
#include "tree-core.h"
+#include "rust-gcc.h"
#include "print-tree.h"
#include "fold-const.h"
#include "langhooks.h"
@@ -78,6 +79,8 @@ static tree
wrapping_op_handler_inner (Context *ctx, TyTy::FnType *fntype, tree_code op);
static tree
copy_nonoverlapping_handler (Context *ctx, TyTy::FnType *fntype);
+static tree
+op_with_overflow_inner (Context *ctx, TyTy::FnType *fntype, tree_code op);
enum class Prefetch
{
@@ -107,6 +110,14 @@ wrapping_op_handler (tree_code op)
};
}
+const static std::function<tree (Context *, TyTy::FnType *)>
+op_with_overflow (tree_code op)
+{
+ return [op] (Context *ctx, TyTy::FnType *fntype) {
+ return op_with_overflow_inner (ctx, fntype, op);
+ };
+}
+
static inline tree
prefetch_read_data (Context *ctx, TyTy::FnType *fntype)
{
@@ -170,6 +181,9 @@ static const std::map<std::string,
{"wrapping_add", wrapping_op_handler (PLUS_EXPR)},
{"wrapping_sub", wrapping_op_handler (MINUS_EXPR)},
{"wrapping_mul", wrapping_op_handler (MULT_EXPR)},
+ {"add_with_overflow", op_with_overflow (PLUS_EXPR)},
+ {"sub_with_overflow", op_with_overflow (MINUS_EXPR)},
+ {"mul_with_overflow", op_with_overflow (MULT_EXPR)},
{"copy_nonoverlapping", copy_nonoverlapping_handler},
{"prefetch_read_data", prefetch_read_data},
{"prefetch_write_data", prefetch_write_data},
@@ -559,6 +573,96 @@ wrapping_op_handler_inner (Context *ctx, TyTy::FnType *fntype, tree_code op)
}
/**
+ * pub fn add_with_overflow<T>(x: T, y: T) -> (T, bool);
+ */
+static tree
+op_with_overflow_inner (Context *ctx, TyTy::FnType *fntype, tree_code op)
+{
+ // wrapping_<op> intrinsics have two parameter
+ rust_assert (fntype->get_params ().size () == 2);
+
+ tree lookup = NULL_TREE;
+ if (check_for_cached_intrinsic (ctx, fntype, &lookup))
+ return lookup;
+
+ auto fndecl = compile_intrinsic_function (ctx, fntype);
+
+ // setup the params
+ std::vector<Bvariable *> param_vars;
+ compile_fn_params (ctx, fntype, fndecl, &param_vars);
+
+ auto &x_param = param_vars.at (0);
+ auto &y_param = param_vars.at (1);
+
+ if (!ctx->get_backend ()->function_set_parameters (fndecl, param_vars))
+ return error_mark_node;
+
+ enter_intrinsic_block (ctx, fndecl);
+
+ // BUILTIN op_with_overflow FN BODY BEGIN
+ auto x = ctx->get_backend ()->var_expression (x_param, Location ());
+ auto y = ctx->get_backend ()->var_expression (y_param, Location ());
+
+ tree overflow_builtin = error_mark_node;
+ switch (op)
+ {
+ case PLUS_EXPR:
+ BuiltinsContext::get ().lookup_simple_builtin ("add_overflow",
+ &overflow_builtin);
+ break;
+
+ case MINUS_EXPR:
+ BuiltinsContext::get ().lookup_simple_builtin ("sub_overflow",
+ &overflow_builtin);
+ break;
+
+ case MULT_EXPR:
+ BuiltinsContext::get ().lookup_simple_builtin ("mul_overflow",
+ &overflow_builtin);
+ break;
+
+ default:
+ gcc_unreachable ();
+ break;
+ }
+ rust_assert (overflow_builtin != error_mark_node);
+
+ // this should match y as well or we can take it from the TyTy structure
+ tree overflow_op_type = TREE_TYPE (x);
+ tree tmp_stmt = error_mark_node;
+ Bvariable *bvar
+ = ctx->get_backend ()->temporary_variable (fndecl, NULL_TREE,
+ overflow_op_type, NULL_TREE,
+ true /*address_is_taken*/,
+ Location (), &tmp_stmt);
+ ctx->add_statement (tmp_stmt);
+
+ tree result_decl = bvar->get_tree (Location ());
+ tree result_ref = build_fold_addr_expr_loc (BUILTINS_LOCATION, result_decl);
+
+ tree did_overflow_node
+ = build_call_expr_loc (BUILTINS_LOCATION, overflow_builtin, 3, x, y,
+ result_ref);
+
+ std::vector<tree> vals = {result_decl, did_overflow_node};
+ tree tuple_type = TREE_TYPE (DECL_RESULT (fndecl));
+ tree result_expr
+ = ctx->get_backend ()->constructor_expression (tuple_type, false, vals, -1,
+ Location ());
+
+ auto return_statement
+ = ctx->get_backend ()->return_statement (fndecl, {result_expr},
+ Location ());
+ ctx->add_statement (return_statement);
+
+ // BUILTIN wrapping_<op> FN BODY END
+
+ finalize_intrinsic_block (ctx, fndecl);
+
+ return fndecl;
+}
+
+/**
* fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: usize);
*/
static tree
diff --git a/gcc/testsuite/rust/compile/torture/intrinsics-8.rs b/gcc/testsuite/rust/compile/torture/intrinsics-8.rs
new file mode 100644
index 0000000..8788da5
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/intrinsics-8.rs
@@ -0,0 +1,38 @@
+mod intrinsics {
+ extern "rust-intrinsic" {
+ pub fn add_with_overflow<T>(x: T, y: T) -> (T, bool);
+ pub fn sub_with_overflow<T>(x: T, y: T) -> (T, bool);
+ pub fn mul_with_overflow<T>(x: T, y: T) -> (T, bool);
+ }
+}
+
+pub enum Option<T> {
+ None,
+ Some(T),
+}
+
+impl i32 {
+ pub fn checked_add(self, rhs: Self) -> Option<Self> {
+ let (a, b) = self.overflowing_add(rhs);
+ if b {
+ Option::None
+ } else {
+ Option::Some(a)
+ }
+ }
+
+ pub fn overflowing_add(self, rhs: Self) -> (Self, bool) {
+ let (a, b) = unsafe { intrinsics::add_with_overflow(self as i32, rhs as i32) };
+ (a as Self, b)
+ }
+
+ pub fn overflowing_sub(self, rhs: Self) -> (Self, bool) {
+ let (a, b) = unsafe { intrinsics::sub_with_overflow(self as i32, rhs as i32) };
+ (a as Self, b)
+ }
+
+ pub fn overflowing_mul(self, rhs: Self) -> (Self, bool) {
+ let (a, b) = unsafe { intrinsics::mul_with_overflow(self as i32, rhs as i32) };
+ (a as Self, b)
+ }
+}