blob: 78ee35f05ca580ecdc1b01fbc32cb0dec5eec705 [file]
// Compiler:
//
// Run-time:
// status: 0
// Checks that cg_gcc passes an over-aligned by-value ("byval") argument where the platform ABI
// says it goes, by calling in both directions with `tests/c/overaligned_byval_abi.c`, which is
// compiled by the real GCC.
//
// `tests/run/overaligned_byval_arg.rs` covers the Rust-visible half of the same bug. It cannot
// cover this one: with cg_gcc on both sides of a call, caller and callee place the argument at
// the same wrong offset and agree with each other.
//
// Two over-aligned arguments are used rather than one so that the failure is deterministic. A
// backend that drops `align(64)` packs the arguments at offsets 0, 24, 88 and 112 of the argument
// area; 112 - 24 = 88 is not a multiple of 64, so the two of them cannot both land on a 64-byte
// boundary however the argument area itself is aligned. With a single over-aligned argument the
// frame often happens to be 64-aligned and the bug hides.
//
// Only the values received are checked, never the address an argument landed at: which alignment
// a target gives a by-value stack slot differs between targets, but the two sides of a call
// agreeing on it does not. `overaligned_byval_arg.rs` is where the alignment itself is asserted.
#![feature(no_core)]
#![no_std]
#![no_core]
#![no_main]
extern crate mini_core;
use mini_core::*;
#[repr(C)]
struct Big {
a: i64,
b: i64,
c: i64,
}
#[repr(C, align(64))]
struct Aligned {
x: i32,
}
extern "C" {
fn c_take_both(first: Big, second: Aligned, third: Big, fourth: Aligned) -> i32;
fn c_call_rust() -> i32;
}
// The callee for the GCC-built caller in `c_call_rust`.
//
// `#[no_mangle]` is not only about the symbol name: it makes the symbol externally visible, which
// pins the calling convention. Without it the function has internal linkage and GCC is free to
// clone it with a changed convention at `-O3` (the symbol comes out as `...constprop.0.isra.0`),
// so the arguments never travel through the stack slots and the release build passes spuriously.
#[no_mangle]
extern "C" fn rust_take_both(first: Big, second: Aligned, third: Big, fourth: Aligned) -> i32 {
if first.a as i32 != 1 || first.b as i32 != 2 || first.c as i32 != 3 {
return 5;
}
if second.x != 42 {
return 6;
}
if third.a as i32 != 4 || third.b as i32 != 5 || third.c as i32 != 6 {
return 7;
}
if fourth.x != 43 {
return 8;
}
0
}
#[no_mangle]
extern "C" fn main(_argc: i32, _argv: *const *const u8) -> i32 {
// cg_gcc as the caller, GCC as the callee.
let result = unsafe {
c_take_both(
Big { a: 1, b: 2, c: 3 },
Aligned { x: 42 },
Big { a: 4, b: 5, c: 6 },
Aligned { x: 43 },
)
};
if result != 0 {
return result;
}
// GCC as the caller, cg_gcc as the callee.
unsafe { c_call_rust() }
}