public inbox for gcc-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc/devel/rust/master] intrinsics: Add wrapping_{add, sub, mul}
@ 2022-08-13  8:42 Thomas Schwinge
  0 siblings, 0 replies; only message in thread
From: Thomas Schwinge @ 2022-08-13  8:42 UTC (permalink / raw)
  To: gcc-cvs

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, &param_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
+}


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2022-08-13  8:42 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-08-13  8:42 [gcc/devel/rust/master] intrinsics: Add wrapping_{add, sub, mul} Thomas Schwinge

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).