如何使用 gcc 优化“不关心”参数?

问题描述 投票:0回答:2

有时函数不使用参数(可能是因为另一个“flags”参数不启用特定功能)。

但是,您必须指定一些内容,因此通常只需输入

0
。但如果您这样做,并且该函数是外部的,
gcc
将发出代码以“真正确保”该参数设置为
0

有没有办法告诉

gcc
函数的特定参数并不重要,并且它可以忽略参数寄存器中恰好存在的任何值?

更新:有人问关于XY问题。这个问题背后的上下文是我想在

x86_64
中实现一个 varargs 函数,而不使用编译器 varargs 支持。当参数位于堆栈上时,这是最简单的,因此我声明我的函数首先采用 5 或 6 个虚拟参数,以便最后一个非可变参数和所有可变参数最终都位于堆栈上。这工作得很好,只是它显然不是最佳的 - 当查看汇编代码时,很明显
gcc
正在将调用者中的所有这些参数寄存器初始化为零。

c gcc optimization
2个回答
2
投票

请不要认真对待以下答案。这个问题要求破解,所以你就可以了。

GCC 将有效地将未初始化变量的值视为“不关心”,因此我们可以尝试利用这一点:

int foo(int x, int y);

int bar_1(int y) {
  int tmp = tmp;  // Suppress uninitialized warnings
  return foo(tmp, y);
}

不幸的是,我的 GCC 版本仍然懦弱地将

tmp
初始化为零,但你的版本可能更激进:

bar_1:
.LFB0:
  .cfi_startproc
  movl  %edi, %esi
  xorl  %edi, %edi
  jmp   foo
  .cfi_endproc

另一种选择是 (ab) 使用内联汇编来伪造 GCC,使其认为

tmp
已定义(而实际上并未定义):

int bar_2(int y) {
  int tmp;
  asm("" : "=r"(tmp));
  return foo(tmp, y);
}

通过这个 GCC 成功摆脱了参数初始化:

bar_2:
.LFB1:
  .cfi_startproc
  movl  %edi, %esi
  jmp   foo
  .cfi_endproc

请注意,内联汇编必须紧接在函数调用之前,否则 GCC 会认为它必须保留输出值,这会损害寄存器分配。


0
投票

大多数(所有?)C 实现的 ABI 允许跳过未使用的参数。您可以使用所需的每组参数为要调用的函数定义任意数量的别名,然后使用正确的别名调用该函数。

仅当您想省略最后 N 个参数时,这才有效。如果您想省略前 N 个参数,那将无济于事。

如果函数位于不同的翻译单元中,则必须在被调用者的翻译单元中定义别名。如果您不想修改正在调用的库,您可能需要对链接器进行更多操作。

© www.soinside.com 2019 - 2024. All rights reserved.