You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

向函数传递参数:栈推送与寄存器方式的正确性探讨

Understanding Function Argument Passing: Stack vs. Register Calls

Great question—this cuts right to the core of calling conventions: the standardized rules that define how callers and functions exchange arguments, return values, and manage the stack. Let’s break down your two examples and clarify what’s going on:

1. Stack-Based Argument Passing (cdecl Convention)

The code you showed:

push $2
push $5
call addme
add $16, %rsp

This is a valid implementation for the cdecl calling convention, which is common in 32-bit x86 systems (and can be used in 64-bit if explicitly specified). Here’s why it works:

  • Right-to-left parameter pushing: Since addme(x, y) takes x as the first parameter and y as the second, cdecl requires pushing the rightmost parameter first (y=2), then the leftmost (x=5). This ensures the function can access arguments in the correct order from the stack.
  • Caller cleans up the stack: The add $16, %rsp (note: in 32-bit this would be add $8, %esp, since each parameter is 4 bytes) adjusts the stack pointer back to its original position after the call. This is a key part of cdecl—since the caller knows how many arguments were passed, it’s responsible for cleaning up the stack.

This convention is especially useful for variable-argument functions (like printf), where the number of arguments isn’t known at compile time—traversing the stack to access arguments is straightforward.

2. Register-Based Argument Passing (System V AMD64 Convention)

Using %rdi, %rsi, etc., is the default calling convention for 64-bit Linux, macOS, and BSD systems (called the System V AMD64 ABI). Here’s how it works for your addme function:

  • The first 6 integer/pointer parameters are passed in registers, in order: %rdi (1st), %rsi (2nd), %rdx (3rd), %rcx (4th), %r8 (5th), %r9 (6th). Any additional parameters get pushed to the stack (right-to-left, just like cdecl).
  • For addme(x, y), the correct assembly call would be:
    mov $5, %rdi   ; Load x=5 into the 1st argument register
    mov $2, %rsi   ; Load y=2 into the 2nd argument register
    call addme
    
  • Callee manages stack cleanup: Unlike cdecl, the function itself (callee) is responsible for setting up and tearing down its own stack frame, so the caller doesn’t need to adjust %rsp after the call.

This convention is faster because register access is quicker than stack access, making it the preferred choice for modern 64-bit systems.

Are These Approaches "Right" or "Wrong"?

Neither is inherently correct or incorrect—they just follow different rules. The critical thing is that the caller and the function must adhere to the same calling convention. If you compile your addme function with a compiler that uses the System V AMD64 convention (like GCC on 64-bit Linux), using register passing is required. If you compile it for 32-bit cdecl, stack-based passing (with right-to-left pushes and caller cleanup) is the way to go.

Mixing conventions (e.g., using register passing to call a function compiled for cdecl) will lead to crashes, incorrect argument values, or corrupted stack state—so consistency is key.

内容的提问来源于stack exchange,提问作者carl.hiass

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 23:07:46