Skip to content
Draft
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
73 changes: 73 additions & 0 deletions gcc/rust/backend/rust-compile-intrinsic.cc
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,8 @@ unchecked_op_handler (tree_code op)
};
}

static tree write_bytes_handler (Context *ctx, TyTy::FnType *fntype);

static inline tree copy_handler_inner (Context *ctx, TyTy::FnType *fntype,
bool overlaps);

Expand Down Expand Up @@ -220,6 +222,7 @@ static const std::map<std::string,
{"mul_with_overflow", op_with_overflow (MULT_EXPR)},
{"copy", copy_handler (true)},
{"copy_nonoverlapping", copy_handler (false)},
{"write_bytes", write_bytes_handler},
{"prefetch_read_data", prefetch_read_data},
{"prefetch_write_data", prefetch_write_data},
{"atomic_store_seqcst", atomic_store_handler (__ATOMIC_SEQ_CST)},
Expand Down Expand Up @@ -718,6 +721,76 @@ op_with_overflow_inner (Context *ctx, TyTy::FnType *fntype, tree_code op)
return fndecl;
}

/**
* fn write_bytes<T>(dst: *mut T, val: u8, count: usize);
*/
static tree
write_bytes_handler (Context *ctx, TyTy::FnType *fntype)
{
rust_assert (fntype->get_params ().size () == 3);
rust_assert (fntype->get_num_substitutions () == 1);

tree lookup = NULL_TREE;
if (check_for_cached_intrinsic (ctx, fntype, &lookup))
return lookup;

auto fndecl = compile_intrinsic_function (ctx, fntype);

// memset modifies memory, so the function is not pure.
TREE_READONLY (fndecl) = 0;
TREE_SIDE_EFFECTS (fndecl) = 1;

// setup the params
std::vector<Bvariable *> param_vars;
compile_fn_params (ctx, fntype, fndecl, &param_vars);

if (!Backend::function_set_parameters (fndecl, param_vars))
return error_mark_node;

enter_intrinsic_block (ctx, fndecl);

// BUILTIN write_bytes BODY BEGIN

auto dst = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
auto val = Backend::var_expression (param_vars[1], UNDEF_LOCATION);
auto count = Backend::var_expression (param_vars[2], UNDEF_LOCATION);

// We want to create the following GIMPLE statement:
// memset(dst, val, count * size_of::<T>());

// 1. Resolve `<T>` and get `size_of::<T>()`
auto *resolved_ty = fntype->get_substs ().at (0).get_param_ty ()->resolve ();
auto param_type = TyTyResolveCompile::compile (ctx, resolved_ty);

// 2. Build the MULT_EXPR: count * TYPE_SIZE_UNIT(T)
tree size_expr
= build2 (MULT_EXPR, size_type_node, TYPE_SIZE_UNIT (param_type), count);

// 3. Type Safety: Rust passes `val` as `u8`, but C's `memset` expects an
// `int`. We explicitly cast the `u8` tree to an integer_type_node to prevent
// ICEs in the backend.
tree val_int = build1 (CONVERT_EXPR, integer_type_node, val);

// 4. Lookup __builtin_memset and build the call
tree memset_raw = nullptr;
BuiltinsContext::get ().lookup_simple_builtin ("__builtin_memset",
&memset_raw);
rust_assert (memset_raw);

auto memset_fn = build_fold_addr_expr_loc (UNKNOWN_LOCATION, memset_raw);
auto memset_call
= Backend::call_expression (memset_fn, {dst, val_int, size_expr}, nullptr,
UNDEF_LOCATION);

ctx->add_statement (memset_call);

// BUILTIN write_bytes BODY END

finalize_intrinsic_block (ctx, fndecl);

return fndecl;
}

/**
* fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: usize);
* fn copy<T>(src: *const T, dst: *mut T, count: usize);
Expand Down
25 changes: 25 additions & 0 deletions gcc/testsuite/rust/compile/write_bytes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// { dg-do compile }
// { dg-options "-fdump-tree-gimple" }

#![feature(no_core, intrinsics, lang_items)]
#![no_core]

#[lang = "sized"]
pub trait Sized {}

extern "rust-intrinsic" {
pub fn write_bytes<T>(dst: *mut T, val: u8, count: usize);
}

// Test 1: 1-byte type. Should lower to __builtin_memset(ptr, 0, 5)
pub unsafe fn test_memset_u8(ptr: *mut u8) {
write_bytes(ptr, 0, 5);
}

// Test 2: 4-byte type. Should lower to __builtin_memset(ptr, 0, 20)
pub unsafe fn test_memset_u32(ptr: *mut u32) {
write_bytes(ptr, 0, 5);
}

// Assert that the backend successfully generated the built-in exactly twice
// { dg-final { scan-tree-dump-times "__builtin_memset" 2 "gimple" } }