我尝试编译Zend引擎的溢出检测宏:
null
将这个结果集合在一起:
#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)
编译器仅对宏的输入和输出使用2个寄存器,我认为它必须至少为3,并导致错误的计算结果(例如,-1 * -1)。有什么建议吗?
答案 0 :(得分:5)
汇编代码有问题。来自GCC关于extended asm的文档:
对所有不能与输入重叠的输出操作数使用'&'约束修饰符(请参阅修饰符)。否则,GCC可以将输出操作数分配到同一寄存器中作为无关输入操作数,前提是汇编代码在产生输出之前消耗其输入。如果汇编程序代码实际上包含多个指令,则此假设可能为false。
这基本上表示从您写入未标有&符号的输出参数的那一刻起,您不再允许使用输入参数,因为它们可能已被覆盖。
答案 1 :(得分:3)
语法是围绕包装单个insn的概念设计的,该insn在写入输出之前读取其输入。
当您使用多个insn时,通常需要在约束("=&x"
)上使用early-clobber修饰符,让编译器知道您在读取所有输入之前写入输出或读写寄存器。然后它将确保输出寄存器与任何输入寄存器不是同一个寄存器。
另请参阅x86代码wiki,以及我的内联asm文档和SO答案at the bottom of this answer的集合。
#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)
你真的需要所有这些指令都在内联asm中吗?你不能让long tmp = a * b
成为输入操作数吗?然后,如果编译器在函数的其他地方需要a*b
,CSE就可以看到它。
You can convince gcc to broadcast the sign bit with an arithmetic right shift using a ternary operator。所以希望你可以哄骗编译器以这种方式执行sub
。然后,它可以使用subs
设置sub
的标记,而无需在usedval
上单独测试insn。
如果您无法让目标编译器生成您想要的代码,那么请确保将内联作为一个镜头。但要注意,我已经看到clang比使用内联asm的gcc差很多。它倾向于使内联的代码更糟糕 asm on x86。