clang / gcc:某些内联汇编操作数可以满足多个约束,例如,当操作数可以满足寄存器或内存位置时,
"rm"
。例如,64 x 64 = 128 位乘法:
__asm__ ("mulq %q3" : "=a" (rl), "=d" (rh) : "%0" (x), "rm" (y) : "cc")
生成的代码似乎为参数
3
选择了内存约束,如果我们寄存器匮乏,这会很好,以避免溢出。显然,x86-64 上的寄存器压力比 IA32 上的小。然而,生成的程序集片段(由clang)是:
movq %rcx, -8(%rbp)
## InlineAsm Start
mulq -8(%rbp)
## InlineAsm End
选择内存限制显然毫无意义!将约束更改为:
"r" (y)
,但是(强制寄存器)我们得到:
## InlineAsm Start
mulq %rcx
## InlineAsm End
正如预期的那样。这些结果适用于 clang / LLVM 3.2(当前 Xcode 版本)。第一个问题:为什么 clang 在这种情况下会选择效率较低的约束?
其次,还有不太广泛使用的、以逗号分隔的多重替代约束语法:
"r,m" (y)
,应该评估每种替代方案的成本,并选择导致较少复制的方案。这似乎有效,但 clang 只是选择第一个 - 证据如下:"m,r" (y)
我可以简单地删除
"m"
替代约束,但这并不能表达可能的合法操作数的范围。这让我想到了第二个问题:这些问题是否已在 3.3 中得到解决或至少得到承认?我尝试过查看 LLVM 开发档案,但我宁愿在进一步不必要地限制约束或加入项目讨论之前征求一些答案等等
我在 cfe-dev(clang 前端开发人员列表)上收到了一位开发人员的回复:
LLVM 目前总是溢出“rm”约束以简化 在后端处理内联汇编(如果需要,可以在 llvmdev 上询问 细节)。 我不知道在不久的将来有任何计划来解决这个问题。所以这显然是一个“已知”问题。 clang 的目标之一是正确处理 gcc 的内联汇编语法以及其他扩展,在本例中它就是这样做的 - 只是效率不高。简而言之,这本身并不是一个错误。
"r,m"
约束语法。我认为这是目前最好的妥协方案。
gcc
将选择最好的 - 可能是尽可能使用寄存器 - 并且
clang
将通过忽略逗号后的其他选项来强制使用寄存器。如果不出意外,它仍然保留了汇编语句的语义意图,即描述可能的约束,即使它们被忽略了。
最后的说明(20130715): 这个特定的示例不会在单个位置使用 "r,m"
约束进行编译 - 我们必须为每个位置提供替代约束匹配,例如,
: "=a,a" (rl), "=d,d" (rh) : "%0,0" (x), "r,m" (y)
对于 GCC 的多个替代约束,这是
必需的。但我们正在进入 GCC 过去已知会出现错误的领域 - 无论从 4.8.1 开始是否属实,我不知道。 Clang 可以在没有其他约束中的替代方案的情况下工作,这与 GCC 语法不兼容,因此必须被视为一个错误。
如果性能至关重要,请使用"r"
,否则,请坚持使用
"rm"
,也许 clang 将来会解决这个问题,即使它对 GCC 有利。