Skip to content

Commit 0e76446

Browse files
ensure byval allocas are sufficiently aligned
1 parent 209ed07 commit 0e76446

File tree

3 files changed

+157
-39
lines changed

3 files changed

+157
-39
lines changed

compiler/rustc_codegen_ssa/src/mir/block.rs

Lines changed: 46 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ use rustc_target::abi::call::{ArgAbi, FnAbi, PassMode, Reg};
2323
use rustc_target::abi::{self, HasDataLayout, WrappingRange};
2424
use rustc_target::spec::abi::Abi;
2525

26+
use std::cmp;
27+
2628
// Indicates if we are in the middle of merging a BB's successor into it. This
2729
// can happen when BB jumps directly to its successor and the successor has no
2830
// other predecessors.
@@ -1360,36 +1362,58 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
13601362
// Force by-ref if we have to load through a cast pointer.
13611363
let (mut llval, align, by_ref) = match op.val {
13621364
Immediate(_) | Pair(..) => match arg.mode {
1363-
PassMode::Indirect { .. } | PassMode::Cast(..) => {
1365+
PassMode::Indirect { attrs, .. } => {
1366+
// Indirect argument may have higher alignment requirements than the type's alignment.
1367+
// This can happen, e.g. when passing types with <4 byte alignment on the stack on x86.
1368+
let required_align = match attrs.pointee_align {
1369+
Some(pointee_align) => cmp::max(pointee_align, arg.layout.align.abi),
1370+
None => arg.layout.align.abi,
1371+
};
1372+
let scratch = PlaceRef::alloca_aligned(bx, arg.layout, required_align);
1373+
op.val.store(bx, scratch);
1374+
(scratch.llval, scratch.align, true)
1375+
}
1376+
PassMode::Cast(..) => {
13641377
let scratch = PlaceRef::alloca(bx, arg.layout);
13651378
op.val.store(bx, scratch);
13661379
(scratch.llval, scratch.align, true)
13671380
}
13681381
_ => (op.immediate_or_packed_pair(bx), arg.layout.align.abi, false),
13691382
},
1370-
Ref(llval, _, align) => {
1371-
if arg.is_indirect() && align < arg.layout.align.abi {
1372-
// `foo(packed.large_field)`. We can't pass the (unaligned) field directly. I
1373-
// think that ATM (Rust 1.16) we only pass temporaries, but we shouldn't
1374-
// have scary latent bugs around.
1375-
1376-
let scratch = PlaceRef::alloca(bx, arg.layout);
1377-
base::memcpy_ty(
1378-
bx,
1379-
scratch.llval,
1380-
scratch.align,
1381-
llval,
1382-
align,
1383-
op.layout,
1384-
MemFlags::empty(),
1385-
);
1386-
(scratch.llval, scratch.align, true)
1387-
} else {
1388-
(llval, align, true)
1383+
Ref(llval, _, align) => match arg.mode {
1384+
PassMode::Indirect { attrs, .. } => {
1385+
let required_align = match attrs.pointee_align {
1386+
Some(pointee_align) => cmp::max(pointee_align, arg.layout.align.abi),
1387+
None => arg.layout.align.abi,
1388+
};
1389+
if align < required_align {
1390+
// For `foo(packed.large_field)`, and types with <4 byte alignment on x86,
1391+
// alignment requirements may be higher than the type's alignment, so copy
1392+
// to a higher-aligned alloca.
1393+
let scratch = PlaceRef::alloca_aligned(bx, arg.layout, required_align);
1394+
base::memcpy_ty(
1395+
bx,
1396+
scratch.llval,
1397+
scratch.align,
1398+
llval,
1399+
align,
1400+
op.layout,
1401+
MemFlags::empty(),
1402+
);
1403+
(scratch.llval, scratch.align, true)
1404+
} else {
1405+
(llval, align, true)
1406+
}
13891407
}
1390-
}
1408+
_ => (llval, align, true),
1409+
},
13911410
ZeroSized => match arg.mode {
1392-
PassMode::Indirect { .. } => {
1411+
PassMode::Indirect { on_stack, .. } => {
1412+
if on_stack {
1413+
// It doesn't seem like any target can have `byval` ZSTs, so this assert
1414+
// is here to replace a would-be untested codepath.
1415+
bug!("ZST {op:?} passed on stack with abi {arg:?}");
1416+
}
13931417
// Though `extern "Rust"` doesn't pass ZSTs, some ABIs pass
13941418
// a pointer for `repr(C)` structs even when empty, so get
13951419
// one from an `alloca` (which can be left uninitialized).

compiler/rustc_codegen_ssa/src/mir/place.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,18 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
4747
pub fn alloca<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
4848
bx: &mut Bx,
4949
layout: TyAndLayout<'tcx>,
50+
) -> Self {
51+
Self::alloca_aligned(bx, layout, layout.align.abi)
52+
}
53+
54+
pub fn alloca_aligned<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
55+
bx: &mut Bx,
56+
layout: TyAndLayout<'tcx>,
57+
align: Align,
5058
) -> Self {
5159
assert!(layout.is_sized(), "tried to statically allocate unsized place");
52-
let tmp = bx.alloca(bx.cx().backend_type(layout), layout.align.abi);
53-
Self::new_sized(tmp, layout)
60+
let tmp = bx.alloca(bx.cx().backend_type(layout), align);
61+
Self::new_sized_aligned(tmp, layout, align)
5462
}
5563

5664
/// Returns a place for an indirect reference to an unsized place.

tests/codegen/align-byval.rs

Lines changed: 101 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// ignore-tidy-linelength
12
// revisions:m68k wasm x86_64-linux x86_64-windows i686-linux i686-windows
23

34
//[m68k] compile-flags: --target m68k-unknown-linux-gnu
@@ -29,6 +30,16 @@
2930
impl Copy for i32 {}
3031
impl Copy for i64 {}
3132

33+
// This struct can be represented as a pair, so it exercises the OperandValue::Pair
34+
// codepath in `codegen_argument`.
35+
#[repr(C)]
36+
pub struct NaturalAlign1 {
37+
a: i8,
38+
b: i8,
39+
}
40+
41+
// This struct cannot be represented as an immediate, so it exercises the OperandValue::Ref
42+
// codepath in `codegen_argument`.
3243
#[repr(C)]
3344
pub struct NaturalAlign2 {
3445
a: [i16; 16],
@@ -67,7 +78,93 @@ pub struct ForceAlign16 {
6778
b: i8
6879
}
6980

81+
// CHECK-LABEL: @call_na1
82+
#[no_mangle]
83+
pub unsafe fn call_na1(x: NaturalAlign1) {
84+
// CHECK: start:
85+
86+
// m68k: [[ALLOCA:%[a-z0-9+]]] = alloca { i8, i8 }, align 1
87+
// m68k: call void @natural_align_1({{.*}}byval({ i8, i8 }) align 1{{.*}} [[ALLOCA]])
88+
89+
// wasm: [[ALLOCA:%[a-z0-9+]]] = alloca { i8, i8 }, align 1
90+
// wasm: call void @natural_align_1({{.*}}byval({ i8, i8 }) align 1{{.*}} [[ALLOCA]])
91+
92+
// x86_64-linux: call void @natural_align_1(i16
93+
94+
// x86_64-windows: call void @natural_align_1(i16
95+
96+
// i686-linux: [[ALLOCA:%[a-z0-9+]]] = alloca { i8, i8 }, align 4
97+
// i686-linux: call void @natural_align_1({{.*}}byval({ i8, i8 }) align 4{{.*}} [[ALLOCA]])
98+
99+
// i686-windows: [[ALLOCA:%[a-z0-9+]]] = alloca { i8, i8 }, align 4
100+
// i686-windows: call void @natural_align_1({{.*}}byval({ i8, i8 }) align 4{{.*}} [[ALLOCA]])
101+
natural_align_1(x);
102+
}
103+
104+
// CHECK-LABEL: @call_na2
105+
#[no_mangle]
106+
pub unsafe fn call_na2(x: NaturalAlign2) {
107+
// CHECK: start:
108+
109+
// m68k-NEXT: call void @natural_align_2
110+
// wasm-NEXT: call void @natural_align_2
111+
// x86_64-linux-NEXT: call void @natural_align_2
112+
// x86_64-windows-NEXT: call void @natural_align_2
113+
114+
// i686-linux: [[ALLOCA:%[0-9]+]] = alloca %NaturalAlign2, align 4
115+
// i686-linux: call void @natural_align_2({{.*}}byval(%NaturalAlign2) align 4{{.*}} [[ALLOCA]])
116+
117+
// i686-windows: [[ALLOCA:%[0-9]+]] = alloca %NaturalAlign2, align 4
118+
// i686-windows: call void @natural_align_2({{.*}}byval(%NaturalAlign2) align 4{{.*}} [[ALLOCA]])
119+
natural_align_2(x);
120+
}
121+
122+
// CHECK-LABEL: @call_fa4
123+
#[no_mangle]
124+
pub unsafe fn call_fa4(x: ForceAlign4) {
125+
// CHECK: start:
126+
// CHECK-NEXT: call void @force_align_4
127+
force_align_4(x);
128+
}
129+
130+
// CHECK-LABEL: @call_na8
131+
#[no_mangle]
132+
pub unsafe fn call_na8(x: NaturalAlign8) {
133+
// CHECK: start:
134+
// CHECK-NEXT: call void @natural_align_8
135+
natural_align_8(x);
136+
}
137+
138+
// CHECK-LABEL: @call_fa8
139+
#[no_mangle]
140+
pub unsafe fn call_fa8(x: ForceAlign8) {
141+
// CHECK: start:
142+
// CHECK-NEXT: call void @force_align_8
143+
force_align_8(x);
144+
}
145+
146+
// CHECK-LABEL: @call_fa16
147+
#[no_mangle]
148+
pub unsafe fn call_fa16(x: ForceAlign16) {
149+
// CHECK: start:
150+
// CHECK-NEXT: call void @force_align_16
151+
force_align_16(x);
152+
}
153+
70154
extern "C" {
155+
// m68k: declare void @natural_align_1({{.*}}byval({ i8, i8 }) align 1{{.*}})
156+
157+
// wasm: declare void @natural_align_1({{.*}}byval({ i8, i8 }) align 1{{.*}})
158+
159+
// x86_64-linux: declare void @natural_align_1(i16)
160+
161+
// x86_64-windows: declare void @natural_align_1(i16)
162+
163+
// i686-linux: declare void @natural_align_1({{.*}}byval({ i8, i8 }) align 4{{.*}})
164+
165+
// i686-windows: declare void @natural_align_1({{.*}}byval({ i8, i8 }) align 4{{.*}})
166+
fn natural_align_1(x: NaturalAlign1);
167+
71168
// m68k: declare void @natural_align_2({{.*}}byval(%NaturalAlign2) align 2{{.*}})
72169

73170
// wasm: declare void @natural_align_2({{.*}}byval(%NaturalAlign2) align 2{{.*}})
@@ -81,7 +178,7 @@ extern "C" {
81178
// i686-linux: declare void @natural_align_2({{.*}}byval(%NaturalAlign2) align 4{{.*}})
82179

83180
// i686-windows: declare void @natural_align_2({{.*}}byval(%NaturalAlign2) align 4{{.*}})
84-
fn natural_align_2(a: NaturalAlign2);
181+
fn natural_align_2(x: NaturalAlign2);
85182

86183
// m68k: declare void @force_align_4({{.*}}byval(%ForceAlign4) align 4{{.*}})
87184

@@ -96,7 +193,7 @@ extern "C" {
96193
// i686-linux: declare void @force_align_4({{.*}}byval(%ForceAlign4) align 4{{.*}})
97194

98195
// i686-windows: declare void @force_align_4({{.*}}byval(%ForceAlign4) align 4{{.*}})
99-
fn force_align_4(b: ForceAlign4);
196+
fn force_align_4(x: ForceAlign4);
100197

101198
// m68k: declare void @natural_align_8({{.*}}byval(%NaturalAlign8) align 4{{.*}})
102199

@@ -128,7 +225,7 @@ extern "C" {
128225
// i686-windows: declare void @force_align_8(
129226
// i686-windows-NOT: byval
130227
// i686-windows-SAME: align 8{{.*}})
131-
fn force_align_8(y: ForceAlign8);
228+
fn force_align_8(x: ForceAlign8);
132229

133230
// m68k: declare void @force_align_16({{.*}}byval(%ForceAlign16) align 16{{.*}})
134231

@@ -145,16 +242,5 @@ extern "C" {
145242
// i686-windows: declare void @force_align_16(
146243
// i686-windows-NOT: byval
147244
// i686-windows-SAME: align 16{{.*}})
148-
fn force_align_16(z: ForceAlign16);
149-
}
150-
151-
pub unsafe fn main(
152-
a: NaturalAlign2, b: ForceAlign4,
153-
x: NaturalAlign8, y: ForceAlign8, z: ForceAlign16
154-
) {
155-
natural_align_2(a);
156-
force_align_4(b);
157-
natural_align_8(x);
158-
force_align_8(y);
159-
force_align_16(z);
245+
fn force_align_16(x: ForceAlign16);
160246
}

0 commit comments

Comments
 (0)