llvm报告:不支持的嵌入式asm:类型为'void *'的输入与类型为'int'的输出匹配 [英] llvm reports: unsupported inline asm: input with type 'void *' matching output with type 'int'

查看:231
本文介绍了llvm报告:不支持的嵌入式asm:类型为'void *'的输入与类型为'int'的输出匹配的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下内联汇编代码:

I have the below inline assembly code:

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进行编译时,错误转储:

When I compile this via llvm, error dumps:

error: unsupported inline asm: input with type 'char *' matching output with type 'int'
                : [a] "r" (a), "rm" (*b), "1" (c)
                                               ^

但是,Linux内核中的memcpy函数具有相同的内联汇编用法格式:

However, the memcpy function in linux kernel has the same format of inline assembly usage:

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;
}

,它可以正常工作,没有任何编译错误.

and this works properly without any compile error.

推荐答案

首先,如果您想让学习的asm变得湿透,那么GNU C内联asm是最难的之一使用asm的方法.您不仅需要编写正确的asm,而且还必须花费大量时间使用深奥的语法来告知编译器确切地您的代码对输入和输出操作数的需求,否则您将遇到麻烦.时间.在ASM中编写整个函数要容易得多.它们不能内联,但这仍然是一项学习练习.普通函数ABI比带有约束的C和嵌入式ASM之间的边界要简单得多.请参阅 Wiki的问题...

First of all, if you're trying to get your feet wet learning asm, GNU C inline asm is one of the hardest ways to use asm. Not only do you have to write correct asm, you have to spend a lot of time using esoteric syntax to inform the compiler of exactly what your code needs for input and output operands, or you will have a bad time. Writing whole functions in ASM is much easier. They can't be inlined, but it's a learning exercise anyway. The normal function ABI is much simpler than the boundary between C and inline ASM with constraints. See the x86 wiki...

除了该编译错误外,您还有一个错误:即使您告诉gcc这是一个仅输入的操作数,也会破坏%[a].

Besides that compile error, you have a bug: you clobber %[a], even though you told gcc it's an input-only operand.

我认为这仍然是正在进行中的工作",因为使用更好的代码可以获得相同的结果. (例如,完全没有必要使用%edx作为暂存器.)当然,在通常情况下,将其内联到代码中,其中a可能是编译时常量,或者已知与其他内容相关.仅使用C语言就能获得更好的代码(除非您花费大量时间针对各种情况制作内联asm变体.)

I assume this still a "work in progress", since you could get the same result with better code. (e.g. using %edx as a scratch reg is totally unnecessary.) Of course, in the general case where this is inlined into code where a might be a compile-time constant, or known to be related to something else, you'd get better code from just doing it in C (unless you spent a lot of time making inline-asm variants for various cases.)

int get_year(int a, int *b, char * c)
{
    int ret, t1, t2;

    asm (
        "addl %[bval], %[a] \n\t"
        "movb $58, 4 + %[cval]\n\t"  // c is an "offsetable" memory operand

        : [t1] "=&r" (t1), [cval] "=o" (*c)
        : [a] "0" (a), [bval] "erm" (*b)
        : // no longer clobbers memory, because we use an output memory operand.
    );

    ret = t1;  // silly redundancy here, could have just used a as an input/output operand and returned it, since you apparently want the value
    return ret;
}

现在进行编译和组装(使用godbolt的"binary"选项进行实际组装). 4 + (%rdx)会产生警告,但会汇编为4(%rdx). IDK如何以没有错误的方式写入偏移(如果已经存在偏移). (例如,如果操作数为*(c+4),则生成的asm为4 + 4(%rdx),则忽略+无效.)

This now compiles and assembles (using godbolt's "binary" option to actually assemble). The 4 + (%rdx) produces a warning, but does assemble to 4(%rdx). IDK how to write the offset in a way that doesn't error if there's already an offset. (e.g. if the operand is *(c+4), so the generated asm is 4 + 4(%rdx), it wouldn't work to leave out the +.)

这仍在使用matching-output-operand技巧,但我改为使用内存或常规约束条件,以允许编译器时间常量最终执行addl $constant, %edi.

This is still using the matching-output-operand trick, but I changed to using memory or general constraints to allow compiler-time constants to end up doing a addl $constant, %edi.

这使编译器在进行内联时具有尽可能多的灵活性.例如如果调用者运行了get_year(10, &arr[10], &some_struct.char_member),则它可以使用所需的加载和存储寻址方式,而不必在单个寄存器中生成c.因此,内联输出最终可能是movb $58, 4+16(%rbp, %rbx),而不是强制其使用4(%reg).

This allows the compiler as much flexibility as possible when inlining. e.g. if a caller ran get_year(10, &arr[10], &some_struct.char_member), it could use whatever addressing mode it wanted for the load and store, instead of having to generate c in a single register. So the inlined output could end up being movb $58, 4+16(%rbp, %rbx) for example, instead of forcing it to use 4(%reg).

这篇关于llvm报告:不支持的嵌入式asm:类型为'void *'的输入与类型为'int'的输出匹配的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆