Rust语言x86_64架构下结构体返回值的ABI规则问询
Rust x86_64架构下结构体返回值的ABI规则解析
问题描述
我希望了解Rust语言在x86_64架构下处理结构体返回值的ABI规则。通过观察以下示例,我仍无法明确相关规则:
示例1
// 12Bytes pub struct test_struct { a: i32, b: i32, c: i32, } pub fn foo() -> test_struct { let data = test_struct { a: 0, b: 1, c: 2, }; data }
对应的LLVM IR:
define void @_ZN7example3foo17h4c785e31173cde01E(ptr sret(%test_struct)
可见返回值通过第一个sret参数传递。
示例2
// 12Bytes pub struct test_struct { a: i64, b: i32, } pub fn foo() -> test_struct { let data = test_struct { a: 0, b: 1, }; data }
对应的LLVM IR:
define { i64, i32 } @_ZN7example3foo17h339bca89e202abeeE()
返回值通过寄存器传递。
示例3
// 8Bytes pub struct test_struct { a: i32, b: i16, c: i16, } pub fn foo() -> test_struct { let data = test_struct { a: 0, b: 1, c: 2, }; data }
对应的LLVM IR:
define i64 @_ZN7example3foo17h4c785e31173cde01E()
返回值通过寄存器传递。
示例1与示例2返回值大小相同,但Rust处理方式不同,说明规则与返回值大小无关;示例1与示例3返回结构体的成员数量相同,但处理方式不同,说明规则与成员数量无关。这让我十分困惑,请问Rust语言中结构体返回值的ABI规则是什么?
规则解析
Rust在x86_64架构(非Windows平台)下遵循System V AMD64 ABI规范,结构体返回值的处理核心取决于类型是否能被适配为通用寄存器可传递的形式,而非单纯看大小或成员数量:
核心判断逻辑
寄存器返回的条件:
结构体/联合体如果能被拆分为最多2个64位通用寄存器(或兼容的向量寄存器,通用场景以通用寄存器为主)可容纳的单元,就会直接通过寄存器返回:- 示例2的
i64 + i32:刚好可以拆分到一个64位寄存器(存i64)和一个32位寄存器(存i32),符合“最多2个寄存器”的限制,因此用寄存器返回。 - 示例3的
i32 + i16 + i16:三个成员总大小8字节,可被打包进单个64位寄存器(内存布局紧凑对齐后,整体占8字节),因此直接用i64寄存器返回。
- 示例2的
sret参数传递的条件:
当结构体无法被适配到最多2个通用寄存器中时,就会使用sret(struct return)方式:调用方提前在栈上分配内存,将内存指针作为第一个参数传递给函数,函数把返回值写入该内存,自身返回void。- 示例1的三个
i32:总大小12字节,虽然小于2个寄存器的总容量(16字节),但无法拆分为2个64位寄存器的有效单元(三个独立的4字节成员无法刚好匹配寄存器宽度),因此只能通过sret方式传递。
- 示例1的三个
补充细节
- Rust默认结构体布局遵循ABI对齐规则(除非使用
#[repr(packed)]强制压缩),布局的对齐方式会直接影响是否能被寄存器打包传递。 - 对于超过16字节的结构体,几乎都会使用
sret方式;但即使小于等于16字节,也要看成员的类型组合是否能适配寄存器的宽度拆分。
内容的提问来源于stack exchange,提问作者super_jh
相关产品推荐
相关产品推荐

