diff options
author | Philip Herron <herron.philip@googlemail.com> | 2023-03-01 11:42:36 +0000 |
---|---|---|
committer | Philip Herron <philip.herron@embecosm.com> | 2023-03-01 21:23:44 +0000 |
commit | 23befb042f90f5c84a59c0901b147c24a704847b (patch) | |
tree | 00ab5e2a018762be7bb8a5382e47a9b97550d6c9 /gcc | |
parent | 7394a6893dd9fc2bc34822e002b53eb200ff51d5 (diff) | |
download | gcc-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.cc | 104 | ||||
-rw-r--r-- | gcc/testsuite/rust/compile/torture/intrinsics-8.rs | 38 |
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, ¶m_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) + } +} |