Gcc 铿锵汇编程序的奇怪行为
我试图编译Zend engine的这个溢出检测宏:Gcc 铿锵汇编程序的奇怪行为,gcc,assembly,clang,llvm,x86,Gcc,Assembly,Clang,Llvm,X86,我试图编译Zend engine的这个溢出检测宏: #define ZEND_SIGNED_MULTIPLY_LONG(a, b, lval, dval, usedval) do { \ long __tmpvar; \ __asm__( \ "mul %0, %2, %3\n" \
#define ZEND_SIGNED_MULTIPLY_LONG(a, b, lval, dval, usedval) do { \
long __tmpvar; \
__asm__( \
"mul %0, %2, %3\n" \
"smulh %1, %2, %3\n" \
"sub %1, %1, %0, asr #63\n" \
: "=X"(__tmpvar), "=X"(usedval) \
: "X"(a), "X"(b)); \
if (usedval) (dval) = (double) (a) * (double) (b); \
else (lval) = __tmpvar; \
} while (0)
在组装中得到了这个结果:
; InlineAsm Start
mul x8, x8, x9
smulh x9, x8, x9
sub x9, x9, x8, asr #63
; InlineAsm End
编译器对宏的输入和输出只使用了2个寄存器,我认为它至少应该是3个,并且导致错误的计算结果(例如,-1*-1)。有什么建议吗?汇编代码有问题。根据GCC关于以下方面的文件: 对不能与输入重叠的所有输出操作数使用“&”约束修饰符(请参见修饰符)。否则,GCC可能将输出操作数分配到与不相关的输入操作数相同的寄存器中,前提是汇编器代码在生成输出之前使用其输入。如果汇编代码实际上由多条指令组成,则此假设可能是错误的
这基本上是说,从您写入一个没有用符号标记的输出参数的那一刻起,就不允许再使用输入参数,因为它们可能已被覆盖。该语法是围绕包装单个insn的概念设计的,该insn在写入其输出之前读取其输入 当您使用多个INSN时,通常需要在约束上使用早期的clobber修饰符(
“=&x”
),以便在读取所有输入之前让编译器知道您写入了输出或读写寄存器。然后它将确保输出寄存器与任何输入寄存器不是同一个寄存器
另请参见TagWiki,以及我的内联asm文档集和答案
您真的需要将所有这些说明放在内联asm中吗?您不能将long tmp=a*b
作为输入操作数吗?然后,如果编译器需要函数中其他地方的a*b
,CSE可以看到它
。因此,希望您可以劝说编译器以这种方式执行sub
。然后它可以使用subs
从sub
设置标志,而不需要在usedval
上进行单独的测试insn
如果你不能让你的目标编译器生成你想要的代码,那么当然,给内联asm一个机会。但请注意,我见过使用内嵌asm的clang比gcc糟糕得多。它倾向于使内联代码变得更糟糕
x86上的asm。答案几乎正确,原因错误。“输入参数使用两次”是胡说八道。实际上,在读取最后一个输入参数之前,所有写入的输出参数都需要它。谢谢@Peter Cordes!主要原因是由于该函数是在最新的php源代码中定义的,所以我根本不想弄乱它。宏位于#内,如果仅适用于aarch64。@Sunary。我不认为
long\uu tmpvar=(long)a*(long)b
是“凌乱的”,除非a
和b
有不同的类型,或者在AARC64上编译得不好。
#define ZEND_SIGNED_MULTIPLY_LONG(a, b, lval, dval, usedval) do { \
long __tmpvar; \
__asm__( \
"mul %[tmp], %[a], %[b]\n\t" \
"smulh %[uv], %[a], %[b]\n\t" \
"sub %[uv], %[uv], %[tmp], asr #63\n" \
: [tmp] "=&X"(__tmpvar), [uv] "=&X"(usedval) \
: [a] "X"(a), [b] "X"(b)); \
if (usedval) (dval) = (double) (a) * (double) (b); \
else (lval) = __tmpvar; \
} while (0)