From: Enes Cevik <[email protected]>
This patch implements the bswap intrinsic for integer types. It maps to the
respective GCC built-ins for 16, 32, 64 and 128-bit integers.
gcc/rust/ChangeLog:
* backend/rust-compile-intrinsic.cc: Add bswap_handler.
* backend/rust-intrinsic-handlers.cc (bswap_handler): New function.
* backend/rust-intrinsic-handlers.h (bswap_handler): New declaration.
gcc/testsuite/ChangeLog:
* rust/compile/bswap.rs: New test.
* rust/execute/bswap.rs: New test.
Signed-off-by: Enes Cevik <[email protected]>
---
This change was merged into the gccrs repository and is posted here for
upstream visibility and potential drive-by review, as requested by GCC
release managers.
Each commit email contains a link to its details on github from where you can
find the Pull-Request and associated discussions.
Commit on github:
https://github.com/Rust-GCC/gccrs/commit/7aa3e7792737ab5ce818fdd240a37c9a80cc919d
The commit has been mentioned in the following pull-request(s):
- https://github.com/Rust-GCC/gccrs/pull/4452
gcc/rust/backend/rust-compile-intrinsic.cc | 3 +-
gcc/rust/backend/rust-intrinsic-handlers.cc | 160 ++++++++++++
gcc/rust/backend/rust-intrinsic-handlers.h | 1 +
gcc/testsuite/rust/compile/bswap.rs | 17 ++
gcc/testsuite/rust/execute/bswap.rs | 263 ++++++++++++++++++++
5 files changed, 443 insertions(+), 1 deletion(-)
create mode 100644 gcc/testsuite/rust/compile/bswap.rs
create mode 100644 gcc/testsuite/rust/execute/bswap.rs
diff --git a/gcc/rust/backend/rust-compile-intrinsic.cc
b/gcc/rust/backend/rust-compile-intrinsic.cc
index f46341966..a4924542d 100644
--- a/gcc/rust/backend/rust-compile-intrinsic.cc
+++ b/gcc/rust/backend/rust-compile-intrinsic.cc
@@ -67,7 +67,8 @@ static const std::map<std::string, handlers::HandlerBuilder>
generic_intrinsics
{IValue::TRY, handlers::try_handler (false)},
{IValue::CATCH_UNWIND, handlers::try_handler (true)},
{IValue::DISCRIMINANT_VALUE, handlers::discriminant_value},
- {IValue::VARIANT_COUNT, handlers::variant_count}};
+ {IValue::VARIANT_COUNT, handlers::variant_count},
+ {IValue::BSWAP, handlers::bswap_handler}};
Intrinsics::Intrinsics (Context *ctx) : ctx (ctx) {}
diff --git a/gcc/rust/backend/rust-intrinsic-handlers.cc
b/gcc/rust/backend/rust-intrinsic-handlers.cc
index 076ab06e0..5c7854ce8 100644
--- a/gcc/rust/backend/rust-intrinsic-handlers.cc
+++ b/gcc/rust/backend/rust-intrinsic-handlers.cc
@@ -1370,6 +1370,166 @@ offset (Context *ctx, TyTy::FnType *fntype)
return fndecl;
}
+/**
+ * pub const fn bswap<T: Copy>(x: T) -> T;
+ */
+tree
+bswap_handler (Context *ctx, TyTy::FnType *fntype)
+{
+ rust_assert (fntype->get_params ().size () == 1);
+
+ tree lookup = NULL_TREE;
+ if (check_for_cached_intrinsic (ctx, fntype, &lookup))
+ return lookup;
+
+ auto fndecl = compile_intrinsic_function (ctx, fntype);
+
+ auto locus = fntype->get_locus ();
+
+ std::vector<Bvariable *> param_vars;
+ compile_fn_params (ctx, fntype, fndecl, ¶m_vars);
+
+ auto &x_param = param_vars.at (0);
+ rust_assert (param_vars.size () == 1);
+ if (!Backend::function_set_parameters (fndecl, param_vars))
+ return error_mark_node;
+
+ auto *monomorphized_type
+ = fntype->get_substs ().at (0).get_param_ty ()->resolve ();
+
+ check_for_basic_integer_type ("bswap", fntype->get_locus (),
+ monomorphized_type);
+
+ tree template_parameter_type
+ = TyTyResolveCompile::compile (ctx, monomorphized_type);
+
+ tree size_expr = TYPE_SIZE_UNIT (template_parameter_type);
+ unsigned HOST_WIDE_INT size = TREE_INT_CST_LOW (size_expr);
+
+ enter_intrinsic_block (ctx, fndecl);
+
+ // BUILTIN bswap FN BODY BEGIN
+
+ auto expr_x = Backend::var_expression (x_param, locus);
+ tree result = NULL_TREE;
+
+ if (size == 1)
+ {
+ result = expr_x;
+ }
+ else
+ {
+ tree target_type = NULL_TREE;
+ const char *builtin_name = nullptr;
+ switch (size)
+ {
+ case 2:
+ builtin_name = "__builtin_bswap16";
+ target_type = uint16_type_node;
+ break;
+ case 4:
+ builtin_name = "__builtin_bswap32";
+ target_type = uint32_type_node;
+ break;
+ case 8:
+ builtin_name = "__builtin_bswap64";
+ target_type = uint64_type_node;
+ break;
+ case 16:
+ builtin_name = "__builtin_bswap128";
+ target_type = uint128_type_node;
+ break;
+ default:
+ return error_mark_node;
+ }
+
+ tree bswap_raw = nullptr;
+ auto ok = BuiltinsContext::get ().lookup_simple_builtin (builtin_name,
+ &bswap_raw);
+
+ if (ok)
+ {
+ tree bswap_fn = build_fold_addr_expr_loc (locus, bswap_raw);
+
+ auto bswap_x = build1 (CONVERT_EXPR, target_type, expr_x);
+
+ auto bswap_call
+ = Backend::call_expression (bswap_fn, {bswap_x}, NULL_TREE, locus);
+
+ result = build1 (CONVERT_EXPR, template_parameter_type, bswap_call);
+ }
+ else
+ {
+ auto ok2 = BuiltinsContext::get ().lookup_simple_builtin (
+ "__builtin_bswap64", &bswap_raw);
+ rust_assert (ok2);
+
+ tree bswap_fn = build_fold_addr_expr_loc (locus, bswap_raw);
+
+ tree tmp_in_stmt = error_mark_node;
+ Bvariable *in_var
+ = Backend::temporary_variable (fndecl, NULL_TREE,
+ template_parameter_type, expr_x,
+ true, locus, &tmp_in_stmt);
+ ctx->add_statement (tmp_in_stmt);
+
+ tree addr_x
+ = build_fold_addr_expr_loc (locus, in_var->get_tree (locus));
+ tree u64_ptr_type = build_pointer_type (uint64_type_node);
+
+ tree low_ptr = fold_convert (u64_ptr_type, addr_x);
+ tree low = build_fold_indirect_ref_loc (locus, low_ptr);
+
+ tree high_ptr = fold_build2 (POINTER_PLUS_EXPR, u64_ptr_type, low_ptr,
+ size_int (8));
+ tree high = build_fold_indirect_ref_loc (locus, high_ptr);
+
+ auto new_high
+ = Backend::call_expression (bswap_fn, {low}, NULL_TREE, locus);
+ auto new_low
+ = Backend::call_expression (bswap_fn, {high}, NULL_TREE, locus);
+
+ tree tmp_stmt = error_mark_node;
+ Bvariable *result_var
+ = Backend::temporary_variable (fndecl, NULL_TREE,
+ template_parameter_type, NULL_TREE,
+ true, locus, &tmp_stmt);
+ ctx->add_statement (tmp_stmt);
+
+ tree addr_res
+ = build_fold_addr_expr_loc (locus, result_var->get_tree (locus));
+ tree res_ptr = fold_convert (u64_ptr_type, addr_res);
+
+ tree store_low
+ = build2 (MODIFY_EXPR, void_type_node,
+ build_fold_indirect_ref_loc (locus, res_ptr), new_low);
+ ctx->add_statement (store_low);
+
+ tree res_high_ptr = fold_build2 (POINTER_PLUS_EXPR, u64_ptr_type,
+ res_ptr, size_int (8));
+ tree store_high
+ = build2 (MODIFY_EXPR, void_type_node,
+ build_fold_indirect_ref_loc (locus, res_high_ptr),
+ new_high);
+ ctx->add_statement (store_high);
+
+ result = result_var->get_tree (locus);
+ }
+ }
+
+ auto return_statement = Backend::return_statement (fndecl, result, locus);
+
+ TREE_READONLY (result) = 1;
+
+ ctx->add_statement (return_statement);
+
+ // BUILTIN bswap FN BODY END
+
+ finalize_intrinsic_block (ctx, fndecl);
+
+ return fndecl;
+}
+
} // namespace handlers
} // namespace Compile
} // namespace Rust
diff --git a/gcc/rust/backend/rust-intrinsic-handlers.h
b/gcc/rust/backend/rust-intrinsic-handlers.h
index 1111f34f0..06174a535 100644
--- a/gcc/rust/backend/rust-intrinsic-handlers.h
+++ b/gcc/rust/backend/rust-intrinsic-handlers.h
@@ -64,6 +64,7 @@ tree move_val_init (Context *ctx, TyTy::FnType *fntype);
tree assume (Context *ctx, TyTy::FnType *fntype);
tree discriminant_value (Context *ctx, TyTy::FnType *fntype);
tree variant_count (Context *ctx, TyTy::FnType *fntype);
+tree bswap_handler (Context *ctx, TyTy::FnType *fntype);
tree prefetch_data (Context *ctx, TyTy::FnType *fntype, Prefetch kind);
diff --git a/gcc/testsuite/rust/compile/bswap.rs
b/gcc/testsuite/rust/compile/bswap.rs
new file mode 100644
index 000000000..a41607efc
--- /dev/null
+++ b/gcc/testsuite/rust/compile/bswap.rs
@@ -0,0 +1,17 @@
+// { dg-do compile }
+#![feature(intrinsics, lang_items, no_core)]
+#![no_core]
+
+#[lang = "sized"]
+pub trait Sized {}
+
+#[lang = "copy"]
+pub trait Copy {}
+
+extern "rust-intrinsic" {
+ pub fn bswap<T>(x: T) -> T; // { dg-error "bswap intrinsics can only be
used with basic integer types .got 'bool'." }
+}
+
+fn main() {
+ let _ = bswap(true);
+}
diff --git a/gcc/testsuite/rust/execute/bswap.rs
b/gcc/testsuite/rust/execute/bswap.rs
new file mode 100644
index 000000000..b64256508
--- /dev/null
+++ b/gcc/testsuite/rust/execute/bswap.rs
@@ -0,0 +1,263 @@
+// { dg-do run }
+// { dg-options "-O2" }
+
+/*
+ * Note on test design:
+ * This test uses a union-based type punning approach rather than standard
+ * integer literals (e.g., 0x..._u128) or bitwise shifts for 64-bit and
128-bit types.
+ * We do this for a few practical reasons:
+ * gccrs doesn't support 128-bit integer literals yet.
+ * Doing manual bitwise shifts on 32-bit targets currently triggers ICEs in
the frontend.
+ * Casting via raw pointers gets optimized away by GCC under -O2 due to
strict aliasing rules.
+ * Using a #[repr(C)] union gives us a safe workaround.
+ * As a bonus, by asserting against the physical memory layout ([u8; N])
instead
+ * of the logical integer value, this test is inherently endian-agnostic.
+ */
+
+#![feature(intrinsics, lang_items, no_core)]
+#![no_core]
+
+#[lang = "sized"]
+pub trait Sized {}
+
+#[lang = "copy"]
+pub trait Copy {}
+
+extern "rust-intrinsic" {
+ pub fn bswap<T>(x: T) -> T;
+ pub fn abort() -> !;
+}
+
+#[repr(C)]
+union U64Pun {
+ bytes: [u8; 8],
+ val: u64,
+}
+#[repr(C)]
+union I64Pun {
+ bytes: [u8; 8],
+ val: i64,
+}
+#[repr(C)]
+union U128Pun {
+ bytes: [u8; 16],
+ val: u128,
+}
+#[repr(C)]
+union I128Pun {
+ bytes: [u8; 16],
+ val: i128,
+}
+
+fn test_bswap_16_and_32() {
+ if bswap(0x0102_u16) != 0x0201_u16 {
+ abort();
+ }
+ if bswap(0x0102_i16) != 0x0201_i16 {
+ abort();
+ }
+
+ if bswap(0x01020304_u32) != 0x04030201_u32 {
+ abort();
+ }
+ if bswap(0x01020304_i32) != 0x04030201_i32 {
+ abort();
+ }
+}
+
+fn test_bswap_u64() {
+ let in_pun = U64Pun {
+ bytes: [1, 2, 3, 4, 5, 6, 7, 8],
+ };
+ unsafe {
+ let out_pun = U64Pun {
+ val: bswap(in_pun.val),
+ };
+ if out_pun.bytes[0] != 8 {
+ abort();
+ }
+ if out_pun.bytes[1] != 7 {
+ abort();
+ }
+ if out_pun.bytes[2] != 6 {
+ abort();
+ }
+ if out_pun.bytes[3] != 5 {
+ abort();
+ }
+ if out_pun.bytes[4] != 4 {
+ abort();
+ }
+ if out_pun.bytes[5] != 3 {
+ abort();
+ }
+ if out_pun.bytes[6] != 2 {
+ abort();
+ }
+ if out_pun.bytes[7] != 1 {
+ abort();
+ }
+ }
+}
+
+fn test_bswap_i64() {
+ let in_pun = I64Pun {
+ bytes: [1, 2, 3, 4, 5, 6, 7, 8],
+ };
+ unsafe {
+ let out_pun = I64Pun {
+ val: bswap(in_pun.val),
+ };
+ if out_pun.bytes[0] != 8 {
+ abort();
+ }
+ if out_pun.bytes[1] != 7 {
+ abort();
+ }
+ if out_pun.bytes[2] != 6 {
+ abort();
+ }
+ if out_pun.bytes[3] != 5 {
+ abort();
+ }
+ if out_pun.bytes[4] != 4 {
+ abort();
+ }
+ if out_pun.bytes[5] != 3 {
+ abort();
+ }
+ if out_pun.bytes[6] != 2 {
+ abort();
+ }
+ if out_pun.bytes[7] != 1 {
+ abort();
+ }
+ }
+}
+
+fn test_bswap_u128() {
+ let in_pun = U128Pun {
+ bytes: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
+ };
+ unsafe {
+ let out_pun = U128Pun {
+ val: bswap(in_pun.val),
+ };
+ if out_pun.bytes[0] != 16 {
+ abort();
+ }
+ if out_pun.bytes[1] != 15 {
+ abort();
+ }
+ if out_pun.bytes[2] != 14 {
+ abort();
+ }
+ if out_pun.bytes[3] != 13 {
+ abort();
+ }
+ if out_pun.bytes[4] != 12 {
+ abort();
+ }
+ if out_pun.bytes[5] != 11 {
+ abort();
+ }
+ if out_pun.bytes[6] != 10 {
+ abort();
+ }
+ if out_pun.bytes[7] != 9 {
+ abort();
+ }
+ if out_pun.bytes[8] != 8 {
+ abort();
+ }
+ if out_pun.bytes[9] != 7 {
+ abort();
+ }
+ if out_pun.bytes[10] != 6 {
+ abort();
+ }
+ if out_pun.bytes[11] != 5 {
+ abort();
+ }
+ if out_pun.bytes[12] != 4 {
+ abort();
+ }
+ if out_pun.bytes[13] != 3 {
+ abort();
+ }
+ if out_pun.bytes[14] != 2 {
+ abort();
+ }
+ if out_pun.bytes[15] != 1 {
+ abort();
+ }
+ }
+}
+
+fn test_bswap_i128() {
+ let in_pun = I128Pun {
+ bytes: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
+ };
+ unsafe {
+ let out_pun = I128Pun {
+ val: bswap(in_pun.val),
+ };
+ if out_pun.bytes[0] != 16 {
+ abort();
+ }
+ if out_pun.bytes[1] != 15 {
+ abort();
+ }
+ if out_pun.bytes[2] != 14 {
+ abort();
+ }
+ if out_pun.bytes[3] != 13 {
+ abort();
+ }
+ if out_pun.bytes[4] != 12 {
+ abort();
+ }
+ if out_pun.bytes[5] != 11 {
+ abort();
+ }
+ if out_pun.bytes[6] != 10 {
+ abort();
+ }
+ if out_pun.bytes[7] != 9 {
+ abort();
+ }
+ if out_pun.bytes[8] != 8 {
+ abort();
+ }
+ if out_pun.bytes[9] != 7 {
+ abort();
+ }
+ if out_pun.bytes[10] != 6 {
+ abort();
+ }
+ if out_pun.bytes[11] != 5 {
+ abort();
+ }
+ if out_pun.bytes[12] != 4 {
+ abort();
+ }
+ if out_pun.bytes[13] != 3 {
+ abort();
+ }
+ if out_pun.bytes[14] != 2 {
+ abort();
+ }
+ if out_pun.bytes[15] != 1 {
+ abort();
+ }
+ }
+}
+
+fn main() -> i32 {
+ test_bswap_16_and_32();
+ test_bswap_u64();
+ test_bswap_i64();
+ test_bswap_u128();
+ test_bswap_i128();
+ 0
+}
base-commit: 9b1e540f3e5d1a3486e58c9df3c18907294c33d7
--
2.53.0