C++ &引用;无效符号重新定义“;LLVM上的内联ASM
我在Xcode(4.5.2)中有一个项目,它使用调试配置构建得很好。然而,现在我已经切换到构建发行版配置,我遇到了一个问题:我的一个内联汇编函数得到了错误C++ &引用;无效符号重新定义“;LLVM上的内联ASM,c++,assembly,llvm,clang,gnu-assembler,C++,Assembly,Llvm,Clang,Gnu Assembler,我在Xcode(4.5.2)中有一个项目,它使用调试配置构建得很好。然而,现在我已经切换到构建发行版配置,我遇到了一个问题:我的一个内联汇编函数得到了错误无效符号重新定义。通过谷歌搜索那个错误消息,我发现有几个人犯了那个编译器错误,但并没有关于它意味着什么的信息。下面是函数,注释了错误行: inline int MulDivAdd(int nNumber, int nNumerator, int nDenominator,
无效符号重新定义
。通过谷歌搜索那个错误消息,我发现有几个人犯了那个编译器错误,但并没有关于它意味着什么的信息。下面是函数,注释了错误行:
inline int MulDivAdd(int nNumber,
int nNumerator,
int nDenominator,
int nToAdd)
{
int nRet;
__asm__ __volatile__ (
"mov %4, %%ecx \n"
"mov %1, %%eax \n"
"mull %2 \n"
"cmp $0, %%ecx \n"
"jl __sub \n"
"addl %%ecx, %%eax \n"
"adc $0, %%edx \n"
"jmp __div \n"
"__sub: \n" // "Invalid symbol redefinition"
"neg %%ecx \n"
"subl %%ecx, %%eax \n"
"sbb $0, %%edx \n"
"__div: \n" // "Invalid symbol redefinition"
"divl %3 \n"
"mov %%eax, %0 \n"
: "=m" (nRet)
: "m" (nNumber),
"m" (nNumerator),
"m" (nDenominator),
"m" (nToAdd)
: "eax", "ecx", "edx"
);
return nRet;
}
我尝试用\uu sbt
替换\uu sub
,因为我认为\uu sub
可能是一个受保护的名称,但事实并非如此。我不明白为什么这只发生在发行版中-可能是因为优化吗?使用本地标签,例如1:
和2:
,以及jxx 1f
或jxx 1b
。需要跳跃方向(f
用于向前或b
用于向后)。因此,您的代码应该如下所示:
\uuuuu asm\uuuuu volatile\uuuuu(
mov%4,%%ecx\n
mov%1,%%eax\n
“考虑%2\n”
“cmp$0,%%ecx\n”
“jl 1f\n”
添加%%ecx,%%eax\n
“adc$0,%%edx\n”
“JMP2F\n”
“1:\n”
“neg%%ecx\n”
“subl%%ecx,%%eax\n”
sbb$0,%%edx\n
“2:\n”
“divl%3\n”
“mov%%eax,%0\n”
)
纯粹由数字组成的符号是“函数的局部”。因为“内联”意味着代码在物理上是重复的,所以您得到多个符号定义的原因是您的符号确实以“全局”的方式定义了多次
当然,如果你有一个调试版本,它通常意味着“没有内联”,所以内联函数不是内联的,符号只声明一次,它“工作”
[我对这种方法的效率与编译器本身会做什么有点怀疑-我认为至少考虑对一些输入使用寄存器会使它更有效] 谢谢你的快速回答!至于效率,这是我一直在移植的东西,所以我只是将原始的Intel语法ASM转换为GAS。不过,我可能会尝试在将来对它进行优化。我会尝试先将它重写为C,然后看看它是如何运行的。[我不是100%确定它是做什么的,但它看起来像是一个大数数学函数的一部分-但这可能是完全错误的]你可以解释一下跳转目标上的
f
和b
后缀是什么意思=)@Mats-“使用数字标签…”-我认为这些被称为本地标签。看,它们实际上不是函数的局部。如果重新排列代码而不更正标签引用中的f
orward和b
ack方向,则可以跳转到内联asm块的上一个副本中的1:
。gas不支持x86目标的1$:
有限范围本地标签名称。仅供参考:。如果用C编写(使用int64\t
),您将获得更好的codegen。分支完全没有必要(它应该是一个扩展和添加的符号)。我还对使用带符号数据的无符号乘法指令(mull
)感到困扰。虽然这可能与所讨论的程序无关,但它可能是一个bug。@StephenCanon那么这与问题有什么关系呢?这不需要是易变的。它只需要运行就可以产生输出。(而且,volatile
不会阻止它内联到多个位置,从而导致问题)。但是Stephen是对的,对于强制内存操作数的效率来说,这看起来很糟糕。这里使用内联asm的唯一原因是如果您知道64b/32b=>32bdiv
不会溢出商和错误。gccl/clang仍然不知道如何利用这一点,使用单个idiv
,而不是为64b/64b分区调用libgcc帮助函数。