| // 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, rustc_private)] | |
| #![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() } | |
| } |