【问题标题】:Why argument's size of function is increased to word size?为什么参数的函数大小增加到字大小?
【发布时间】:2019-09-04 18:44:02
【问题描述】:

我阅读了 i386 和 AMD64 的 System V ABI。他们说参数必须四舍五入到字长的倍数。我不明白为什么。

这是情况。如果您将 4 个 char 参数传递给 i386 架构上的函数,它将占用 16 个字节(每个 char 参数 4 个字节)。只为所有 4 个参数分配 4 个字节不是更有效吗?就像它应该与局部变量一样?

对齐不是答案。因为在这两种情况下,16 字节堆栈对齐可能需要 4-12 字节填充。

【问题讨论】:

标签: c linux assembly x86 abi


【解决方案1】:

将 4 个chars 放入单个寄存器(或堆栈位置)将需要创建并随后提取各个参数,这在指令方面是昂贵的。请注意,即使您在谈论堆栈,内存访问也应该非常快,因为它很可能在缓存中。

如果您真的想节省那么多空间,您仍然可以使用单个 4 字节参数自己完成。

【讨论】:

  • 我不擅长组装,但我不明白为什么它会很昂贵。如果你传递了字长参数,你会得到像这样的“mov -8(%ebp) %ecx mov -12(%ebp) %edx etc.”。在我的示例中,它将类似于“movb -8(%ebp) %ecx movb -9(%ebp) %edx etc.”。也许我什么都不懂。
  • @yevhen:大多数参数都在寄存器中传递。
  • @注意,调用约定实际上将 4 个字符打包到一个寄存器中,如果它们是结构类型参数的成员。
  • @YevhenGrushko AFAIK movb 需要一个字节大小的目标,即那些指令不存在。
  • @YevhenGrushko 是的,这可行,但请注意,它仍然是非对齐访问,因此它可能仍然读取 4 字节并在引擎盖下进行一些按摩。我不知道确切的性能,请随意在最近的 CPU 中测量!
【解决方案2】:

这样做不是更有效率吗?

你总是要说出你想优化什么:

  • 执行速度快
  • 小程序大小
  • 更少的堆栈使用量
  • 更简单的编译器
  • ...

如果你想优化以减少堆栈使用,将字节传递给函数确实会更有效。

但是,通常您希望针对快速执行速度或较小的程序大小进行优化。

与现代编译器(mov 堆栈的参数)不同,大多数 1990 年代编写的编译器我知道 push 堆栈的参数。如果编译器使用push 操作,则将字节放入堆栈会相当复杂——这会使程序变得缓慢而冗长。

(请注意,我从未见过对参数进行pop 操作。)

【讨论】:

  • 要在当前调用约定中获取参数,您应该执行 "mov 0x8(%ebp) %ecx mov 0xc(%ebp) %ebx mov 0x10(%ebp) %edx mov 0x14(%ebp) %eax ”,但在我的示例中它将是“movsbl 0x8(%ebp) %ecx movsbl 0x9(%ebp) %ebx movsbl 0xa(%ebp) %edx movsbl 0xb(%ebp) %eax” 是不是更慢?
  • @YevhenGrushko 读取参数:不,问题是写入参数。在 1980 年代或 1990 年代,当开发 System V 调用约定时,大多数编译器使用push 指令将参数放入堆栈。而且你不能push 8 位值。
【解决方案3】:

我认为最初的 C 语言作者更关注可移植性和可维护性,而不是压缩每个字节和周期。并不是说 C 对资源粗心大意,而是做出了适当的权衡。

将每个参数提升到堆栈粒度大小是有道理的,现在仍然如此。如果您不顾一切地挤进去,您可以随时更换:

int f(int a, int b, int c, int d) { ... }

struct fparm { char a,b,c,d; }; int f(struct fparm a) { ... }

现代 C 编译器对用户不太友好;或者更确切地说,他们唯一的朋友是名为 benchmark 的 luser....

【讨论】:

    猜你喜欢
    • 2015-12-06
    • 2021-10-31
    • 1970-01-01
    相关资源
    最近更新 更多