想象你走进一家名为「x86_64 大酒楼」的餐厅。你点了一桌菜(调用一个函数),后厨(被调函数)热火朝天地忙活,终于把菜炒好了。问题是:这一盘盘菜是怎么递到你手上的?答案藏在餐厅的「出餐窗口」里。这家酒楼有个铁规矩:所有炒菜(整数返回值)从主窗口 RAX 递出;如果是双拼大份(128 位返回值),第二份从副窗口 RDX 递;甜点(浮点返回值)走甜点窗口 XMM0;要是点了全家桶(结构体返回值),后厨还会按大小决定「打包盒怎么递」或「要不要你自带保温箱」。这套出餐规矩写在餐饮行业协会发布的《标准操作手册》里,业内管它叫 ABI(应用二进制接口)。它保证了无论哪家饭馆(哪种语言编译出来的程序),只要按手册出餐,客人(另一个程序)就能稳稳接住。今天,我们就当一回后厨学徒,把这份出餐手册从头到尾翻一遍。
理解返回值寄存器约定,是读懂汇编、排查崩溃现场、编写跨语言绑定与高性能库的底层基本功。AI 从业者掌握它,能在模型算子优化、FFI 集成和编译器后端工作中游刃有余。
目录
可视化演示
展示整数返回值RAX/RDX、浮点返回值XMM0与结构体返回值寄存器/指针传递规则,交互理解x86_64 ABI返回值约定。
本节配有交互动效(进入视口后自动加载,约 3KB)。
'+modeData.integer.caption+'
整数返回约定:主窗口 RAX
你走进「x86_64 大酒楼」,点了一份红烧肉。后厨一阵叮当响后,炒好的肉被端到了主窗口 RAX 上——不管这道菜是糖醋里脊还是清蒸鲈鱼(int、指针、枚举),只要它是「一道菜」(64 位以内的整数),餐厅就约定从 RAX 这个固定窗口递出。客人(调用者)根本不用猜,直接去主窗口取就行。这个「约定俗成」就是寄存器返回约定的雏形。
要把这套规矩变成整个行业的共识,就需要白纸黑字的《出餐手册》——System V ABI。手册明确写着:所有 64 位以内的整数、指针、枚举值,一律存放在 RAX 寄存器返回。如果函数要返回的是 __int128 这种「两个菜一锅出」的大份单品,那么额外开一个副窗口 RDX:RAX 放低 64 位,RDX 放高 64 位。两个窗口拼一起,128 位的菜就完整递出去了。
编译器(后厨总管)在生成代码时,会在函数结尾放 mov rax, 返回值 或 mov rdx, 高64位 这类指令,把结果规规矩矩地送到窗口上。调用者则从同一个窗口接住。返回 char 或 short 这类小尺寸菜时,厨房还会「顺手扩个盘」——把高位按符号或零填充好,避免客人拿错尺寸。
所以,主窗口 RAX 承载了后厨绝大多数快递业务。你下次读汇编时,只要看到函数结尾往 RAX 里塞东西,就知道「菜」已经端到窗口上了。如果同时还动了 RDX,说明这是一份 128 位的「大份双拼」,别忘了两个窗口一起看。
- RAX 低 64 位函数返回前,将整值
v的低 64 位写入 RAX。这便是调用者取回结果的唯一主窗口。 - RDX 高 64 位当
v的位宽超过 64 位(如__int128)时,高 64 位写入 RDX 寄存器。 - 符号扩展返回
int8_t/int16_t时,编译器将值符号扩展为 32 位或 64 位再写入 RAX。
浮点返回通道:甜点窗口 XMM0
大酒楼除了炒菜窗口,还有专门的甜点窗口 XMM0。浮点数(float、double)就像蓝莓芝士蛋糕——它跟红烧肉在物理形态上完全不同,如果强行从炒菜窗口递,轻则糊一手奶油。于是行业手册规定:浮点返回值走 XMM0 专用窗口。这个隔离让「甜点」和「正餐」各走各的通道,互不污染。
XMM0 是 SSE/AVX 向量寄存器家族的一员,宽度 128 位及以上。浮点返回值按精度占用它的一部分:float 占低 32 位,double 占低 64 位。如果函数的返回值是 __m128 这样的向量类型,则直接使用完整的 XMM0,甚至扩展到 XMM0:XMM1 组合。
为什么不用 RAX?因为 x86 的通用寄存器(RAX 等)从设计上就不适合直接做浮点运算——早期 x87 浮点栈(ST(0))是单独的体系,SSE 普及后浮点又搬进了 XMM 家族。ABI 这样拆分「出餐窗口」,等于把甜点和正餐物理隔离。混合返回场景中,比如返回 struct { double d; long l; } 这种 16 字节结构体,分类规则会把它拆成 XMM0(d)和 RAX(l)两个窗口,各取所需。
甜点窗口的存在,让后厨不必为一道甜品停下炒锅——浮点结果与整数结果互不抢道。这也是 SIMD 时代留给 ABI 的清晰印记:每个返回值的「形态」都有自己专属的通道。
- XMM0 写入将
v的二进制位模式放入 XMM0 寄存器的低 64 位(double)或低 32 位(float)。 - 单精度 float只写入 XMM0 的低 32 位,高位内容不被 ABI 保证。
- 向量扩展返回
__m256等 256 位向量时使用 YMM0,512 位使用 ZMM0。
结构体返回路径:打包盒与双槽
如果客人点的是「小份全家桶」——一个不超过 16 字节的结构体,比如 struct Point { int x, y; }——后厨不会叫客人拿大箱子,而是直接拿两个标准打包盒(八字节槽位)装好,从 RAX 和 RDX 两个窗口同时递出。整个过程不需要经过一块额外内存。
System V ABI 对结构体返回值有一套「分类」算法:把结构体看作一串 8 字节槽位,再根据槽内字段的类型把每个槽归类为 INTEGER、SSE、MEMORY 等。若所有槽位都不是 MEMORY 类且总大小不超过 16 字节,就通过寄存器返回:第一个槽放 RAX,第二个槽放 RDX(需要时)。若结构体里混有 double 字段,对应槽位会被分类为 SSE,改走 XMM0/XMM1。
这个分类不是简单按大小切两半。比如 struct { char c; double d; } 大小 16 字节:第一槽 c + 7字节填充 是 INTEGER,第二槽 d 是 SSE,于是返回时两个槽分开走 RAX 和 XMM0。编译器会生成两条指令把两个槽塞进对应寄存器,调用者再拼接回结构体。这个细节在把 C 代码移植到汇编或写 FFI 绑定时极易踩坑。
「小份全家桶」用标准打包盒刚好装下,两个窗口一递,客人拎着就走,中间连一块内存都不需要碰——这就是小结构体寄存器返回的价值。
- 大小判定编译器计算结构体
sizeof(S),若超过 16 字节则直接进入大结构体路径。 - 八字节槽分组把结构体内存按 8 字节一组切分,得到若干槽位。
- 类型分类每个槽按内容字段类型合并为 INTEGER 或 SSE 等类别。
大结构体返回:自带保温箱 RDI
现在客人点了「超级全家桶」——一个 64 字节的大结构体。打包盒塞不进出餐窗,怎么办?后厨和客人的约定是:客人自己带一个保温箱(栈上缓冲区),进门前先把箱子从 RDI 这个特殊通道递给后厨;后厨炒好后直接往箱子里装,装完再把箱子递回来。全程没有一个菜需要走窗口,也没有多搬一次手。
这正是 System V ABI 处理大结构体返回的方式:调用者(客人)在栈上分配一块足够大的内存缓冲区,把缓冲区的地址作为「隐藏的第 0 个参数」放入 RDI 寄存器,交给被调函数。被调函数把整个结构体的内容直接写入这块内存,然后返回,RAX 同时带上缓冲区地址以方便调用者链式使用。这里面没有多余的一次内存拷贝——写入就是最终位置。
这个设计的工程意义重大:如果按朴素思路「先在函数内构造一个临时结构体,再拷贝到栈上另一处给调用者」,64 字节的菜就会被来回搬运两次,缓存和带宽都被白白浪费。RDI 隐式指针方案把目标地址直接传给后厨,后厨在目标位置原地装盒。C++ 编译器在此基础上进一步做 RVO/NRVO:连函数体内的临时对象都省略掉,直接在缓冲区构造返回值。
保温箱方案少走两道窗口,大份菜也毫发无损——它是 ABI 设计中的「零拷贝」智慧,也是 Rust/C++ 大型对象返回值性能表现优异的底层原因之一。
- 栈上分配调用者预留
sizeof(S)字节的栈空间作为返回缓冲区。 - RDI 传址将
buf的地址放入 RDI,作为隐藏的第 0 个参数传给被调函数。 - 直接写入被调函数把结构体数据写入
[buf, buf+size)。 - 返回指针函数返回时 RAX 携带缓冲区地址,方便调用者链式使用。
ABI 调用约定:行业手册 System V
前面说的所有窗口规则,并不是某一家饭店自创的,而是「餐饮行业协会」发布的统一手册——《System V ABI》。只要后厨照手册做,全世界的客人都能看懂你的出餐口。这也是为什么 C 写的库能被 Rust、Go、Python 扩展等不同「国别」的代码调用。
System V ABI(简称 psABI)是 x86_64 Linux、BSD 等类 Unix 系统遵循的二进制接口标准。它规定了参数怎么传(前 6 个整数参数依次用 RDI、RSI、RDX、RCX、R8、R9),返回值怎么接(整数用 RAX/RDX,浮点用 XMM0/XMM1),以及调用者保存 vs 被调者保存的寄存器边界。函数只需要在自己的躯体里保证:进来时遵守手册,出去时也遵守手册,整个程序就可以像流水线一样协作。
寄存器分为调用者保存(caller-saved,如 RAX、RCX、RDX、RSI、RDI)和被调者保存(callee-saved,如 RBX、RBP、R12-R15)。「调用者保存」的意思是:如果你在乎这些寄存器里的值,就得自己先存起来,因为被调函数可能随意改写它们。「被调者保存」则保证你从函数回来时,这些寄存器和进去时一样。返回值正好落在调用者保存区——因为它本来就是函数「交接」给调用者的物品,不需要额外保护。注意 Windows x64 的 ABI 与 System V 不同,参数寄存器前四个是 RCX、RDX、R8、R9,返回值寄存器则相似。跨平台写 JIT 或手写汇编时,选错手册会导致灾难。
行业手册的价值在于「统一」。你写的库、我写的框架、他用汇编抠的底层组件,只要都按 System V 出餐,就能严丝合缝地拼在一起。这也是 ABI 被称为二进制世界「通用语言」的原因。
- RAX/RDX 通用返回第一个整数或指针返回值存放在 RAX,第二个在 RDX。
- XMM0/XMM1 浮点返回第一个浮点返回值存放在 XMM0,第二个在 XMM1。
- 独立分类返回值寄存器的分配只由返回类型自身决定,与函数参数个数无关。
编译优化策略:老后厨的出餐心得
老练的后厨不会傻等菜出锅再找盒子——他们会在你递保温箱时就顺手把锅端到箱子边。编译器的 RVO(返回值优化)正是这个道理。所谓 RVO,是指编译器看到 T f() { T tmp; ... return tmp; } 这种写法时,干脆不在函数内部创建 tmp 的独立栈空间,而是直接在 RDI 指向的返回缓冲区里构造 tmp。于是 return 语句不再触发拷贝,整个函数一次拷贝都没有。
这是「别名」思想的胜利:编译器让局部变量与返回缓冲区共享同一块内存,所有对 tmp 的操作都在最终目的地完成。结构体越大,收益越明显——省掉的不只是一次 memcpy,而是缓存污染和两次全量搬家。C++17 甚至把某些场景的 RVO 从「优化」升级为「标准强制行为」,正是这层语义带来的保证。
除了 RVO,后厨还有几手绝活。寄存器分配:较小的返回值通过 RAX/RDX/XMM0 传递,编译器会优先把计算结果直接放进这些寄存器,避免临时存内存。尾调用优化:如果函数 A 的返回值就是调用函数 B 的返回值,编译器可以把 A 的返回地址也用作 B 的,直接把 B 的返回值收编为 A 的返回值,省掉一层调用栈。内联展开:小函数直接嵌进调用者体内,返回寄存器从「传递」变成了「无感交付」。理解这些优化,对设计高性能库的返回类型以及手写汇编都有直接指导意义。
后厨的本事,在于把「多一道手的搬运」化简到零。下一回你在 C++ 里返回一个大对象,心里该有数:是 RDI 保温箱加上 RVO 的巧手,让那盆全家桶稳稳落在客人桌上。
- 临时消除编译器不为局部变量
t分配独立栈帧,直接与返回缓冲区地址合一。 - 构造下沉
t的构造和赋值语句全部直接作用于 RDI 指向的缓冲区。 - 拷贝消除
return t;不再调用拷贝构造函数或 memcpy。
AI 应用场景
场景一:AI 推理引擎的张量算子优化
阶段:模型推理与性能优化
解决的问题:AI 推理引擎(如 PyTorch CPU 版、ONNX Runtime)每层张量计算都会调用大量 C++ 算子。若算子返回 Eigen 固定大小矩阵或形状描述符(8~16 字节小结构体),不了解 ABI 时可能会习惯性返回指针或引用。正确利用「小结构体走 RAX/RDX 寄存器返回」的规则,可以省去堆分配与解引用,显著降低每算子纳秒级开销。这就像服务员不绕路去储物柜取打包盒,直接在出餐窗口递给你。
具体案例:PyTorch 的 TensorIterator 在广播形状推导时返回一种 16 字节以内的形状描述类型,编译器按 ABI 自动把结果放入 RAX/RDX,省一次内存访问。手写算子时,让返回类型保持在 16 字节以内并借助 ABI 寄存器传递,热点路径可提速 5%~15%。
场景二:ctypes / FFI 跨语言绑定返回值处理
阶段:工具链与集成调试
解决的问题:Python AI 生态常用 ctypes 调用 C/C++ 库,若 C 函数返回一个 C struct,ctypes 需要在调用前正确声明 restype,否则默认按 int 解析返回的 RAX,导致 3D 坐标、颜色值等结构体数据错乱。理解「小结构体走 RAX/RDX、大结构体走 RDI 隐式指针」后,可以快速定位这类静默错误:不是 Python 写错了,而是出餐窗口认错了。
具体案例:调用 Vulkan/OpenGL 数学库时,返回 struct { float x, y, z; }(12 字节),ctypes 中把 restype 设置为对应 Structure 类型,内部才会按 XMM0/RAX 规则正确拼接。若错误地把大结构体当小结构体声明,则会读到垃圾数据甚至段错误。
场景三:AI 编译器后端的返回值代码生成
阶段:编译优化与代码生成
解决的问题:TVM/XLA 等 AI 编译框架把算子描述编译为 LLVM IR 或直接生成机器码时,必须遵守目标平台 ABI 的返回值约定,否则生成的函数无法被宿主程序正确调用。特别是自定义调度返回聚合类型时,编译器后端会自动插入 RDI 隐式指针和 RAX 返回地址。这相当于后厨在开张前,先把《出餐手册》背得滚瓜烂熟。
具体案例:TVM 的 CodeGenCPU 在生成返回多个值的调度时,会把结果打包成小结构体按寄存器返回,或在结构体超过 16 字节时自动生成栈缓冲加 RDI 传递。理解这个约定,才能在调试 IR 时看清返回值被搬到了哪里。
Python 演示
"""x86_64 System V ABI 返回值寄存器分配模拟器""" def classify_int(nbits): """整数返回值: <=64 位走 RAX, <=128 位走 RAX:RDX""" if nbits <= 64: return ['RAX'] if nbits <= 128: return ['RAX', 'RDX'] raise ValueError('x86_64 ABI 不支持超过 128 位的整数返回') def classify_float(typename): """浮点返回值: float/double 走 XMM0""" if typename in ('float', 'double'): return ['XMM0'] raise ValueError('不支持的浮点类型: ' + typename) class StructType: """模拟 C 结构体, 只需名称与大小""" def __init__(self, name, size): self.name = name self.size = size def __repr__(self): return 'struct %s (%dB)' % (self.name, self.size) def classify_struct(st): """结构体返回: <=16B 拆成两个八字节槽走寄存器, >16B 走 RDI 隐式指针""" if st.size <= 8: return ['RAX'] if st.size <= 16: return ['RAX', 'RDX'] return ['RDI(隐式指针)', 'RAX(缓冲区地址)'] def get_return_locations(return_type): """根据 System V ABI 规则, 返回返回值应使用的寄存器列表""" if isinstance(return_type, int): return classify_int(return_type) if isinstance(return_type, str): return classify_float(return_type) if isinstance(return_type, StructType): return classify_struct(return_type) raise TypeError('未知返回类型: %r' % (return_type,)) def main(): cases = [ 32, 64, 128, 'double', StructType('Point2D', 8), StructType('Point3D', 12), StructType('Matrix4x4', 64), ] for case in cases: locs = get_return_locations(case) print('返回 %-16s -> %s' % (case, ' + '.join(locs))) if __name__ == '__main__': main()
if nbits <= 64: return ['RAX']64 位及以内的整数只有一个返回值槽,走主窗口 RAX。if nbits <= 128: return ['RAX', 'RDX']128 位整数需要主窗口加副窗口,两半拼成完整返回值。if typename in ('float', 'double'): return ['XMM0']浮点值统一走甜点窗口 XMM0,不占炒菜窗口。if st.size <= 16: return ['RAX', 'RDX']小结构体按两个八字节打包盒装进 RAX/RDX,无需内存。return ['RDI(隐式指针)', 'RAX(缓冲区地址)']大结构体由调用者经 RDI 递入保温箱,函数原地装盒。
常见误区
误区结构体字段多、体积大,返回值肯定是在栈上构造一份再拷贝给调用者。64 字节的大对象来回搬两次是没办法的事。
正解即使结构体达到 16 字节(如两个 double),也完全可以通过 RAX/RDX 或 XMM0/XMM1 寄存器返回,没有任何栈拷贝。只有大于 16 字节且分类为 MEMORY 时才走 RDI 隐式指针路径;而且该路径直接在调用者的缓冲区中写入,不存在「先构造再拷贝」的多余动作。大酒楼的保温箱方案就是为省掉这次搬运而设计的。
为何常见:人们把编译器的朴素实现(临时对象加拷贝)当成 ABI 唯一的实现方式,忽略了寄存器传递和隐式指针优化这两条更聪明的出餐路线。
误区函数返回值嘛,理应都存在 RAX 里。浮点数无非是把 RAX 当整数用,结构体也是先塞进 RAX 再读出来。
正解浮点返回走 XMM0/XMM1(SSE 向量寄存器),与 RAX 完全独立;大结构体返回则通过 RDI 传入缓冲区地址,数据本体根本不经过寄存器。RAX 只负责整数、指针以及部分小结构体返回。这就像甜点有自己的窗口,全家桶走保温箱,不是每道菜都非得从主窗口递。
为何常见:教材默认讲整数返回,容易把 RAX 想象成「唯一返回值寄存器」,忽略了浮点与聚合返回的分流设计。
误区RVO 无非是编译器帮我省掉一个拷贝构造函数,性能差别很小,不值得关注。
正解对于大结构体,RVO 省掉的是与函数返回值缓冲区之间的完整内存拷贝,可能是几十到几百字节的搬运,以及由此产生的缓存 miss。C++17 后部分 RVO 已是语言标准行为而非可选优化,理解它才能正确理解对象生命周期。后厨把锅直接端到保温箱边,省下的不是「一次端盘」,而是整条配送链路。
为何常见:小对象上拷贝代价低,反衬不出 RVO 的价值;大对象默认构造可能又慢又被复用,实际收益被严重低估。
误区网上资料说 x86_64 返回值放 RAX,那我写 Windows 程序也这么写肯定没问题。毕竟都是 x86 嘛。
正解System V ABI 只用于 x86_64 的 Linux、BSD、macOS 等类 Unix 系统;Windows x64 使用 Microsoft x64 ABI。虽然整数返回值也是 RAX,但参数传递(RCX、RDX、R8、R9)和寄存器保存规则不同,结构体返回的分类逻辑也有差异。两本手册都对,但饭馆的连锁店开到了不同城市,规矩得按当地分店来。
为何常见:大家讨论 ABI 时默认环境是 Linux,「x86_64」字眼掩盖了 Windows 与 Unix 之间的关键差异。
面试题
Q1. 在 x86_64 System V ABI 下,函数返回一个 64 位整数,结果存放于哪个寄存器?
- A.累加器寄存器 EAX
- B.返回寄存器 RAX← 正确答案
- C.基址寄存器 RBX
- D.参数寄存器 RDI
答案:B。System V ABI 规定 64 位整数返回值存放在 RAX,而不是 32 位的 EAX(EAX 只是 RAX 的低 32 位)或 RBX/RDI。就像大酒楼主窗口 RAX 才是递菜的固定位置,其他窗口各有用途,不能混用。
Q2. 函数返回 __int128 时,高 64 位存放在哪个寄存器?
- A.低段部分 RAX
- B.参数通道 RCX
- C.高位副窗 RDX← 正确答案
- D.栈顶指针 RSP
答案:C。x86_64 System V ABI 把 128 位返回值拆成两半:低 64 位放 RAX,高 64 位放 RDX。对应比喻:大份双拼菜分别走主窗口 RAX 和副窗口 RDX,两个窗口拼起来才是完整的一道菜。
Q3. 浮点 double 返回值存储在哪个寄存器?
- A.FPU 浮点栈顶 ST(0)
- B.向量寄存器 XMM0← 正确答案
- C.通用寄存器 RAX
- D.栈内存临时区
答案:B。System V ABI 规定 double/float 返回值经 SSE 寄存器 XMM0 传递,不再使用 x87 的 ST(0)。对应比喻:甜点走甜点窗口 XMM0,不占炒菜窗口 RAX;FPU 浮点栈是 32 位时代的老黄历。
Q4. 按 System V ABI,结构体返回值在多字节以内时通过寄存器返回?
- A.不超过 8 字节
- B.不超过 16 字节← 正确答案
- C.不超过 32 字节
- D.不超过 64 字节
答案:B。ABI 的分类规则中,结构体总大小不超过 16 字节,且每个 8 字节槽分类结果都不是 MEMORY 时,通过寄存器返回。超过 16 字节或任一槽为 MEMORY 则走 RDI 隐式指针。小份全家桶用 RAX/RDX 两个打包盒刚好装下。
Q5. 大结构体(如 64 字节的矩阵)作为返回值,通过什么机制传递给调用者?
- A.存入静态全局变量区
- B.RDI 隐式指针写入缓冲区← 正确答案
- C.XMM0 配合栈区临时存储
- D.RAX 多批次分块传递
答案:B。调用者先在栈上分配缓冲区,把地址放在 RDI 作为隐藏的第 0 个参数传给函数,函数直接把结果写入缓冲区,从源头避免栈拷贝。对应比喻:客人自带保温箱从 RDI 通道递进去,后厨原地装盒,一个菜都不走窗口。
Q6. 以下关于 System V ABI 返回值约定的说法,正确的有哪些?
- A.整数返回值存放在 RAX← 正确答案
- B.浮点返回值存放在 XMM0← 正确答案
- C.指针返回值存放在 RDI
- D.128 位整数高位在 RDX← 正确答案
答案:ABD。整数与指针都属于 INTEGER 类,通过 RAX 返回,所以 C 错,RDI 是参数寄存器;浮点走 XMM0;128 位整数由 RAX(低)+ RDX(高)拼接。三句话对应三个出餐窗口的职责,互不串位。
Q7. 关于 C++ 的 RVO(返回值优化),下列说法正确的是?
- A.仅在 C++17 之后生效
- B.消除大对象返回时的额外拷贝← 正确答案
- C.仅对整数值类型返回有效
- D.强制增加临时对象的构造
答案:B。RVO 让局部对象直接在 RDI 指向的返回缓冲区中构造,省掉 return 时的拷贝。C++17 只是把部分场景写成标准强制,但 GCC/MSVC 早在 C++11 前就实现了 RVO,因此 A 错;它主要针对类对象而非整数,C 错;它减少而非增加临时对象,D 错。
Q8. 在 x86_64 System V ABI 下,以下哪些说法正确?
- A.返回 char 类型时值扩展到 32 位← 正确答案
- B.返回 float 只用 XMM0 的低 32 位← 正确答案
- C.返回指针使用 RDI 寄存器
- D.返回 __int128 需要 RAX + RDX← 正确答案
答案:ABD。char 等窄整数返回会经 EAX 做符号或零扩展;float 只占用 XMM0 的低 32 位;__int128 拆成 RAX(低)和 RDX(高)。指针和整数一样走 RAX,所以 C 错。
Q9. 结构体 struct { int a, b, c; }(12 字节)作为返回值,寄存器分配的正确结果是?
- A.三个字段全部存入 RAX 寄存器
- B.前两个在 RAX 第三个在 RDX← 正确答案
- C.因超过阈值走 RDI 隐式指针
- D.仅首字段在 RAX 其余在 RDX
答案:B。12 字节不超过 16 字节,按 8 字节槽切分:低槽含 a、b(INTEGER),高槽含 c 与 4 字节填充(INTEGER),两个槽分别放入 RAX 与 RDX。不是所有字段都塞进 RAX,也不是走隐式指针。
Q10. 关于 ABI(应用二进制接口)的作用,以下说法正确的有哪些?
- A.规定函数调用时参数的传递寄存器← 正确答案
- B.规定返回值存放位置和字节宽度← 正确答案
- C.决定 CPU 的指令集架构细节
- D.保证不同语言编译产物可互操作← 正确答案
答案:ABD。ABI 是一套二进制层面的「出餐手册」,定义参数或返回值寄存器、栈布局、字节序等,让 C、Rust、汇编等不同编译器产物在同一平台协同工作(D)。它依赖但不决定 CPU 的微架构细节(C 错)。