Skip to content

Commit 44209f2

Browse files
authored
Unrolled build for rust-lang#122212
Rollup merge of rust-lang#122212 - erikdesjardins:byval-align2, r=wesleywiser Copy byval argument to alloca if alignment is insufficient Fixes rust-lang#122211 "Ignore whitespace" recommended.
2 parents f4b771b + 818f130 commit 44209f2

File tree

4 files changed

+221
-75
lines changed

4 files changed

+221
-75
lines changed

compiler/rustc_codegen_llvm/src/abi.rs

+55-49
Original file line numberDiff line numberDiff line change
@@ -203,57 +203,63 @@ impl<'ll, 'tcx> ArgAbiExt<'ll, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> {
203203
val: &'ll Value,
204204
dst: PlaceRef<'tcx, &'ll Value>,
205205
) {
206-
if self.is_ignore() {
207-
return;
208-
}
209-
if self.is_sized_indirect() {
210-
OperandValue::Ref(val, None, self.layout.align.abi).store(bx, dst)
211-
} else if self.is_unsized_indirect() {
212-
bug!("unsized `ArgAbi` must be handled through `store_fn_arg`");
213-
} else if let PassMode::Cast { cast, pad_i32: _ } = &self.mode {
214-
// FIXME(eddyb): Figure out when the simpler Store is safe, clang
215-
// uses it for i16 -> {i8, i8}, but not for i24 -> {i8, i8, i8}.
216-
let can_store_through_cast_ptr = false;
217-
if can_store_through_cast_ptr {
218-
bx.store(val, dst.llval, self.layout.align.abi);
219-
} else {
220-
// The actual return type is a struct, but the ABI
221-
// adaptation code has cast it into some scalar type. The
222-
// code that follows is the only reliable way I have
223-
// found to do a transform like i64 -> {i32,i32}.
224-
// Basically we dump the data onto the stack then memcpy it.
225-
//
226-
// Other approaches I tried:
227-
// - Casting rust ret pointer to the foreign type and using Store
228-
// is (a) unsafe if size of foreign type > size of rust type and
229-
// (b) runs afoul of strict aliasing rules, yielding invalid
230-
// assembly under -O (specifically, the store gets removed).
231-
// - Truncating foreign type to correct integral type and then
232-
// bitcasting to the struct type yields invalid cast errors.
233-
234-
// We instead thus allocate some scratch space...
235-
let scratch_size = cast.size(bx);
236-
let scratch_align = cast.align(bx);
237-
let llscratch = bx.alloca(cast.llvm_type(bx), scratch_align);
238-
bx.lifetime_start(llscratch, scratch_size);
239-
240-
// ... where we first store the value...
241-
bx.store(val, llscratch, scratch_align);
242-
243-
// ... and then memcpy it to the intended destination.
244-
bx.memcpy(
245-
dst.llval,
246-
self.layout.align.abi,
247-
llscratch,
248-
scratch_align,
249-
bx.const_usize(self.layout.size.bytes()),
250-
MemFlags::empty(),
251-
);
206+
match &self.mode {
207+
PassMode::Ignore => {}
208+
// Sized indirect arguments
209+
PassMode::Indirect { attrs, meta_attrs: None, on_stack: _ } => {
210+
let align = attrs.pointee_align.unwrap_or(self.layout.align.abi);
211+
OperandValue::Ref(val, None, align).store(bx, dst);
212+
}
213+
// Unsized indirect qrguments
214+
PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ } => {
215+
bug!("unsized `ArgAbi` must be handled through `store_fn_arg`");
216+
}
217+
PassMode::Cast { cast, pad_i32: _ } => {
218+
// FIXME(eddyb): Figure out when the simpler Store is safe, clang
219+
// uses it for i16 -> {i8, i8}, but not for i24 -> {i8, i8, i8}.
220+
let can_store_through_cast_ptr = false;
221+
if can_store_through_cast_ptr {
222+
bx.store(val, dst.llval, self.layout.align.abi);
223+
} else {
224+
// The actual return type is a struct, but the ABI
225+
// adaptation code has cast it into some scalar type. The
226+
// code that follows is the only reliable way I have
227+
// found to do a transform like i64 -> {i32,i32}.
228+
// Basically we dump the data onto the stack then memcpy it.
229+
//
230+
// Other approaches I tried:
231+
// - Casting rust ret pointer to the foreign type and using Store
232+
// is (a) unsafe if size of foreign type > size of rust type and
233+
// (b) runs afoul of strict aliasing rules, yielding invalid
234+
// assembly under -O (specifically, the store gets removed).
235+
// - Truncating foreign type to correct integral type and then
236+
// bitcasting to the struct type yields invalid cast errors.
237+
238+
// We instead thus allocate some scratch space...
239+
let scratch_size = cast.size(bx);
240+
let scratch_align = cast.align(bx);
241+
let llscratch = bx.alloca(cast.llvm_type(bx), scratch_align);
242+
bx.lifetime_start(llscratch, scratch_size);
243+
244+
// ... where we first store the value...
245+
bx.store(val, llscratch, scratch_align);
246+
247+
// ... and then memcpy it to the intended destination.
248+
bx.memcpy(
249+
dst.llval,
250+
self.layout.align.abi,
251+
llscratch,
252+
scratch_align,
253+
bx.const_usize(self.layout.size.bytes()),
254+
MemFlags::empty(),
255+
);
252256

253-
bx.lifetime_end(llscratch, scratch_size);
257+
bx.lifetime_end(llscratch, scratch_size);
258+
}
259+
}
260+
_ => {
261+
OperandRef::from_immediate_or_packed_pair(bx, val, self.layout).val.store(bx, dst);
254262
}
255-
} else {
256-
OperandRef::from_immediate_or_packed_pair(bx, val, self.layout).val.store(bx, dst);
257263
}
258264
}
259265

compiler/rustc_codegen_ssa/src/mir/mod.rs

+39-23
Original file line numberDiff line numberDiff line change
@@ -377,29 +377,45 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
377377
}
378378
}
379379

380-
if arg.is_sized_indirect() {
381-
// Don't copy an indirect argument to an alloca, the caller
382-
// already put it in a temporary alloca and gave it up.
383-
// FIXME: lifetimes
384-
let llarg = bx.get_param(llarg_idx);
385-
llarg_idx += 1;
386-
LocalRef::Place(PlaceRef::new_sized(llarg, arg.layout))
387-
} else if arg.is_unsized_indirect() {
388-
// As the storage for the indirect argument lives during
389-
// the whole function call, we just copy the fat pointer.
390-
let llarg = bx.get_param(llarg_idx);
391-
llarg_idx += 1;
392-
let llextra = bx.get_param(llarg_idx);
393-
llarg_idx += 1;
394-
let indirect_operand = OperandValue::Pair(llarg, llextra);
395-
396-
let tmp = PlaceRef::alloca_unsized_indirect(bx, arg.layout);
397-
indirect_operand.store(bx, tmp);
398-
LocalRef::UnsizedPlace(tmp)
399-
} else {
400-
let tmp = PlaceRef::alloca(bx, arg.layout);
401-
bx.store_fn_arg(arg, &mut llarg_idx, tmp);
402-
LocalRef::Place(tmp)
380+
match arg.mode {
381+
// Sized indirect arguments
382+
PassMode::Indirect { attrs, meta_attrs: None, on_stack: _ } => {
383+
// Don't copy an indirect argument to an alloca, the caller already put it
384+
// in a temporary alloca and gave it up.
385+
// FIXME: lifetimes
386+
if let Some(pointee_align) = attrs.pointee_align
387+
&& pointee_align < arg.layout.align.abi
388+
{
389+
// ...unless the argument is underaligned, then we need to copy it to
390+
// a higher-aligned alloca.
391+
let tmp = PlaceRef::alloca(bx, arg.layout);
392+
bx.store_fn_arg(arg, &mut llarg_idx, tmp);
393+
LocalRef::Place(tmp)
394+
} else {
395+
let llarg = bx.get_param(llarg_idx);
396+
llarg_idx += 1;
397+
LocalRef::Place(PlaceRef::new_sized(llarg, arg.layout))
398+
}
399+
}
400+
// Unsized indirect qrguments
401+
PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ } => {
402+
// As the storage for the indirect argument lives during
403+
// the whole function call, we just copy the fat pointer.
404+
let llarg = bx.get_param(llarg_idx);
405+
llarg_idx += 1;
406+
let llextra = bx.get_param(llarg_idx);
407+
llarg_idx += 1;
408+
let indirect_operand = OperandValue::Pair(llarg, llextra);
409+
410+
let tmp = PlaceRef::alloca_unsized_indirect(bx, arg.layout);
411+
indirect_operand.store(bx, tmp);
412+
LocalRef::UnsizedPlace(tmp)
413+
}
414+
_ => {
415+
let tmp = PlaceRef::alloca(bx, arg.layout);
416+
bx.store_fn_arg(arg, &mut llarg_idx, tmp);
417+
LocalRef::Place(tmp)
418+
}
403419
}
404420
})
405421
.collect::<Vec<_>>();

compiler/rustc_target/src/abi/call/mod.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -633,10 +633,8 @@ impl<'a, Ty> ArgAbi<'a, Ty> {
633633
/// If the resulting alignment differs from the type's alignment,
634634
/// the argument will be copied to an alloca with sufficient alignment,
635635
/// either in the caller (if the type's alignment is lower than the byval alignment)
636-
/// or in the callee (if the type's alignment is higher than the byval alignment),
636+
/// or in the callee (if the type's alignment is higher than the byval alignment),
637637
/// to ensure that Rust code never sees an underaligned pointer.
638-
///
639-
/// † This is currently broken, see <https://github.com/rust-lang/rust/pull/122212>.
640638
pub fn make_indirect_byval(&mut self, byval_align: Option<Align>) {
641639
assert!(!self.layout.is_unsized(), "used byval ABI for unsized layout");
642640
self.make_indirect();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
// ignore-tidy-linelength
2+
//@ revisions:i686-linux x86_64-linux
3+
4+
//@[i686-linux] compile-flags: --target i686-unknown-linux-gnu
5+
//@[i686-linux] needs-llvm-components: x86
6+
//@[x86_64-linux] compile-flags: --target x86_64-unknown-linux-gnu
7+
//@[x86_64-linux] needs-llvm-components: x86
8+
9+
// Tests that we correctly copy arguments into allocas when the alignment of the byval argument
10+
// is different from the alignment of the Rust type.
11+
12+
// For the following test cases:
13+
// All of the `*_decreases_alignment` functions should codegen to a direct call, since the
14+
// alignment is already sufficient.
15+
// All off the `*_increases_alignment` functions should copy the argument to an alloca
16+
// on i686-unknown-linux-gnu, since the alignment needs to be increased, and should codegen
17+
// to a direct call on x86_64-unknown-linux-gnu, where byval alignment matches Rust alignment.
18+
19+
#![feature(no_core, lang_items)]
20+
#![crate_type = "lib"]
21+
#![no_std]
22+
#![no_core]
23+
#![allow(non_camel_case_types)]
24+
25+
#[lang = "sized"]
26+
trait Sized {}
27+
#[lang = "freeze"]
28+
trait Freeze {}
29+
#[lang = "copy"]
30+
trait Copy {}
31+
32+
// This type has align 1 in Rust, but as a byval argument on i686-linux, it will have align 4.
33+
#[repr(C)]
34+
#[repr(packed)]
35+
struct Align1 {
36+
x: u128,
37+
y: u128,
38+
z: u128,
39+
}
40+
41+
// This type has align 16 in Rust, but as a byval argument on i686-linux, it will have align 4.
42+
#[repr(C)]
43+
#[repr(align(16))]
44+
struct Align16 {
45+
x: u128,
46+
y: u128,
47+
z: u128,
48+
}
49+
50+
extern "C" {
51+
fn extern_c_align1(x: Align1);
52+
fn extern_c_align16(x: Align16);
53+
}
54+
55+
// CHECK-LABEL: @rust_to_c_increases_alignment
56+
#[no_mangle]
57+
pub unsafe fn rust_to_c_increases_alignment(x: Align1) {
58+
// i686-linux: start:
59+
// i686-linux-NEXT: [[ALLOCA:%[0-9a-z]+]] = alloca %Align1, align 4
60+
// i686-linux-NEXT: call void @llvm.memcpy.{{.+}}(ptr {{.*}}align 4 {{.*}}[[ALLOCA]], ptr {{.*}}align 1 {{.*}}%x
61+
// i686-linux-NEXT: call void @extern_c_align1({{.+}} [[ALLOCA]])
62+
63+
// x86_64-linux: start:
64+
// x86_64-linux-NEXT: call void @extern_c_align1
65+
extern_c_align1(x);
66+
}
67+
68+
// CHECK-LABEL: @rust_to_c_decreases_alignment
69+
#[no_mangle]
70+
pub unsafe fn rust_to_c_decreases_alignment(x: Align16) {
71+
// CHECK: start:
72+
// CHECK-NEXT: call void @extern_c_align16
73+
extern_c_align16(x);
74+
}
75+
76+
extern "Rust" {
77+
fn extern_rust_align1(x: Align1);
78+
fn extern_rust_align16(x: Align16);
79+
}
80+
81+
// CHECK-LABEL: @c_to_rust_decreases_alignment
82+
#[no_mangle]
83+
pub unsafe extern "C" fn c_to_rust_decreases_alignment(x: Align1) {
84+
// CHECK: start:
85+
// CHECK-NEXT: call void @extern_rust_align1
86+
extern_rust_align1(x);
87+
}
88+
89+
// CHECK-LABEL: @c_to_rust_increases_alignment
90+
#[no_mangle]
91+
pub unsafe extern "C" fn c_to_rust_increases_alignment(x: Align16) {
92+
// i686-linux: start:
93+
// i686-linux-NEXT: [[ALLOCA:%[0-9a-z]+]] = alloca %Align16, align 16
94+
// i686-linux-NEXT: call void @llvm.memcpy.{{.+}}(ptr {{.*}}align 16 {{.*}}[[ALLOCA]], ptr {{.*}}align 4 {{.*}}%0
95+
// i686-linux-NEXT: call void @extern_rust_align16({{.+}} [[ALLOCA]])
96+
97+
// x86_64-linux: start:
98+
// x86_64-linux-NEXT: call void @extern_rust_align16
99+
extern_rust_align16(x);
100+
}
101+
102+
extern "Rust" {
103+
fn extern_rust_ref_align1(x: &Align1);
104+
fn extern_rust_ref_align16(x: &Align16);
105+
}
106+
107+
// CHECK-LABEL: @c_to_rust_ref_decreases_alignment
108+
#[no_mangle]
109+
pub unsafe extern "C" fn c_to_rust_ref_decreases_alignment(x: Align1) {
110+
// CHECK: start:
111+
// CHECK-NEXT: call void @extern_rust_ref_align1
112+
extern_rust_ref_align1(&x);
113+
}
114+
115+
// CHECK-LABEL: @c_to_rust_ref_increases_alignment
116+
#[no_mangle]
117+
pub unsafe extern "C" fn c_to_rust_ref_increases_alignment(x: Align16) {
118+
// i686-linux: start:
119+
// i686-linux-NEXT: [[ALLOCA:%[0-9a-z]+]] = alloca %Align16, align 16
120+
// i686-linux-NEXT: call void @llvm.memcpy.{{.+}}(ptr {{.*}}align 16 {{.*}}[[ALLOCA]], ptr {{.*}}align 4 {{.*}}%0
121+
// i686-linux-NEXT: call void @extern_rust_ref_align16({{.+}} [[ALLOCA]])
122+
123+
// x86_64-linux: start:
124+
// x86_64-linux-NEXT: call void @extern_rust_ref_align16
125+
extern_rust_ref_align16(&x);
126+
}

0 commit comments

Comments
 (0)