Skip to content
Open
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
4 changes: 4 additions & 0 deletions compiler/rustc_codegen_gcc/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1459,6 +1459,10 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
);
}

fn vscale(&mut self, _: Self::Type) -> Self::Value {
unimplemented!("`rustc_codegen_gcc` doesn't support scalable vectors yet")
}

fn select(
&mut self,
cond: RValue<'gcc>,
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_codegen_llvm/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1226,6 +1226,10 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
}
}

fn vscale(&mut self, ty: &'ll Type) -> &'ll Value {
unsafe { llvm::LLVMRustBuildVScale(self.llbuilder, ty) }
}

fn select(
&mut self,
cond: &'ll Value,
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_codegen_llvm/src/llvm/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2139,6 +2139,8 @@ unsafe extern "C" {
IsVolatile: bool,
) -> &'a Value;

pub(crate) fn LLVMRustBuildVScale<'a>(B: &Builder<'a>, Ty: &'a Type) -> &'a Value;

pub(crate) fn LLVMRustTimeTraceProfilerInitialize();

pub(crate) fn LLVMRustTimeTraceProfilerFinishThread();
Expand Down
11 changes: 11 additions & 0 deletions compiler/rustc_codegen_ssa/src/traits/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,11 @@ pub trait BuilderMethods<'a, 'tcx>:
flags: MemFlags,
);

// Produce a value from calling the `vscale` intrinsic (containing the `vscale` multiplier that
// a scalable vector's element size and count can be multiplied by to get the real size of the
// vector)
fn vscale(&mut self, ty: Self::Type) -> Self::Value;

/// *Typed* copy for non-overlapping places.
///
/// Has a default implementation in terms of `memcpy`, but specific backends
Expand Down Expand Up @@ -513,6 +518,12 @@ pub trait BuilderMethods<'a, 'tcx>:
temp.val.store_with_flags(self, dst.with_type(layout), flags);
} else if !layout.is_zst() {
let bytes = self.const_usize(layout.size.bytes());
let bytes = if layout.peel_transparent_wrappers(self).ty.is_scalable_vector() {
let vscale = self.vscale(self.type_i64());
Comment thread
mati865 marked this conversation as resolved.
self.mul(vscale, bytes)

@scottmcm scottmcm Jun 25, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pondering: should this be unchecked_sumul?

Also, it's really not good if everywhere looking at sizes needs to look at vscale specifically. How can it be moved into layout better?

View changes since the review

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I expect we'll end up refactoring Layout in some way that makes it easier to ensure that users think about the scalable case, and that when they do, it's easy to get right. For now, this patch is sufficient to get stdarch testing which is useful to keep things working.

} else {
bytes
};
self.memcpy(dst.llval, dst.align, src.llval, src.align, bytes, flags, None);
}
}
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1497,6 +1497,10 @@ LLVMRustBuildMemMove(LLVMBuilderRef B, LLVMValueRef Dst, unsigned DstAlign,
unwrap(Size), IsVolatile));
}

extern "C" LLVMValueRef LLVMRustBuildVScale(LLVMBuilderRef B, LLVMTypeRef Ty) {
return wrap(unwrap(B)->CreateVScale(unwrap(Ty)));
}

extern "C" LLVMValueRef LLVMRustBuildMemSet(LLVMBuilderRef B, LLVMValueRef Dst,
unsigned DstAlign, LLVMValueRef Val,
LLVMValueRef Size,
Expand Down
88 changes: 88 additions & 0 deletions tests/codegen-llvm/scalable-vectors/memcpy.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
//@ compile-flags: -Copt-level=0 -Ctarget-feature=+sve,+sve2
//@ edition: 2021
//@ only-aarch64

#![crate_type = "lib"]
#![feature(simd_ffi)]
#![feature(stdarch_aarch64_sve)]

// Test that `vscale * size` is generated for `memcpy` of scalable vector types

use std::arch::aarch64::*;

#[allow(improper_ctypes)]
unsafe extern "C" {
fn svcreate2_s16_wrapper(__dst: *mut svint16x2_t, x0: *const svint16_t, x1: *const svint16_t);
fn svcreate3_s16_wrapper(
__dst: *mut svint16x3_t,
x0: *const svint16_t,
x1: *const svint16_t,
x2: *const svint16_t,
);
fn svcreate4_s16_wrapper(
__dst: *mut svint16x4_t,
x0: *const svint16_t,
x1: *const svint16_t,
x2: *const svint16_t,
x3: *const svint16_t,
);
}

pub fn foo(x0_val: svint16_t) {
unsafe {
let __pred = svptrue_b16();

let mut __c_return_value = std::mem::MaybeUninit::uninit();
svcreate2_s16_wrapper(__c_return_value.as_mut_ptr(), &raw const x0_val, &raw const x0_val);
let __c_return_value = __c_return_value.assume_init();
// CHECK: call void @svcreate2_s16_wrapper(
// CHECK-NEXT: [[VSCALE:%.*]] = call i64 @llvm.vscale.i64()
// CHECK-NEXT: [[VSCALE_SIZE:%.*]] = mul i64 [[VSCALE]], 32
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64({{.*}}, {{.*}}, i64 [[VSCALE_SIZE]]

let eq = svcmpeq_s16(
__pred,
svget2_s16::<1>(__c_return_value),
svget2_s16::<1>(__c_return_value),
);

let mut __c_return_value = std::mem::MaybeUninit::uninit();
svcreate3_s16_wrapper(
__c_return_value.as_mut_ptr(),
&raw const x0_val,
&raw const x0_val,
&raw const x0_val,
);
let __c_return_value = __c_return_value.assume_init();
// CHECK: call void @svcreate3_s16_wrapper(
// CHECK-NEXT: [[VSCALE:%.*]] = call i64 @llvm.vscale.i64()
// CHECK-NEXT: [[VSCALE_SIZE:%.*]] = mul i64 [[VSCALE]], 48
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64({{.*}}, {{.*}}, i64 [[VSCALE_SIZE]]

let eq = svcmpeq_s16(
__pred,
svget3_s16::<2>(__c_return_value),
svget3_s16::<2>(__c_return_value),
);

let mut __c_return_value = std::mem::MaybeUninit::uninit();
svcreate4_s16_wrapper(
__c_return_value.as_mut_ptr(),
&raw const x0_val,
&raw const x0_val,
&raw const x0_val,
&raw const x0_val,
);
let __c_return_value = __c_return_value.assume_init();
// CHECK: call void @svcreate4_s16_wrapper(
// CHECK-NEXT: [[VSCALE:%.*]] = call i64 @llvm.vscale.i64()
// CHECK-NEXT: [[VSCALE_SIZE:%.*]] = mul i64 [[VSCALE]], 64
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64({{.*}}, {{.*}}, i64 [[VSCALE_SIZE]]

let eq = svcmpeq_s16(
__pred,
svget4_s16::<3>(__c_return_value),
svget4_s16::<3>(__c_return_value),
);
}
}
Loading