GCC内联ASM X86 CPU标志作为输入依赖性
gcc inline asm x86 CPU flags as input dependency
我想为两个具有溢流检测的16位整数创建一个函数。我有便携式c编写的通用变体。但是通用变体对于x86目标并不是最佳的,因为执行添加/sub/etc时,CPU内部计算溢出标志。当然,有__builtin_add_overflow()
,但就我而言,它会生成一些样板。因此,我编写以下代码:
#include <cstdint>
struct result_t
{
uint16_t src;
uint16_t dst;
uint8_t of;
};
static void add_u16_with_overflow(result_t& r)
{
char of, cf;
asm (
" addw %[dst], %[src] "
: [dst] "+mr"(r.dst)//, "=@cco"(of), "=@ccc"(cf)
: [src] "imr" (r.src)
: "cc"
);
asm (" seto %0 " : "=rm" (r.of) );
}
uint16_t test_add(uint16_t a, uint16_t b)
{
result_t r;
r.src = a;
r.dst = b;
add_u16_with_overflow(r);
add_u16_with_overflow(r);
return (r.dst + r.of); // use r.dst and r.of for prevent discarding
}
我已经玩过https://godbolt.org/g/2mlf55(GCC 7.2 -O2 -STD = C 11),结果
test_add(unsigned short, unsigned short):
seto %al
movzbl %al, %eax
addw %si, %di
addw %si, %di
addl %esi, %eax
ret
所以,重新排序seto %0
。GCC似乎认为asm()
语句之间没有依赖性。和" CC" Clobber对标志依赖性没有任何影响。
我不能使用volatile
,因为seto %0
或整个功能可以(如果结果(或结果的某些部分)不使用)。
i可以添加R.DST:asm (" seto %0 " : "=rm" (r.of) : "rm"(r.dst) );
的依赖关系,并且不会发生重新排序。但这不是"正确的事情",编译器仍然可以在add
和seto
语句之间插入一些代码更改标志(但不能更改r.dst)。
是否可以说"此ASM()语句更改某些CPU标志"answers"此asm()使用一些CPU标志"来依赖语句和防止重新排序?
我还没有查看__builtin_add_overflow
的GCC输出,但是有多糟糕?@David使用它的建议,以及https://gcc.gnu.org/wiki/dontuseinlinelineasm通常很好,尤其是当您担心这将如何优化时。asm
击败了不断的传播和其他一些事情。
另外,如果您要使用ASM,请注意ATT语法是add %[src], %[dst]
操作数订单。有关详细信息,请参阅标签Wiki,除非您始终使用-masm=intel
构建代码。
是否可以说"此ASM()语句更改一些CPU标志"answers"此ASM()使用一些CPU标志"来依赖语句和防止重新排序?
否。将标志耗电指令(seto
)放入与标志产生指令的同一asm
块中。asm
语句可以随意具有许多输入和输出操作数,仅受寄存器分配难度的限制(但是多个内存输出可以使用相同的基本寄存器与不同的偏移量)。无论如何,包含add
的语句上的额外写入输出不会导致任何效率低下。
我要建议您要从一项指令中多个标志输出,请使用lahf从标志加载AH。但这不包括其他条件代码。这通常是不方便的,似乎是一个糟糕的设计选择,因为在eflags/rflags的低8位中有一些未使用的保留位,因此可以与CF,SF,SF,ZF,PF和AF一起使用。但是由于事实并非如此,因此setc
seto
可能比pushf
/Reload更好,但值得考虑。
即使有用于标志输入的语法(例如有标志输出),从让GCC插入其一些自己的某些非flag-modifying指令中,就会有很少的收益。(例如lea
或mov
)在您的两个单独的asm
语句之间。
您不希望它们重新排序或其他任何东西,因此将它们放在同一ASM语句中最有意义。即使在订购CPU上,add
也是低潜伏期,因此在其之后放置依赖指令并不是一个大的瓶颈。
和顺便说一句,如果溢出是正常发生的错误条件,则jcc
可能会更有效。但是不幸的是,GNU C asm goto
不支持输出操作数。您可以在存储器中进行指针输入并修改dst
(并使用"memory"
Clobber),但是强迫商店/重新加载远远超过使用setc
或seto
来产生编译器生成的test
/jnz
。
如果您也不需要输出,则可以在return true
和return false
语句上放置C标签if()
。例如请查看Linux的工作方式:(在我发现的这两个示例中,请使用额外的复杂因素):在启动后检查一次CPU功能后,设置用于修补代码,或者在arch_static_branch
中使用跳跃表的部分。)
- 在C++程序中输入的文本文件将不起作用,除非文本被复制和粘贴
- 多个输入输入 for 循环
- 可视C++ - 从链接器>输入>其他依赖项中排除文件
- 当我输入一个字母时,cin 输入(输入是一个 int),而不是打印错误一次,它打印正确一次,然后在循环的其余部分打印 i
- 提供依赖于输入的程序的文件路径
- 将输入输入到 c++ 数组时出现运行时错误
- 它不会打印出基于用户输入 c++ 输入的任务
- 将用户输入验证为整数:用户必须两次输入输入
- 是否可以将输入输入作为参数
- C 必须将输入输入两次
- 依赖于文件输入的c++模板
- C++使变量类型依赖于用户输入
- 在C++程序运行时输入输入值 (./filename.out)
- 即使我做了>> cin ,也不让我输入输入
- 在没有用户输入输入的情况下中断while循环
- visual将c++ Gui中的输入输入到另一个c++程序中
- 在C中输入输入后,输出窗口关闭
- 依赖范围;需要在前面输入名称;
- 将Python的输入输入到c++程序中
- 带空格的输入输入所有循环