即使文件是用
-O0
编译的,是否仍然强制优化 clang 上的 C 函数?
我正在寻找与 gcc 的
__attribute((optimize("s"))
或 __attribute((optimize(3))
等同的东西。
(相关:在 clang 中,如何使用 per-function 优化属性?)
我想做的是通过宏在几乎纯汇编中生成某些函数——其中剩余的 C 代码不应生成任何汇编代码。 理想情况下,宏将使用基于 C 的整数常量表达式来选择要粘贴的代码,并在生成函数静态化之前编写
static
。
我也不想在函数的序言中进行堆栈操作。
在 GCC 上是这样的:
enum { CONSTANT = 0 };
__attribute((optimize("Os"),noinline,noipa))
int foo(void){
if (CONSTANT) asm("mov $1, %eax; ret;");
else asm("xor %eax, %eax; ret;");
__builtin_unreachable();
}
成功掌握要点。 在 clang 上,
optimize
属性无法识别并生成一个 push %rbp; mov %rsp, %rbp
序言,这会破坏我的真实用例,以及这个玩具示例中的 ret
,所以这是最不可取的。
在 GCC 上,
__attribute((naked))
还致力于消除序言并禁用内联和过程间分析 (IPA),但 clang 坚决拒绝它,强制要求裸函数仅由纯汇编组成(没有非生成的 C 代码,甚至)。
根据 GCC 文档 的 x86 函数属性:
naked
这个属性允许编译器构造必要的函数声明,同时允许函数体是汇编代码。指定的函数不会有编译器生成的序言/结尾序列。只有基本的 asm 语句可以安全地包含在裸函数中(参见 Basic Asm)。虽然使用扩展 asm 或基本 asm 和 C 代码的混合可能看起来有效,但它们不能可靠地工作并且不受支持。
虽然不受支持,但它对我的用例来说已经足够好了。
__attribute__((optimize("Os"),noinline,noipa))
的 hack 甚至更 hacky,但实际上编译为我想要的当前 GCC 的 asm。我想用 clang 做一些类似的事情。
你如何将选择器和替代项放入三个单独的函数中,后两个标有
__attribute((naked))
你说有效?像这样的东西:
enum { CONSTANT = 0 };
__attribute((naked))
int foo1(void){
asm("mov $1, %eax; ret;");
}
__attribute((naked))
int foo0(void){
asm("xor %eax, %eax; ret;");
}
int foo(void){
if (CONSTANT) return foo1();
else return foo0();
}
Jester 的答案可能适用于足够简单的情况,如果您可以手动创建您可能需要的 asm 块的每个组合。如果它们只在一个编译单元中使用,它们可以
static
让未使用的优化掉。
但是你确实希望非内联版本对内联可见,这样你就不会在每次调用时得到额外的
jmp
尾调用,所以所有调用者都必须在同一个编译单元中。
如果那不可行,链接时优化应该让未使用的版本优化掉而不是膨胀你的二进制文件。
如果你有许多不同的分支会导致维护的可能性组合爆炸太大,你绝对应该考虑在你的构建系统中添加一个步骤来获取这些常量作为 CPP 宏,这样你就可以使用
#if
或 #ifdef
以明确定义的方式围绕 asm("");
函数中的多个 naked
语句。
你现在使用非
naked
函数所做的是对编译器的可怕滥用,它根本不受支持,只是碰巧可以工作。
if(constant)
在 naked
函数中也不受 officially 支持,但在我看来不太可能破坏,只要常量是真正的编译时常量表达式。仍然,没有保证,不像你使用 C 预处理器只是将文本粘贴在一起。