Assembly llvm报告:不支持的内联asm:输入类型为';void*';将输出与类型';int';
我有以下内联汇编代码:Assembly llvm报告:不支持的内联asm:输入类型为';void*';将输出与类型';int';,assembly,inline-assembly,lldb,x86,Assembly,Inline Assembly,Lldb,X86,我有以下内联汇编代码: int get_year(int a, int *b, char * c) { int ret, t1, t2; asm ( "addl %3, %[a] \n\t" "movl %[a], %[t1] \n\t" "movl $58, %%edx \n\t" "movb %%dl, 0x04(%1)
int get_year(int a, int *b, char * c)
{
int ret, t1, t2;
asm (
"addl %3, %[a] \n\t"
"movl %[a], %[t1] \n\t"
"movl $58, %%edx \n\t"
"movb %%dl, 0x04(%1) \n\t"
: [t1] "=r" (t1), "=&D" (t2)
: [a] "r" (a), "rm" (*b), "1" (c)
: "edx", "memory"
);
ret = t1;
return ret;
}
当我通过llvm编译时,错误转储:
error: unsupported inline asm: input with type 'char *' matching output with type 'int'
: [a] "r" (a), "rm" (*b), "1" (c)
^
但是,linux内核中的memcpy函数具有相同的内联程序集使用格式:
void *memcpy(void *dest, const void *src, size_t n)
{
int d0, d1, d2;
asm volatile(
"rep ; movsl\n\t"
"movl %4,%%ecx\n\t"
"rep ; movsb\n\t"
: "=&c" (d0), "=&D" (d1), "=&S" (d2)
: "0" (n >> 2), "g" (n & 3), "1" (dest), "2" (src)
: "memory");
return dest;
}
这可以正常工作,没有任何编译错误。如果我只在生成64位代码时用叮当声编译代码,我可以重现这个问题。以32位代码为目标时没有错误。正如Michael Petch所说,这表明问题在于两个操作数的大小不同 现在还不完全清楚最好的解决方案是什么,因为你的asm声明没有多大意义。这相当于:
int get_year(int a, int *b, char *c) {
a += *b;
c[4] = 58;
return a;
}
使用汇编语句来完成使用上面的C代码可以更清楚、更高效地完成的事情没有任何好处。因此,最好的解决方案是用等效的C代码完全替换您的代码
如果您只是在玩内联汇编,那么等效的内联汇编将是:
int get_year2(int a, int *b, char * c)
{
asm("addl %[b], %[a]"
: [a] "+r" (a)
: [b] "m" (*b)
: "cc");
asm("movb $58, %[c4]"
: [c4] "=rm" (c[4]));
return a;
}
我使用了两个asm语句,因为这两个部分是不相关的。将它们分开可以提供更多的优化机会。例如,如果调用此函数但不使用返回值,编译器可以消除第一个asm语句,因为它的结果未被使用,并且没有副作用
我使用“+”约束修饰符将操作数标记为输入和输出,而不是使用给您带来问题的匹配约束,即“1”
约束。我觉得这样效果更好。[b]
操作数的约束实际上应该是“rm”
但是
您可能注意到我只使用了两条汇编语句,而您的示例使用了四条。MOVL指令不是必需的,编译器可以处理将结果移入返回值寄存器(如果需要)。最后两条汇编语句可以折叠为一条语句,该语句可以将常量直接移动到内存中,而无需破坏寄存器。说到这里,您的asm语句会破坏EFLAGS,条件代码,因此
“cc”
应该被列为破坏,但正如Peter Cordes指出的那样,x86目标不需要,但编译器认为它们无论如何都是。如果我只在生成64位代码时使用clang编译代码,我可以重现这个问题。以32位代码为目标时没有错误。正如Michael Petch所说,这表明问题在于两个操作数的大小不同
现在还不完全清楚最好的解决方案是什么,因为你的asm声明没有多大意义。这相当于:
int get_year(int a, int *b, char *c) {
a += *b;
c[4] = 58;
return a;
}
使用汇编语句来完成使用上面的C代码可以更清楚、更高效地完成的事情没有任何好处。因此,最好的解决方案是用等效的C代码完全替换您的代码
如果您只是在玩内联汇编,那么等效的内联汇编将是:
int get_year2(int a, int *b, char * c)
{
asm("addl %[b], %[a]"
: [a] "+r" (a)
: [b] "m" (*b)
: "cc");
asm("movb $58, %[c4]"
: [c4] "=rm" (c[4]));
return a;
}
我使用了两个asm语句,因为这两个部分是不相关的。将它们分开可以提供更多的优化机会。例如,如果调用此函数但不使用返回值,编译器可以消除第一个asm语句,因为它的结果未被使用,并且没有副作用
我使用“+”约束修饰符将操作数标记为输入和输出,而不是使用给您带来问题的匹配约束,即“1”
约束。我觉得这样效果更好。[b]
操作数的约束实际上应该是“rm”
但是
您可能注意到我只使用了两条汇编语句,而您的示例使用了四条。MOVL指令不是必需的,编译器可以处理将结果移入返回值寄存器(如果需要)。最后两条汇编语句可以折叠为一条语句,该语句可以将常量直接移动到内存中,而无需破坏寄存器。说到这里,您的asm语句会破坏EFLAGS,条件代码,因此应该将“cc”列为破坏,但正如Peter Cordes指出的,x86目标没有必要,但编译器认为它们无论如何都是破坏的。首先,如果您想了解asm,GNUCinlineASM是最难使用asm的方法之一。您不仅需要编写正确的asm,还需要花费大量时间使用深奥的语法来告知编译器您的代码对于输入和输出操作数的确切需求,否则您将度过一段不愉快的时光。在ASM中编写整个函数要容易得多。它们不能内联,但无论如何,这是一个学习练习。标准函数ABI比C和带约束的内联ASM之间的边界简单得多。查看维基
除了编译错误之外,您还有一个bug:您将
%[a]
,即使您告诉gcc它是一个仅输入的操作数
我假设这仍然是一个“正在进行的工作”,因为您可以用更好的代码获得相同的结果。(例如,使用%edx
作为scratch reg是完全不必要的。)当然,在一般情况下,如果这是内联到代码中的a
可能是编译时常量,或者已知与其他内容相关,则只需在C中执行,您就可以获得更好的代码(除非您花费大量时间为各种情况制作内嵌asm变体。)
现在(使用godbolt的“二进制”选项进行实际汇编)。4+(%rdx)
会产生警告,但会汇编到4(%rdx)
。IDK如何以一种在已经有偏移量时不会出错的方式写入偏移量。(例如,如果操作数是*(c+4)
,则生成的asm是4+4(%rdx)
,省略+
)是行不通的
这仍然使用匹配输出操作数的技巧,但我改为使用内存或常规约束,以允许编译器时间常数以d结尾