-
Notifications
You must be signed in to change notification settings - Fork 13.8k
rustc_target: Add alignment to indirectly-passed by-value types, correcting the alignment of byval
on x86 in the process.
#103830
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -499,9 +499,7 @@ impl<'a, Ty> ArgAbi<'a, Ty> { | |
.set(ArgAttribute::NonNull) | ||
.set(ArgAttribute::NoUndef); | ||
attrs.pointee_size = layout.size; | ||
// FIXME(eddyb) We should be doing this, but at least on | ||
// i686-pc-windows-msvc, it results in wrong stack offsets. | ||
// attrs.pointee_align = Some(layout.align.abi); | ||
attrs.pointee_align = Some(layout.align.abi); | ||
|
||
let extra_attrs = layout.is_unsized().then_some(ArgAttributes::new()); | ||
|
||
|
@@ -518,11 +516,19 @@ impl<'a, Ty> ArgAbi<'a, Ty> { | |
self.mode = Self::indirect_pass_mode(&self.layout); | ||
} | ||
|
||
pub fn make_indirect_byval(&mut self) { | ||
pub fn make_indirect_byval(&mut self, byval_align: Option<Align>) { | ||
self.make_indirect(); | ||
match self.mode { | ||
PassMode::Indirect { attrs: _, extra_attrs: _, ref mut on_stack } => { | ||
PassMode::Indirect { ref mut attrs, extra_attrs: _, ref mut on_stack } => { | ||
*on_stack = true; | ||
|
||
// Some platforms, like 32-bit x86, change the alignment of the type when passing | ||
// `byval`. Account for that. | ||
if let Some(byval_align) = byval_align { | ||
// On all targets with byval align this is currently true, so let's assert it. | ||
debug_assert!(byval_align >= Align::from_bytes(4).unwrap()); | ||
attrs.pointee_align = Some(byval_align); | ||
} | ||
} | ||
_ => unreachable!(), | ||
} | ||
|
@@ -659,7 +665,8 @@ impl<'a, Ty> FnAbi<'a, Ty> { | |
{ | ||
if abi == spec::abi::Abi::X86Interrupt { | ||
if let Some(arg) = self.args.first_mut() { | ||
arg.make_indirect_byval(); | ||
// FIXME(pcwalton): This probably should use the x86 `byval` ABI... | ||
|
||
arg.make_indirect_byval(None); | ||
} | ||
return Ok(()); | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
// ignore-x86 | ||
// ignore-aarch64 | ||
// ignore-aarch64_be | ||
// ignore-arm | ||
// ignore-armeb | ||
// ignore-avr | ||
// ignore-bpfel | ||
// ignore-bpfeb | ||
// ignore-hexagon | ||
// ignore-mips | ||
// ignore-mips64 | ||
// ignore-msp430 | ||
// ignore-powerpc64 | ||
// ignore-powerpc64le | ||
// ignore-powerpc | ||
// ignore-r600 | ||
// ignore-amdgcn | ||
// ignore-sparc | ||
// ignore-sparcv9 | ||
// ignore-sparcel | ||
// ignore-s390x | ||
// ignore-tce | ||
// ignore-thumb | ||
// ignore-thumbeb | ||
// ignore-xcore | ||
// ignore-nvptx | ||
// ignore-nvptx64 | ||
// ignore-le32 | ||
// ignore-le64 | ||
// ignore-amdil | ||
// ignore-amdil64 | ||
// ignore-hsail | ||
// ignore-hsail64 | ||
// ignore-spir | ||
// ignore-spir64 | ||
// ignore-kalimba | ||
// ignore-shave | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This looks really fragile. I'd suggest to implement this in the same way as https://github.com/rust-lang/rust/blob/master/tests/codegen/abi-repr-ext.rs, i.e. a no_core test explicitly specifying targets. That should also allow you to test a non-windows target for the issue below. |
||
// | ||
// Tests that `byval` alignment is properly specified (#80127). | ||
// The only targets that use `byval` are m68k, wasm, x86-64, and x86. Note that | ||
// x86 has special rules (see #103830), and it's therefore ignored here. | ||
|
||
#[repr(C)] | ||
#[repr(align(16))] | ||
struct Foo { | ||
a: [i32; 16], | ||
} | ||
|
||
extern "C" { | ||
// CHECK: declare void @f({{.*}}byval(%Foo) align 16{{.*}}) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The Windows x64 ABI mandates passing ; Function Attrs: uwtable
declare void @f(ptr noalias nocapture noundef align 16 dereferenceable(64)) unnamed_addr #1 Can we conditionally check for |
||
fn f(foo: Foo); | ||
|
||
} | ||
|
||
pub fn main() { | ||
unsafe { f(Foo { a: [1; 16] }) } | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
include ../tools.mk | ||
|
||
all: $(call NATIVE_STATICLIB,test) | ||
$(RUSTC) test.rs | ||
$(call RUN,test) || exit 1 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
#include <assert.h> | ||
#include <stdbool.h> | ||
#include <stdint.h> | ||
#include <string.h> | ||
|
||
struct TwoU64s | ||
{ | ||
uint64_t a; | ||
uint64_t b; | ||
} __attribute__((aligned(16))); | ||
|
||
struct BoolAndU32 | ||
{ | ||
bool a; | ||
uint32_t b; | ||
}; | ||
|
||
int32_t many_args( | ||
void *a, | ||
void *b, | ||
const char *c, | ||
uint64_t d, | ||
bool e, | ||
struct BoolAndU32 f, | ||
void *g, | ||
struct TwoU64s h, | ||
void *i, | ||
void *j, | ||
void *k, | ||
void *l, | ||
const char *m) | ||
{ | ||
assert(strcmp(m, "Hello world") == 0); | ||
return 0; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
// Issue #80127: Passing structs via FFI should work with explicit alignment. | ||
|
||
use std::ffi::CString; | ||
use std::ptr::null_mut; | ||
|
||
#[derive(Clone, Copy, Debug, PartialEq)] | ||
#[repr(C)] | ||
#[repr(align(16))] | ||
pub struct TwoU64s { | ||
pub a: u64, | ||
pub b: u64, | ||
} | ||
|
||
#[repr(C)] | ||
#[derive(Debug, Copy, Clone)] | ||
pub struct BoolAndU32 { | ||
pub a: bool, | ||
pub b: u32, | ||
} | ||
|
||
#[link(name = "test", kind = "static")] | ||
extern "C" { | ||
fn many_args( | ||
a: *mut (), | ||
b: *mut (), | ||
c: *const i8, | ||
d: u64, | ||
e: bool, | ||
f: BoolAndU32, | ||
g: *mut (), | ||
h: TwoU64s, | ||
i: *mut (), | ||
j: *mut (), | ||
k: *mut (), | ||
l: *mut (), | ||
m: *const i8, | ||
) -> i32; | ||
} | ||
|
||
fn main() { | ||
let two_u64s = TwoU64s { a: 1, b: 2 }; | ||
let bool_and_u32 = BoolAndU32 { a: true, b: 3 }; | ||
let string = CString::new("Hello world").unwrap(); | ||
unsafe { | ||
many_args( | ||
null_mut(), | ||
null_mut(), | ||
null_mut(), | ||
4, | ||
true, | ||
bool_and_u32, | ||
null_mut(), | ||
two_u64s, | ||
null_mut(), | ||
null_mut(), | ||
null_mut(), | ||
null_mut(), | ||
string.as_ptr(), | ||
); | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.