Skip to content

Commit 463ad90

Browse files
committed
Support memcpy/memmove with differing src/dst alignment
If LLVM 7 is used, generate memcpy/memmove with differing src/dst alignment. I've added new FFI functions to construct these through the builder API, which is more convenient than dealing with differing intrinsic signatures depending on the LLVM version.
1 parent ac70882 commit 463ad90

File tree

11 files changed

+98
-51
lines changed

11 files changed

+98
-51
lines changed

src/librustc_codegen_llvm/abi.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -225,9 +225,10 @@ impl ArgTypeExt<'ll, 'tcx> for ArgType<'tcx, Ty<'tcx>> {
225225
// ...and then memcpy it to the intended destination.
226226
base::call_memcpy(bx,
227227
bx.pointercast(dst.llval, Type::i8p(cx)),
228+
self.layout.align,
228229
bx.pointercast(llscratch, Type::i8p(cx)),
230+
scratch_align,
229231
C_usize(cx, self.layout.size.bytes()),
230-
self.layout.align.min(scratch_align),
231232
MemFlags::empty());
232233

233234
bx.lifetime_end(llscratch, scratch_size);

src/librustc_codegen_llvm/base.rs

+12-15
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ use mir::place::PlaceRef;
5353
use attributes;
5454
use builder::{Builder, MemFlags};
5555
use callee;
56-
use common::{C_bool, C_bytes_in_context, C_i32, C_usize};
56+
use common::{C_bool, C_bytes_in_context, C_usize};
5757
use rustc_mir::monomorphize::item::DefPathBasedNames;
5858
use common::{C_struct_in_context, C_array, val_ty};
5959
use consts;
@@ -77,7 +77,6 @@ use rustc_data_structures::sync::Lrc;
7777
use std::any::Any;
7878
use std::cmp;
7979
use std::ffi::CString;
80-
use std::i32;
8180
use std::ops::{Deref, DerefMut};
8281
use std::sync::mpsc;
8382
use std::time::{Instant, Duration};
@@ -319,8 +318,8 @@ pub fn coerce_unsized_into(
319318
}
320319

321320
if src_f.layout.ty == dst_f.layout.ty {
322-
memcpy_ty(bx, dst_f.llval, src_f.llval, src_f.layout,
323-
src_f.align.min(dst_f.align), MemFlags::empty());
321+
memcpy_ty(bx, dst_f.llval, dst_f.align, src_f.llval, src_f.align,
322+
src_f.layout, MemFlags::empty());
324323
} else {
325324
coerce_unsized_into(bx, src_f, dst_f);
326325
}
@@ -420,44 +419,42 @@ pub fn to_immediate_scalar(
420419
pub fn call_memcpy(
421420
bx: &Builder<'_, 'll, '_>,
422421
dst: &'ll Value,
422+
dst_align: Align,
423423
src: &'ll Value,
424+
src_align: Align,
424425
n_bytes: &'ll Value,
425-
align: Align,
426426
flags: MemFlags,
427427
) {
428428
if flags.contains(MemFlags::NONTEMPORAL) {
429429
// HACK(nox): This is inefficient but there is no nontemporal memcpy.
430-
let val = bx.load(src, align);
430+
let val = bx.load(src, src_align);
431431
let ptr = bx.pointercast(dst, val_ty(val).ptr_to());
432-
bx.store_with_flags(val, ptr, align, flags);
432+
bx.store_with_flags(val, ptr, dst_align, flags);
433433
return;
434434
}
435435
let cx = bx.cx;
436-
let ptr_width = &cx.sess().target.target.target_pointer_width;
437-
let key = format!("llvm.memcpy.p0i8.p0i8.i{}", ptr_width);
438-
let memcpy = cx.get_intrinsic(&key);
439436
let src_ptr = bx.pointercast(src, Type::i8p(cx));
440437
let dst_ptr = bx.pointercast(dst, Type::i8p(cx));
441438
let size = bx.intcast(n_bytes, cx.isize_ty, false);
442-
let align = C_i32(cx, align.abi() as i32);
443-
let volatile = C_bool(cx, flags.contains(MemFlags::VOLATILE));
444-
bx.call(memcpy, &[dst_ptr, src_ptr, size, align, volatile], None);
439+
let volatile = flags.contains(MemFlags::VOLATILE);
440+
bx.memcpy(dst_ptr, dst_align.abi(), src_ptr, src_align.abi(), size, volatile);
445441
}
446442

447443
pub fn memcpy_ty(
448444
bx: &Builder<'_, 'll, 'tcx>,
449445
dst: &'ll Value,
446+
dst_align: Align,
450447
src: &'ll Value,
448+
src_align: Align,
451449
layout: TyLayout<'tcx>,
452-
align: Align,
453450
flags: MemFlags,
454451
) {
455452
let size = layout.size.bytes();
456453
if size == 0 {
457454
return;
458455
}
459456

460-
call_memcpy(bx, dst, src, C_usize(bx.cx, size), align, flags);
457+
call_memcpy(bx, dst, dst_align, src, src_align, C_usize(bx.cx, size), flags);
461458
}
462459

463460
pub fn call_memset(

src/librustc_codegen_llvm/builder.rs

+18
Original file line numberDiff line numberDiff line change
@@ -784,6 +784,24 @@ impl Builder<'a, 'll, 'tcx> {
784784
}
785785
}
786786

787+
pub fn memcpy(&self, dst: &'ll Value, dst_align: u64,
788+
src: &'ll Value, src_align: u64,
789+
size: &'ll Value, is_volatile: bool) -> &'ll Value {
790+
unsafe {
791+
llvm::LLVMRustBuildMemCpy(self.llbuilder, dst, dst_align as c_uint,
792+
src, src_align as c_uint, size, is_volatile)
793+
}
794+
}
795+
796+
pub fn memmove(&self, dst: &'ll Value, dst_align: u64,
797+
src: &'ll Value, src_align: u64,
798+
size: &'ll Value, is_volatile: bool) -> &'ll Value {
799+
unsafe {
800+
llvm::LLVMRustBuildMemMove(self.llbuilder, dst, dst_align as c_uint,
801+
src, src_align as c_uint, size, is_volatile)
802+
}
803+
}
804+
787805
pub fn minnum(&self, lhs: &'ll Value, rhs: &'ll Value) -> &'ll Value {
788806
self.count_insn("minnum");
789807
unsafe {

src/librustc_codegen_llvm/context.rs

-6
Original file line numberDiff line numberDiff line change
@@ -530,12 +530,6 @@ fn declare_intrinsic(cx: &CodegenCx<'ll, '_>, key: &str) -> Option<&'ll Value> {
530530
let t_v4f64 = Type::vector(t_f64, 4);
531531
let t_v8f64 = Type::vector(t_f64, 8);
532532

533-
ifn!("llvm.memcpy.p0i8.p0i8.i16", fn(i8p, i8p, t_i16, t_i32, i1) -> void);
534-
ifn!("llvm.memcpy.p0i8.p0i8.i32", fn(i8p, i8p, t_i32, t_i32, i1) -> void);
535-
ifn!("llvm.memcpy.p0i8.p0i8.i64", fn(i8p, i8p, t_i64, t_i32, i1) -> void);
536-
ifn!("llvm.memmove.p0i8.p0i8.i16", fn(i8p, i8p, t_i16, t_i32, i1) -> void);
537-
ifn!("llvm.memmove.p0i8.p0i8.i32", fn(i8p, i8p, t_i32, t_i32, i1) -> void);
538-
ifn!("llvm.memmove.p0i8.p0i8.i64", fn(i8p, i8p, t_i64, t_i32, i1) -> void);
539533
ifn!("llvm.memset.p0i8.i16", fn(i8p, t_i8, t_i16, t_i32, i1) -> void);
540534
ifn!("llvm.memset.p0i8.i32", fn(i8p, t_i8, t_i32, t_i32, i1) -> void);
541535
ifn!("llvm.memset.p0i8.i64", fn(i8p, t_i8, t_i64, t_i32, i1) -> void);

src/librustc_codegen_llvm/intrinsic.rs

+7-21
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use glue;
2323
use type_::Type;
2424
use type_of::LayoutLlvmExt;
2525
use rustc::ty::{self, Ty};
26-
use rustc::ty::layout::{HasDataLayout, LayoutOf};
26+
use rustc::ty::layout::LayoutOf;
2727
use rustc::hir;
2828
use syntax::ast;
2929
use syntax::symbol::Symbol;
@@ -690,28 +690,14 @@ fn copy_intrinsic(
690690
let cx = bx.cx;
691691
let (size, align) = cx.size_and_align_of(ty);
692692
let size = C_usize(cx, size.bytes());
693-
let align = C_i32(cx, align.abi() as i32);
694-
695-
let operation = if allow_overlap {
696-
"memmove"
697-
} else {
698-
"memcpy"
699-
};
700-
701-
let name = format!("llvm.{}.p0i8.p0i8.i{}", operation,
702-
cx.data_layout().pointer_size.bits());
703-
693+
let align = align.abi();
704694
let dst_ptr = bx.pointercast(dst, Type::i8p(cx));
705695
let src_ptr = bx.pointercast(src, Type::i8p(cx));
706-
let llfn = cx.get_intrinsic(&name);
707-
708-
bx.call(llfn,
709-
&[dst_ptr,
710-
src_ptr,
711-
bx.mul(size, count),
712-
align,
713-
C_bool(cx, volatile)],
714-
None)
696+
if allow_overlap {
697+
bx.memmove(dst_ptr, align, src_ptr, align, bx.mul(size, count), volatile)
698+
} else {
699+
bx.memcpy(dst_ptr, align, src_ptr, align, bx.mul(size, count), volatile)
700+
}
715701
}
716702

717703
fn memset_intrinsic(

src/librustc_codegen_llvm/llvm/ffi.rs

+16
Original file line numberDiff line numberDiff line change
@@ -998,6 +998,22 @@ extern "C" {
998998
Bundle: Option<&OperandBundleDef<'a>>,
999999
Name: *const c_char)
10001000
-> &'a Value;
1001+
pub fn LLVMRustBuildMemCpy(B: &Builder<'a>,
1002+
Dst: &'a Value,
1003+
DstAlign: c_uint,
1004+
Src: &'a Value,
1005+
SrcAlign: c_uint,
1006+
Size: &'a Value,
1007+
IsVolatile: bool)
1008+
-> &'a Value;
1009+
pub fn LLVMRustBuildMemMove(B: &Builder<'a>,
1010+
Dst: &'a Value,
1011+
DstAlign: c_uint,
1012+
Src: &'a Value,
1013+
SrcAlign: c_uint,
1014+
Size: &'a Value,
1015+
IsVolatile: bool)
1016+
-> &'a Value;
10011017
pub fn LLVMBuildSelect(B: &Builder<'a>,
10021018
If: &'a Value,
10031019
Then: &'a Value,

src/librustc_codegen_llvm/mir/block.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -784,7 +784,8 @@ impl FunctionCx<'a, 'll, 'tcx> {
784784
// have scary latent bugs around.
785785

786786
let scratch = PlaceRef::alloca(bx, arg.layout, "arg");
787-
base::memcpy_ty(bx, scratch.llval, llval, op.layout, align, MemFlags::empty());
787+
base::memcpy_ty(bx, scratch.llval, scratch.align, llval, align,
788+
op.layout, MemFlags::empty());
788789
(scratch.llval, scratch.align, true)
789790
} else {
790791
(llval, align, true)

src/librustc_codegen_llvm/mir/operand.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -282,8 +282,8 @@ impl OperandValue<'ll> {
282282
}
283283
match self {
284284
OperandValue::Ref(r, None, source_align) => {
285-
base::memcpy_ty(bx, dest.llval, r, dest.layout,
286-
source_align.min(dest.align), flags)
285+
base::memcpy_ty(bx, dest.llval, dest.align, r, source_align,
286+
dest.layout, flags)
287287
}
288288
OperandValue::Ref(_, Some(_), _) => {
289289
bug!("cannot directly store unsized values");
@@ -324,7 +324,7 @@ impl OperandValue<'ll> {
324324
// Allocate an appropriate region on the stack, and copy the value into it
325325
let (llsize, _) = glue::size_and_align_of_dst(&bx, unsized_ty, Some(llextra));
326326
let lldst = bx.array_alloca(Type::i8(bx.cx), llsize, "unsized_tmp", max_align);
327-
base::call_memcpy(&bx, lldst, llptr, llsize, min_align, flags);
327+
base::call_memcpy(&bx, lldst, max_align, llptr, min_align, llsize, flags);
328328

329329
// Store the allocated region and the extra to the indirect place.
330330
let indirect_operand = OperandValue::Pair(lldst, llextra);

src/rustllvm/RustWrapper.cpp

+34
Original file line numberDiff line numberDiff line change
@@ -1239,6 +1239,40 @@ extern "C" LLVMValueRef LLVMRustBuildCall(LLVMBuilderRef B, LLVMValueRef Fn,
12391239
unwrap(Fn), makeArrayRef(unwrap(Args), NumArgs), Bundles, Name));
12401240
}
12411241

1242+
extern "C" LLVMValueRef LLVMRustBuildMemCpy(LLVMBuilderRef B,
1243+
LLVMValueRef Dst, unsigned DstAlign,
1244+
LLVMValueRef Src, unsigned SrcAlign,
1245+
LLVMValueRef Size, bool IsVolatile) {
1246+
#if LLVM_VERSION_GE(7, 0)
1247+
return wrap(unwrap(B)->CreateMemCpy(
1248+
unwrap(Dst), DstAlign,
1249+
unwrap(Src), SrcAlign,
1250+
unwrap(Size), IsVolatile));
1251+
#else
1252+
unsigned Align = std::min(DstAlign, SrcAlign);
1253+
return wrap(unwrap(B)->CreateMemCpy(
1254+
unwrap(Dst), unwrap(Src),
1255+
unwrap(Size), Align, IsVolatile));
1256+
#endif
1257+
}
1258+
1259+
extern "C" LLVMValueRef LLVMRustBuildMemMove(LLVMBuilderRef B,
1260+
LLVMValueRef Dst, unsigned DstAlign,
1261+
LLVMValueRef Src, unsigned SrcAlign,
1262+
LLVMValueRef Size, bool IsVolatile) {
1263+
#if LLVM_VERSION_GE(7, 0)
1264+
return wrap(unwrap(B)->CreateMemMove(
1265+
unwrap(Dst), DstAlign,
1266+
unwrap(Src), SrcAlign,
1267+
unwrap(Size), IsVolatile));
1268+
#else
1269+
unsigned Align = std::min(DstAlign, SrcAlign);
1270+
return wrap(unwrap(B)->CreateMemMove(
1271+
unwrap(Dst), unwrap(Src),
1272+
unwrap(Size), Align, IsVolatile));
1273+
#endif
1274+
}
1275+
12421276
extern "C" LLVMValueRef
12431277
LLVMRustBuildInvoke(LLVMBuilderRef B, LLVMValueRef Fn, LLVMValueRef *Args,
12441278
unsigned NumArgs, LLVMBasicBlockRef Then,

src/test/codegen/packed.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ pub struct BigPacked2 {
6565
pub fn call_pkd1(f: fn() -> Array) -> BigPacked1 {
6666
// CHECK: [[ALLOCA:%[_a-z0-9]+]] = alloca %Array
6767
// CHECK: call void %{{.*}}(%Array* noalias nocapture sret dereferenceable(32) [[ALLOCA]])
68-
// CHECK: call void @llvm.memcpy.{{.*}}(i8* align 1 %{{.*}}, i8* align 1 %{{.*}}, i{{[0-9]+}} 32, i1 false)
68+
// CHECK: call void @llvm.memcpy.{{.*}}(i8* align 1 %{{.*}}, i8* align 4 %{{.*}}, i{{[0-9]+}} 32, i1 false)
6969
// check that calls whose destination is a field of a packed struct
7070
// go through an alloca rather than calling the function with an
7171
// unaligned destination.
@@ -77,7 +77,7 @@ pub fn call_pkd1(f: fn() -> Array) -> BigPacked1 {
7777
pub fn call_pkd2(f: fn() -> Array) -> BigPacked2 {
7878
// CHECK: [[ALLOCA:%[_a-z0-9]+]] = alloca %Array
7979
// CHECK: call void %{{.*}}(%Array* noalias nocapture sret dereferenceable(32) [[ALLOCA]])
80-
// CHECK: call void @llvm.memcpy.{{.*}}(i8* align 2 %{{.*}}, i8* align 2 %{{.*}}, i{{[0-9]+}} 32, i1 false)
80+
// CHECK: call void @llvm.memcpy.{{.*}}(i8* align 2 %{{.*}}, i8* align 4 %{{.*}}, i{{[0-9]+}} 32, i1 false)
8181
// check that calls whose destination is a field of a packed struct
8282
// go through an alloca rather than calling the function with an
8383
// unaligned destination.

src/test/codegen/stores.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ pub fn small_array_alignment(x: &mut [i8; 4], y: [i8; 4]) {
3131
// CHECK: store i32 %0, i32* [[TMP]]
3232
// CHECK: [[Y8:%[0-9]+]] = bitcast [4 x i8]* %y to i8*
3333
// CHECK: [[TMP8:%[0-9]+]] = bitcast i32* [[TMP]] to i8*
34-
// CHECK: call void @llvm.memcpy.{{.*}}(i8* align 1 [[Y8]], i8* align 1 [[TMP8]], i{{[0-9]+}} 4, i1 false)
34+
// CHECK: call void @llvm.memcpy.{{.*}}(i8* align 1 [[Y8]], i8* align 4 [[TMP8]], i{{[0-9]+}} 4, i1 false)
3535
*x = y;
3636
}
3737

@@ -45,6 +45,6 @@ pub fn small_struct_alignment(x: &mut Bytes, y: Bytes) {
4545
// CHECK: store i32 %0, i32* [[TMP]]
4646
// CHECK: [[Y8:%[0-9]+]] = bitcast %Bytes* %y to i8*
4747
// CHECK: [[TMP8:%[0-9]+]] = bitcast i32* [[TMP]] to i8*
48-
// CHECK: call void @llvm.memcpy.{{.*}}(i8* align 1 [[Y8]], i8* align 1 [[TMP8]], i{{[0-9]+}} 4, i1 false)
48+
// CHECK: call void @llvm.memcpy.{{.*}}(i8* align 1 [[Y8]], i8* align 4 [[TMP8]], i{{[0-9]+}} 4, i1 false)
4949
*x = y;
5050
}

0 commit comments

Comments
 (0)