[gcc/devel/rust/master] intrinsics: Add wrapping_{add, sub, mul}
Thomas Schwinge
tschwinge@gcc.gnu.org
Sat Aug 13 08:42:29 GMT 2022
https://gcc.gnu.org/g:8899dc9bf70b193dc59dbc8e81400de22c203e8f
commit 8899dc9bf70b193dc59dbc8e81400de22c203e8f
Author: Arthur Cohen <arthur.cohen@embecosm.com>
Date: Thu Aug 11 12:01:10 2022 +0200
intrinsics: Add wrapping_{add, sub, mul}
Since wrapping arithmetics are guaranteed in Rust, we turn on the -fwrapv and simply desugar wrapping_{add, sub, mul} to their non-checked inner operations. This is the only difference between a wrapping add and a regular addition: The regular addition will gain some checks for overflows, which are simply not used for the wrapping version.
Diff:
---
gcc/rust/backend/rust-compile-intrinsic.cc | 69 +++++++++++++++++++++-
gcc/rust/rust-lang.cc | 8 ++-
gcc/testsuite/rust/execute/torture/wrapping_op1.rs | 14 +++++
gcc/testsuite/rust/execute/torture/wrapping_op2.rs | 20 +++++++
4 files changed, 109 insertions(+), 2 deletions(-)
diff --git a/gcc/rust/backend/rust-compile-intrinsic.cc b/gcc/rust/backend/rust-compile-intrinsic.cc
index 67e38c3261c..06dc45797e9 100644
--- a/gcc/rust/backend/rust-compile-intrinsic.cc
+++ b/gcc/rust/backend/rust-compile-intrinsic.cc
@@ -37,6 +37,8 @@ static tree
transmute_handler (Context *ctx, TyTy::FnType *fntype);
static tree
rotate_handler (Context *ctx, TyTy::FnType *fntype, tree_code op);
+static tree
+wrapping_op_handler (Context *ctx, TyTy::FnType *fntype, tree_code op);
static inline tree
rotate_left_handler (Context *ctx, TyTy::FnType *fntype)
@@ -49,13 +51,32 @@ rotate_right_handler (Context *ctx, TyTy::FnType *fntype)
return rotate_handler (ctx, fntype, RROTATE_EXPR);
}
+static inline tree
+wrapping_add_handler (Context *ctx, TyTy::FnType *fntype)
+{
+ return wrapping_op_handler (ctx, fntype, PLUS_EXPR);
+}
+static inline tree
+wrapping_sub_handler (Context *ctx, TyTy::FnType *fntype)
+{
+ return wrapping_op_handler (ctx, fntype, MINUS_EXPR);
+}
+static inline tree
+wrapping_mul_handler (Context *ctx, TyTy::FnType *fntype)
+{
+ return wrapping_op_handler (ctx, fntype, MULT_EXPR);
+}
+
static const std::map<std::string,
std::function<tree (Context *, TyTy::FnType *)>>
generic_intrinsics = {{"offset", &offset_handler},
{"size_of", &sizeof_handler},
{"transmute", &transmute_handler},
{"rotate_left", &rotate_left_handler},
- {"rotate_right", &rotate_right_handler}};
+ {"rotate_right", &rotate_right_handler},
+ {"wrapping_add", &wrapping_add_handler},
+ {"wrapping_sub", &wrapping_sub_handler},
+ {"wrapping_mul", &wrapping_mul_handler}};
Intrinsics::Intrinsics (Context *ctx) : ctx (ctx) {}
@@ -373,5 +394,51 @@ rotate_handler (Context *ctx, TyTy::FnType *fntype, tree_code op)
return fndecl;
}
+/**
+ * pub fn wrapping_{add, sub, mul}<T>(lhs: T, rhs: T) -> T;
+ */
+static tree
+wrapping_op_handler (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 &lhs_param = param_vars.at (0);
+ auto &rhs_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 wrapping_<op> FN BODY BEGIN
+ auto lhs = ctx->get_backend ()->var_expression (lhs_param, Location ());
+ auto rhs = ctx->get_backend ()->var_expression (rhs_param, Location ());
+
+ // Operations are always wrapping in Rust, as we have -fwrapv enabled by
+ // default. The difference between a wrapping_{add, sub, mul} and a regular
+ // arithmetic operation is that these intrinsics do not panic - they always
+ // carry over.
+ auto wrap_expr = build2 (op, TREE_TYPE (lhs), lhs, rhs);
+
+ auto return_statement
+ = ctx->get_backend ()->return_statement (fndecl, {wrap_expr}, Location ());
+ ctx->add_statement (return_statement);
+ // BUILTIN wrapping_<op> FN BODY END
+
+ finalize_intrinsic_block (ctx, fndecl);
+
+ return fndecl;
+}
+
} // namespace Compile
} // namespace Rust
diff --git a/gcc/rust/rust-lang.cc b/gcc/rust/rust-lang.cc
index 95c92f8092b..ed822cc4f13 100644
--- a/gcc/rust/rust-lang.cc
+++ b/gcc/rust/rust-lang.cc
@@ -152,8 +152,14 @@ grs_langhook_option_lang_mask (void)
/* Initialize the options structure. */
static void
-grs_langhook_init_options_struct (struct gcc_options * /* opts */)
+grs_langhook_init_options_struct (struct gcc_options *opts)
{
+ /* Operations are always wrapping in Rust, even on signed integer. This is
+ * useful for the low level wrapping_{add, sub, mul} intrinsics, not for
+ * regular arithmetic operations which are checked for overflow anyway using
+ * builtins */
+ opts->x_flag_wrapv = 1;
+
// nothing yet - used by frontends to change specific options for the language
Rust::Session::get_instance ().init_options ();
}
diff --git a/gcc/testsuite/rust/execute/torture/wrapping_op1.rs b/gcc/testsuite/rust/execute/torture/wrapping_op1.rs
new file mode 100644
index 00000000000..64b37085ab7
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/wrapping_op1.rs
@@ -0,0 +1,14 @@
+extern "rust-intrinsic" {
+ pub fn wrapping_add<T>(l: T, r: T) -> T;
+}
+
+fn five() -> u8 {
+ 5
+}
+
+fn main() -> u8 {
+ let l = 255;
+ let r = five();
+
+ unsafe { wrapping_add(l, r) - 4 }
+}
diff --git a/gcc/testsuite/rust/execute/torture/wrapping_op2.rs b/gcc/testsuite/rust/execute/torture/wrapping_op2.rs
new file mode 100644
index 00000000000..f9990157894
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/wrapping_op2.rs
@@ -0,0 +1,20 @@
+extern "rust-intrinsic" {
+ pub fn wrapping_add<T>(l: T, r: T) -> T;
+ pub fn wrapping_sub<T>(l: T, r: T) -> T;
+ pub fn wrapping_mul<T>(l: T, r: T) -> T;
+}
+
+fn five() -> u8 {
+ 5
+}
+
+fn main() -> u8 {
+ let l = 255;
+ let r = five();
+
+ let ret0 = unsafe { wrapping_add(l, r) - 4 }; // 4
+ let ret1 = unsafe { wrapping_sub(r, l) - 6 }; // 6
+ let ret2 = unsafe { wrapping_mul(r, l) - 251 }; // 251
+
+ ret0 + ret1 + ret2
+}
More information about the Gcc-cvs
mailing list