From d06c2d46891bdd867fbc491a2c0f42fb5aec0a79 Mon Sep 17 00:00:00 2001 From: winstonallo Date: Wed, 13 Aug 2025 17:52:51 +0200 Subject: [PATCH 1/7] Add test file for reg-struct-return --- tests/assembly-llvm/reg-struct-return.rs | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/assembly-llvm/reg-struct-return.rs diff --git a/tests/assembly-llvm/reg-struct-return.rs b/tests/assembly-llvm/reg-struct-return.rs new file mode 100644 index 0000000000000..e69de29bb2d1d From 24a767dc6daa4d9914c18b7b3aa48a4b16aa0020 Mon Sep 17 00:00:00 2001 From: winstonallo Date: Thu, 14 Aug 2025 08:08:24 +0200 Subject: [PATCH 2/7] Add assembly tests for `-Zreg-struct-return` --- tests/assembly-llvm/reg-struct-return.rs | 61 ++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/tests/assembly-llvm/reg-struct-return.rs b/tests/assembly-llvm/reg-struct-return.rs index e69de29bb2d1d..6a66b8b6cc762 100644 --- a/tests/assembly-llvm/reg-struct-return.rs +++ b/tests/assembly-llvm/reg-struct-return.rs @@ -0,0 +1,61 @@ +// Test the reg-struct-return ABI. +//@ add-core-stubs +//@ assembly-output: emit-asm +//@ compile-flags: -O --target=i686-unknown-linux-gnu -Crelocation-model=static +//@ revisions: WITH WITHOUT +//@[WITH] compile-flags: -Zreg-struct-return +//@ needs-llvm-components: x86 + +#![feature(no_core)] +#![no_std] +#![no_core] +#![crate_type = "lib"] + +extern crate minicore; +use minicore::*; + +struct div_t { + quot: i32, + rem: i32, +} + +unsafe extern "C" { + fn div(numerator: i32, denominator: i32) -> div_t; +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn direct_construction(numerator: i32, denominator: i32) -> div_t { + // WITH-LABEL: direct_construction + // WITH: movl $42, %eax + // WITH: movl $42, %edx + // WITH: retl + + // WITHOUT-LABEL: direct_construction + // WITHOUT: movl 4(%esp), %eax + // WITHOUT: movl $42, (%eax) + // WITHOUT: movl $42, 4(%eax) + // WITHOUT: retl $4 + div_t { quot: 42, rem: 42 } +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn builtin_call(numerator: i32, denominator: i32) -> div_t { + // WITH-LABEL: builtin_call + // WITH: jmp div + + // WITHOUT-LABEL: builtin_call + // WITHOUT: pushl %esi + // WITHOUT: subl $8, %esp + // WITHOUT: movl 16(%esp), %esi + // WITHOUT: subl $4, %esp + // WITHOUT: pushl 28(%esp) + // WITHOUT: pushl 28(%esp) + // WITHOUT: pushl %esi + // WITHOUT: calll div + // WITHOUT: addl $12, %esp + // WITHOUT: movl %esi, %eax + // WITHOUT: addl $8, %esp + // WITHOUT: popl %esi + // WITHOUT: retl $4 + div(numerator, denominator) +} From fc67ace0018fa62337b50baf446831d8ad48c4e4 Mon Sep 17 00:00:00 2001 From: winstonallo Date: Thu, 14 Aug 2025 08:12:40 +0200 Subject: [PATCH 3/7] Improve test description in comment --- tests/assembly-llvm/reg-struct-return.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/assembly-llvm/reg-struct-return.rs b/tests/assembly-llvm/reg-struct-return.rs index 6a66b8b6cc762..3ae3f963b11c0 100644 --- a/tests/assembly-llvm/reg-struct-return.rs +++ b/tests/assembly-llvm/reg-struct-return.rs @@ -1,4 +1,5 @@ -// Test the reg-struct-return ABI. +// Tests that -Zreg-struct-return changes ABI for small struct returns +// from hidden-pointer convention to register-return convention on x86. //@ add-core-stubs //@ assembly-output: emit-asm //@ compile-flags: -O --target=i686-unknown-linux-gnu -Crelocation-model=static From 427a183456863fae22643afda6a575673e680a17 Mon Sep 17 00:00:00 2001 From: winstonallo Date: Thu, 14 Aug 2025 16:43:52 +0200 Subject: [PATCH 4/7] Add better test description --- tests/assembly-llvm/reg-struct-return.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/assembly-llvm/reg-struct-return.rs b/tests/assembly-llvm/reg-struct-return.rs index 3ae3f963b11c0..24a53f7a543a7 100644 --- a/tests/assembly-llvm/reg-struct-return.rs +++ b/tests/assembly-llvm/reg-struct-return.rs @@ -1,5 +1,8 @@ -// Tests that -Zreg-struct-return changes ABI for small struct returns -// from hidden-pointer convention to register-return convention on x86. +//! Tests that -Zreg-struct-return changes ABI for small struct returns +//! from hidden-pointer convention to register-return on x86_32. +//! This test covers: +//! * Direct struct construction, verifying register return versus hidden pointer +//! * External function calls returning structs, verifying ABI mismatch handling //@ add-core-stubs //@ assembly-output: emit-asm //@ compile-flags: -O --target=i686-unknown-linux-gnu -Crelocation-model=static From f4a931744f2967abe92e0f012f4087281949cb36 Mon Sep 17 00:00:00 2001 From: winstonallo Date: Thu, 14 Aug 2025 17:22:32 +0200 Subject: [PATCH 5/7] Add tests for < 8 and > 8 bytes structs Organize tests into separate module for better legibility --- tests/assembly-llvm/reg-struct-return.rs | 165 +++++++++++++++++------ 1 file changed, 126 insertions(+), 39 deletions(-) diff --git a/tests/assembly-llvm/reg-struct-return.rs b/tests/assembly-llvm/reg-struct-return.rs index 24a53f7a543a7..b4625cc11ab6c 100644 --- a/tests/assembly-llvm/reg-struct-return.rs +++ b/tests/assembly-llvm/reg-struct-return.rs @@ -18,48 +18,135 @@ extern crate minicore; use minicore::*; -struct div_t { - quot: i32, - rem: i32, -} +// Verifies ABI changes for small struct, where both fields fit into one register. +// WITH is expected to use register return, WITHOUT should use hidden pointer. +mod small { + struct small_t { + a: i8, + b: i8, + } + + unsafe extern "C" { + fn small() -> small_t; + } + + #[unsafe(no_mangle)] + pub unsafe extern "C" fn small_direct_construction() -> small_t { + // (42 << 8) | 42 = 10794 + + // WITH-LABEL: small_direct_construction + // WITH: movw $10794, %ax + // WITH: retl -unsafe extern "C" { - fn div(numerator: i32, denominator: i32) -> div_t; + // WITHOUT-LABEL: small_direct_construction + // WITHOUT: movl 4(%esp), %eax + // WITHOUT: movw $10794, (%eax) + // WITHOUT: retl $4 + small_t { a: 42, b: 42 } + } + + #[unsafe(no_mangle)] + pub unsafe extern "C" fn small_call() -> small_t { + // WITH-LABEL: small_call + // WITH: jmp small + + // WITHOUT-LABEL: small_call + // WITHOUT: calll small + // WITHOUT: retl $4 + small() + } } -#[unsafe(no_mangle)] -pub unsafe extern "C" fn direct_construction(numerator: i32, denominator: i32) -> div_t { - // WITH-LABEL: direct_construction - // WITH: movl $42, %eax - // WITH: movl $42, %edx - // WITH: retl - - // WITHOUT-LABEL: direct_construction - // WITHOUT: movl 4(%esp), %eax - // WITHOUT: movl $42, (%eax) - // WITHOUT: movl $42, 4(%eax) - // WITHOUT: retl $4 - div_t { quot: 42, rem: 42 } +// Verifies ABI changes for a struct of size 8, which is the maximum size +// for reg-struct-return. +// WITH is expected to still use register return, WITHOUT should use hidden +// pointer. +mod pivot { + struct pivot_t { + a: i32, + b: i32, + } + + unsafe extern "C" { + fn pivot() -> pivot_t; + } + + #[unsafe(no_mangle)] + pub unsafe extern "C" fn pivot_direct_construction() -> pivot_t { + // WITH-LABEL: pivot_direct_construction + // WITH: movl $42, %eax + // WITH: movl $42, %edx + // WITH: retl + + // WITHOUT-LABEL: pivot_direct_construction + // WITHOUT: movl 4(%esp), %e{{.*}} + // WITHOUT: movl $42, (%e{{.*}}) + // WITHOUT: movl $42, 4(%e{{.*}}) + // WITHOUT: retl $4 + pivot_t { a: 42, b: 42 } + } + + #[unsafe(no_mangle)] + pub unsafe extern "C" fn pivot_call() -> pivot_t { + // WITH-LABEL: pivot_call + // WITH: jmp pivot + + // WITHOUT-LABEL: pivot_call + // WITHOUT: pushl %esi + // WITHOUT: subl $8, %esp + // WITHOUT: movl 16(%esp), %esi + // WITHOUT: %esi, (%esp) + // WITHOUT: calll pivot + // WITHOUT: subl $4, %esp + // WITHOUT: movl %esi, %eax + // WITHOUT: addl $8, %esp + // WITHOUT: popl %esi + // WITHOUT: retl $4 + pivot() + } } -#[unsafe(no_mangle)] -pub unsafe extern "C" fn builtin_call(numerator: i32, denominator: i32) -> div_t { - // WITH-LABEL: builtin_call - // WITH: jmp div - - // WITHOUT-LABEL: builtin_call - // WITHOUT: pushl %esi - // WITHOUT: subl $8, %esp - // WITHOUT: movl 16(%esp), %esi - // WITHOUT: subl $4, %esp - // WITHOUT: pushl 28(%esp) - // WITHOUT: pushl 28(%esp) - // WITHOUT: pushl %esi - // WITHOUT: calll div - // WITHOUT: addl $12, %esp - // WITHOUT: movl %esi, %eax - // WITHOUT: addl $8, %esp - // WITHOUT: popl %esi - // WITHOUT: retl $4 - div(numerator, denominator) +// Verifies ABI changes for a struct of size 12, which is larger than the +// maximum size for reg-struct-return (8 bytes). +// Here, both WITH and WITHOUT should use the hidden pointer convention. +mod large { + struct large_t { + a: i32, + b: i32, + c: i32, + } + + unsafe extern "C" { + fn large() -> large_t; + } + + #[unsafe(no_mangle)] + pub unsafe extern "C" fn large_direct_construction() -> large_t { + // WITH-LABEL: large_direct_construction + // WITH: movl 4(%esp), %e{{.*}} + // WITH: movl $42, (%e{{.*}}) + // WITH: movl $42, 4(%e{{.*}}) + // WITH: movl $42, 8(%e{{.*}}) + // WITH: retl $4 + + // WITHOUT-LABEL: large_direct_construction + // WITHOUT: movl 4(%esp), %e{{.*}} + // WITHOUT: movl $42, (%e{{.*}}) + // WITHOUT: movl $42, 4(%e{{.*}}) + // WITHOUT: movl $42, 8(%e{{.*}}) + // WITHOUT: retl $4 + large_t { a: 42, b: 42, c: 42 } + } + + #[unsafe(no_mangle)] + pub unsafe extern "C" fn large_call() -> large_t { + // WITH-LABEL: large_call + // WITH: calll large + // WITH: retl $4 + + // WITHOUT-LABEL: large_call + // WITHOUT: calll large + // WITHOUT: retl $4 + large() + } } From 3518ac1da6558c5c02837b13e55c67943b6d494d Mon Sep 17 00:00:00 2001 From: winstonallo Date: Thu, 14 Aug 2025 18:58:02 +0200 Subject: [PATCH 6/7] Simplify WITHOUT test in pivot_call --- tests/assembly-llvm/reg-struct-return.rs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/tests/assembly-llvm/reg-struct-return.rs b/tests/assembly-llvm/reg-struct-return.rs index b4625cc11ab6c..9d8cee33862a2 100644 --- a/tests/assembly-llvm/reg-struct-return.rs +++ b/tests/assembly-llvm/reg-struct-return.rs @@ -92,15 +92,7 @@ mod pivot { // WITH: jmp pivot // WITHOUT-LABEL: pivot_call - // WITHOUT: pushl %esi - // WITHOUT: subl $8, %esp - // WITHOUT: movl 16(%esp), %esi - // WITHOUT: %esi, (%esp) // WITHOUT: calll pivot - // WITHOUT: subl $4, %esp - // WITHOUT: movl %esi, %eax - // WITHOUT: addl $8, %esp - // WITHOUT: popl %esi // WITHOUT: retl $4 pivot() } From fcf55d5c5a9a96dc014857090da17e7a439a78df Mon Sep 17 00:00:00 2001 From: winstonallo Date: Thu, 14 Aug 2025 18:58:33 +0200 Subject: [PATCH 7/7] Generalize target registers in tests --- tests/assembly-llvm/reg-struct-return.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/assembly-llvm/reg-struct-return.rs b/tests/assembly-llvm/reg-struct-return.rs index 9d8cee33862a2..26717f3d8a336 100644 --- a/tests/assembly-llvm/reg-struct-return.rs +++ b/tests/assembly-llvm/reg-struct-return.rs @@ -39,8 +39,8 @@ mod small { // WITH: retl // WITHOUT-LABEL: small_direct_construction - // WITHOUT: movl 4(%esp), %eax - // WITHOUT: movw $10794, (%eax) + // WITHOUT: movl 4(%esp), %e{{.*}} + // WITHOUT: movw $10794, (%e{{.*}}) // WITHOUT: retl $4 small_t { a: 42, b: 42 } } @@ -74,8 +74,8 @@ mod pivot { #[unsafe(no_mangle)] pub unsafe extern "C" fn pivot_direct_construction() -> pivot_t { // WITH-LABEL: pivot_direct_construction - // WITH: movl $42, %eax - // WITH: movl $42, %edx + // WITH: movl $42, %e{{.*}} + // WITH: movl $42, %e{{.*}} // WITH: retl // WITHOUT-LABEL: pivot_direct_construction