Skip to content

gccrs: add {add,sub,mul}_with_overflow intrinsics #1945

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 104 additions & 0 deletions gcc/rust/backend/rust-compile-intrinsic.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
{
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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},
Expand Down Expand Up @@ -558,6 +572,96 @@ wrapping_op_handler_inner (Context *ctx, TyTy::FnType *fntype, tree_code op)
return fndecl;
}

/**
* 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);
*/
Expand Down
38 changes: 38 additions & 0 deletions gcc/testsuite/rust/compile/torture/intrinsics-8.rs
Original file line number Diff line number Diff line change
@@ -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)
}
}