Skip to content

Commit 2ddfff4

Browse files
philbertyCohenArthur
authored andcommitted
gccrs: Add move_val_init intrinsic
This implements it as a builtin memcpy using the generic param T for the size hint. Fixes Rust-GCC#1902 gcc/rust/ChangeLog: * backend/rust-compile-intrinsic.cc (move_val_init_handler): new intrinsice (uninit_handler): use a builtin memcpy gcc/testsuite/ChangeLog: * rust/compile/issue-1981.rs: New test. Signed-off-by: Philip Herron <[email protected]>
1 parent dcf6e3c commit 2ddfff4

File tree

2 files changed

+187
-1
lines changed

2 files changed

+187
-1
lines changed

gcc/rust/backend/rust-compile-intrinsic.cc

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ static tree
8383
op_with_overflow_inner (Context *ctx, TyTy::FnType *fntype, tree_code op);
8484
static tree
8585
uninit_handler (Context *ctx, TyTy::FnType *fntype);
86+
static tree
87+
move_val_init_handler (Context *ctx, TyTy::FnType *fntype);
8688

8789
enum class Prefetch
8890
{
@@ -205,6 +207,7 @@ static const std::map<std::string,
205207
{"unchecked_shl", unchecked_op_handler (LSHIFT_EXPR)},
206208
{"unchecked_shr", unchecked_op_handler (RSHIFT_EXPR)},
207209
{"uninit", uninit_handler},
210+
{"move_val_init", move_val_init_handler},
208211
};
209212

210213
Intrinsics::Intrinsics (Context *ctx) : ctx (ctx) {}
@@ -1001,7 +1004,7 @@ uninit_handler (Context *ctx, TyTy::FnType *fntype)
10011004

10021005
auto fndecl = compile_intrinsic_function (ctx, fntype);
10031006

1004-
// get the template parameter type tree fn size_of<T>();
1007+
// get the template parameter type tree fn uninit<T>();
10051008
rust_assert (fntype->get_num_substitutions () == 1);
10061009
auto &param_mapping = fntype->get_substs ().at (0);
10071010
const TyTy::ParamType *param_tyty = param_mapping.get_param_ty ();
@@ -1042,5 +1045,56 @@ uninit_handler (Context *ctx, TyTy::FnType *fntype)
10421045
return fndecl;
10431046
}
10441047

1048+
static tree
1049+
move_val_init_handler (Context *ctx, TyTy::FnType *fntype)
1050+
{
1051+
rust_assert (fntype->get_params ().size () == 2);
1052+
1053+
tree lookup = NULL_TREE;
1054+
if (check_for_cached_intrinsic (ctx, fntype, &lookup))
1055+
return lookup;
1056+
1057+
auto fndecl = compile_intrinsic_function (ctx, fntype);
1058+
1059+
// get the template parameter type tree fn size_of<T>();
1060+
rust_assert (fntype->get_num_substitutions () == 1);
1061+
auto &param_mapping = fntype->get_substs ().at (0);
1062+
const TyTy::ParamType *param_tyty = param_mapping.get_param_ty ();
1063+
TyTy::BaseType *resolved_tyty = param_tyty->resolve ();
1064+
tree template_parameter_type
1065+
= TyTyResolveCompile::compile (ctx, resolved_tyty);
1066+
1067+
std::vector<Bvariable *> param_vars;
1068+
compile_fn_params (ctx, fntype, fndecl, &param_vars);
1069+
1070+
if (!ctx->get_backend ()->function_set_parameters (fndecl, param_vars))
1071+
return error_mark_node;
1072+
1073+
enter_intrinsic_block (ctx, fndecl);
1074+
1075+
// BUILTIN size_of FN BODY BEGIN
1076+
1077+
tree dst = ctx->get_backend ()->var_expression (param_vars[0], Location ());
1078+
tree src = ctx->get_backend ()->var_expression (param_vars[1], Location ());
1079+
tree size = TYPE_SIZE_UNIT (template_parameter_type);
1080+
1081+
tree memcpy_builtin = error_mark_node;
1082+
BuiltinsContext::get ().lookup_simple_builtin ("memcpy", &memcpy_builtin);
1083+
rust_assert (memcpy_builtin != error_mark_node);
1084+
1085+
src = build_fold_addr_expr_loc (BUILTINS_LOCATION, src);
1086+
tree memset_call = build_call_expr_loc (BUILTINS_LOCATION, memcpy_builtin, 3,
1087+
dst, src, size);
1088+
TREE_READONLY (memset_call) = 0;
1089+
TREE_SIDE_EFFECTS (memset_call) = 1;
1090+
1091+
ctx->add_statement (memset_call);
1092+
// BUILTIN size_of FN BODY END
1093+
1094+
finalize_intrinsic_block (ctx, fndecl);
1095+
1096+
return fndecl;
1097+
}
1098+
10451099
} // namespace Compile
10461100
} // namespace Rust
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
mod intrinsics {
2+
extern "rust-intrinsic" {
3+
pub fn offset<T>(ptr: *const T, count: isize) -> *const T;
4+
pub fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: usize);
5+
pub fn move_val_init<T>(dst: *mut T, src: T);
6+
pub fn uninit<T>() -> T;
7+
}
8+
}
9+
10+
mod ptr {
11+
#[lang = "const_ptr"]
12+
impl<T> *const T {
13+
pub unsafe fn offset(self, count: isize) -> *const T {
14+
intrinsics::offset(self, count)
15+
}
16+
}
17+
18+
#[lang = "mut_ptr"]
19+
impl<T> *mut T {
20+
pub unsafe fn offset(self, count: isize) -> *mut T {
21+
intrinsics::offset(self, count) as *mut T
22+
}
23+
}
24+
25+
pub unsafe fn swap_nonoverlapping<T>(x: *mut T, y: *mut T, count: usize) {
26+
let x = x as *mut T;
27+
let y = y as *mut T;
28+
let len = mem::size_of::<T>() * count;
29+
swap_nonoverlapping_bytes(x, y, len)
30+
}
31+
32+
pub unsafe fn swap_nonoverlapping_one<T>(x: *mut T, y: *mut T) {
33+
// For types smaller than the block optimization below,
34+
// just swap directly to avoid pessimizing codegen.
35+
if mem::size_of::<T>() < 32 {
36+
let z = read(x);
37+
intrinsics::copy_nonoverlapping(y, x, 1);
38+
write(y, z);
39+
} else {
40+
swap_nonoverlapping(x, y, 1);
41+
}
42+
}
43+
44+
pub unsafe fn write<T>(dst: *mut T, src: T) {
45+
intrinsics::move_val_init(&mut *dst, src)
46+
}
47+
48+
pub unsafe fn read<T>(src: *const T) -> T {
49+
let mut tmp: T = mem::uninitialized();
50+
intrinsics::copy_nonoverlapping(src, &mut tmp, 1);
51+
tmp
52+
}
53+
54+
unsafe fn swap_nonoverlapping_bytes(x: *mut u8, y: *mut u8, len: usize) {
55+
struct Block(u64, u64, u64, u64);
56+
struct UnalignedBlock(u64, u64, u64, u64);
57+
58+
let block_size = mem::size_of::<Block>();
59+
60+
// Loop through x & y, copying them `Block` at a time
61+
// The optimizer should unroll the loop fully for most types
62+
// N.B. We can't use a for loop as the `range` impl calls `mem::swap` recursively
63+
let mut i = 0;
64+
while i + block_size <= len {
65+
// Create some uninitialized memory as scratch space
66+
// Declaring `t` here avoids aligning the stack when this loop is unused
67+
let mut t: Block = mem::uninitialized();
68+
let t = &mut t as *mut _ as *mut u8;
69+
let x = x.offset(i as isize);
70+
let y = y.offset(i as isize);
71+
72+
// Swap a block of bytes of x & y, using t as a temporary buffer
73+
// This should be optimized into efficient SIMD operations where available
74+
intrinsics::copy_nonoverlapping(x, t, block_size);
75+
intrinsics::copy_nonoverlapping(y, x, block_size);
76+
intrinsics::copy_nonoverlapping(t, y, block_size);
77+
i += block_size;
78+
}
79+
80+
if i < len {
81+
// Swap any remaining bytes
82+
let mut t: UnalignedBlock = mem::uninitialized();
83+
let rem = len - i;
84+
85+
let t = &mut t as *mut _ as *mut u8;
86+
let x = x.offset(i as isize);
87+
let y = y.offset(i as isize);
88+
89+
intrinsics::copy_nonoverlapping(x, t, rem);
90+
intrinsics::copy_nonoverlapping(y, x, rem);
91+
intrinsics::copy_nonoverlapping(t, y, rem);
92+
}
93+
}
94+
}
95+
96+
mod mem {
97+
extern "rust-intrinsic" {
98+
pub fn transmute<T, U>(_: T) -> U;
99+
pub fn size_of<T>() -> usize;
100+
}
101+
102+
pub fn swap<T>(x: &mut T, y: &mut T) {
103+
unsafe {
104+
ptr::swap_nonoverlapping_one(x, y);
105+
}
106+
}
107+
108+
pub fn replace<T>(dest: &mut T, mut src: T) -> T {
109+
swap(dest, &mut src);
110+
src
111+
}
112+
113+
pub unsafe fn uninitialized<T>() -> T {
114+
intrinsics::uninit()
115+
}
116+
}
117+
118+
trait Step {
119+
fn replace_zero(&mut self) -> Self;
120+
}
121+
122+
impl Step for i32 {
123+
fn replace_zero(&mut self) -> Self {
124+
mem::replace(self, 0)
125+
}
126+
}
127+
128+
fn main() -> i32 {
129+
let a = 123;
130+
a.replace_zero();
131+
a
132+
}

0 commit comments

Comments
 (0)