错误:整数寄存器的大小不受支持 [英] error: unsupported size for integer register

查看:260
本文介绍了错误:整数寄存器的大小不受支持的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在Windows上使用i686 gcc.当我用单独的asm语句构建代码时,它起作用了.但是,当我尝试将其合并为一个语句时,它不会生成并给我一个error: unsupported size for integer register.

这是我的代码

u8 lstatus;
u8 lsectors_read;
u8 data_buffer;

void operate(u8 opcode, u8 sector_size, u8 track, u8 sector, u8 head, u8 drive, u8* buffer, u8* status, u8* sectors_read)
{
    asm volatile("mov %3, %%ah;\n"
                "mov %4, %%al;\n"
                "mov %5, %%ch;\n"
                "mov %6, %%cl;\n"
                "mov %7, %%dh;\n"
                "mov %8, %%dl;\n"
                "int $0x13;\n"
                "mov %%ah, %0;\n"
                "mov %%al, %1;\n"
                "mov %%es:(%%bx), %2;\n"
                : "=r"(lstatus), "=r"(lsectors_read), "=r"(buffer)
                : "r"(opcode), "r"(sector_size), "r"(track), "r"(sector), "r"(head), "r"(drive)
                :);
    status = &lstatus;
    sectors_read = &lsectors_read;
    buffer = &data_buffer;
}

解决方案

错误消息有点误导.这似乎是由于GCC的8位寄存器用完了.

有趣的是,如果您只是编辑模板以删除对最后2个操作数的引用,则它会编译而不会出现错误消息( https ://godbolt.org/z/oujNP7 ),即使不将其从输入约束列表中删除! (整理您的asm语句是一种有用的调试技术,可以找出GCC不喜欢它的哪一部分,而现在无需关心asm是否会做任何有用的事情.)

删除2个较早的操作数并更改数字表明"r"(head), "r"(drive)并不是特别的问题,只是所有内容的组合.

似乎GCC避免使用像AH这样的高8位寄存器作为输入,而x86-16仅具有4个低8位寄存器,但您有6个u8输入. 所以我认为GCC意味着它用完了它愿意使用的字节寄存器.

(这3个输出未声明为早期响尾蛇,因此允许它们与输入重叠.)


您也许可以通过使用"rm"为GCC提供选择内存输入的选项来解决此问题. ( x86特定的约束(如"Q"选择高8位寄存器将无济于事,除非您要求它选择 correct 一个,以使编译器为您发出mov.)这可能会让您的代码编译,但是结果将完全被破坏.

您重新引入了与以前基本相同的错误:不告诉编译器您要写入哪个寄存器,因此例如mov %4, %%al将在实际读取该操作数之前覆盖GCC选作输入的寄存器之一. /p>

在您使用的所有寄存器上声明clobbers不会留下足够的寄存器来容纳所有输入变量. (除非您允许使用内存源操作数.)这可能有效,但效率很低:如果您的asm模板字符串以mov开头或结尾,您几乎总是在做错事.

此外,除了使用内联汇编的方式外,还有其他一些严重的错误.您不提供指向缓冲区的输入指针. int $0x13不会为您分配新的缓冲区,它需要ES:BX中的指针(它会取消引用但保持不变). GCC要求ES = DS = SS,因此在调用C代码之前,您已经必须正确设置细分,而且不必每次调用都要做此事.

即使在内联汇编之外的C术语中,您的函数也没有意义. status = &lstatus;修改函数arg的 value ,而不引用它来修改指向输出的变量.这些分配写入的变量在函数末尾消失.但是全局临时变量确实必须进行更新,因为它们是全局变量,其他一些功能也可以看到它们的价值.也许您的意思是*status = lstatus;之类的变量类型不同?

如果那个C问题不明显(至少指出了一次),那么在准备尝试混合C和asm之前,您需要对C进行更多练习,这需要您同时了解 >很好,以便在准确的约束下向编译器正确描述您的asm.


https://stackoverflow.com/tags/inline-assembly/info .例如@fuz的版本没有ES设置(因为GCC需要您在调用任何C之前已经完成此操作):

typedef unsigned char u8;
typedef unsigned short u16;

// Note the different signature, and using the output args correctly.
void read(u8 sector_size, u8 track, u8 sector, u8 head, u8 drive,
    u8 *buffer, u8 *status, u8 *sectors_read)
{
    u16 result;

    asm volatile("int $0x13"
        : "=a"(result)
        : "a"(0x200|sector_size), "b"(buffer),
          "c"(track<<8|sector), "d"(head<<8|drive)
        : "memory" );  // memory clobber was missing from @fuz's version

    *status = result >> 8;
    *sectors_read = result >> 0;
}

使用GCC10.1 -O2 -m16 在Godbolt上进行编译:

read:
        pushl   %ebx
        movzbl  12(%esp), %ecx
        movzbl  16(%esp), %edx
        movzbl  24(%esp), %ebx      # load some stack args
        sall    $8, %ecx
        movzbl  8(%esp), %eax
        orl     %edx, %ecx          # shift and merge into CL,CH instead of writing partial regs
        movzbl  20(%esp), %edx
        orb     $2, %ah
        sall    $8, %edx
        orl     %ebx, %edx
        movl    28(%esp), %ebx     # the pointer arg
        int $0x13                  # from the inline asm statement
        movl    32(%esp), %edx     # load output pointer arg
        movl    %eax, %ecx
        shrw    $8, %cx
        movb    %cl, (%edx)
        movl    36(%esp), %edx
        movb    %al, (%edx)
        popl    %ebx
        ret

可能可以使用register u8 track asm("ch")或其他方式使编译器只编写部分reg,而不是shift/OR.


如果您不想了解约束的工作原理,请不要使用GNU C内联汇编.相反,您可以编写从C调用的独立函数,这些函数根据编译器使用的调用约定接受args(例如gcc -mregparm=3或仅使用传统的低效率调用约定的堆栈上的所有内容.)

您可以比上述GCC的代码生成更好的工作,但请注意,内联汇编程序可以优化为周围的代码,并避免将某些实际复制到内存中以通过堆栈传递args.

I'm using i686 gcc on windows. When I built the code with separate asm statements, it worked. However, when I try to combine it into one statement, it doesn't build and gives me a error: unsupported size for integer register.

Here's my code

u8 lstatus;
u8 lsectors_read;
u8 data_buffer;

void operate(u8 opcode, u8 sector_size, u8 track, u8 sector, u8 head, u8 drive, u8* buffer, u8* status, u8* sectors_read)
{
    asm volatile("mov %3, %%ah;\n"
                "mov %4, %%al;\n"
                "mov %5, %%ch;\n"
                "mov %6, %%cl;\n"
                "mov %7, %%dh;\n"
                "mov %8, %%dl;\n"
                "int $0x13;\n"
                "mov %%ah, %0;\n"
                "mov %%al, %1;\n"
                "mov %%es:(%%bx), %2;\n"
                : "=r"(lstatus), "=r"(lsectors_read), "=r"(buffer)
                : "r"(opcode), "r"(sector_size), "r"(track), "r"(sector), "r"(head), "r"(drive)
                :);
    status = &lstatus;
    sectors_read = &lsectors_read;
    buffer = &data_buffer;
}

解决方案

The error message is a little misleading. It seems to be happening because GCC ran out of 8-bit registers.

Interestingly, it compiles without error messages if you just edit the template to remove references to the last 2 operands (https://godbolt.org/z/oujNP7), even without dropping them from the list of input constraints! (Trimming down your asm statement is a useful debugging technique to figure out which part of it GCC doesn't like, without caring for now if the asm will do anything useful.)

Removing 2 earlier operands and changing numbers shows that "r"(head), "r"(drive) weren't specifically a problem, just the combination of everything.

It looks like GCC is avoiding high-8 registers like AH as inputs, and x86-16 only has 4 low-8 registers but you have 6 u8 inputs. So I think GCC means it ran out of byte registers that it was willing to use.

(The 3 outputs aren't declared early-clobber so they're allowed to overlap the inputs.)


You could maybe work around this by using "rm" to give GCC the option of picking a memory input. (The x86-specific constraints like "Q" that are allowed to pick a high-8 register wouldn't help unless you require it to pick the correct one to get the compiler to emit a mov for you.) That would probably let your code compile, but the result would be totally broken.

You re-introduced basically the same bugs as before: not telling the compiler which registers you write, so for example your mov %4, %%al will overwrite one of the registers GCC picked as an input, before you actually read that operand.

Declaring clobbers on all the registers you use would leave not enough registers to hold all the input variables. (Unless you allow memory source operands.) That could work but is very inefficient: if your asm template string starts or ends with mov, you're almost always doing it wrong.

Also, there are other serious bugs, apart from how you're using inline asm. You don't supply an input pointer to your buffer. int $0x13 doesn't allocate a new buffer for you, it needs a pointer in ES:BX (which it dereferences but leaves unmodified). GCC requires that ES=DS=SS so you already have to have properly set up segmentation before calling into your C code, and isn't something you have to do every call.

Plus even in C terms outside the inline asm, your function doesn't make sense. status = &lstatus; modifies the value of a function arg, not dereferencing it to modify a pointed-to output variable. The variable written by those assignments die at the end of the function. But the global temporaries do have to be updated because they're global and some other function could see their value. Perhaps you meant something like *status = lstatus; with different types for your vars?

If that C problem isn't obvious (at least once it's pointed out), you need some more practice with C before you're ready to try mixing C and asm which require you to understand both very well, in order to correctly describe your asm to the compiler with accurate constraints.


A good and correct way to implement this is shown in @fuz's answer to your previous question. If you want to understand how the constraints can replace your mov instructions, compile it and look at the compiler-generated instructions. See https://stackoverflow.com/tags/inline-assembly/info for links to guides and docs. e.g. @fuz's version without the ES setup (because GCC needs you to have done that already before calling any C):

typedef unsigned char u8;
typedef unsigned short u16;

// Note the different signature, and using the output args correctly.
void read(u8 sector_size, u8 track, u8 sector, u8 head, u8 drive,
    u8 *buffer, u8 *status, u8 *sectors_read)
{
    u16 result;

    asm volatile("int $0x13"
        : "=a"(result)
        : "a"(0x200|sector_size), "b"(buffer),
          "c"(track<<8|sector), "d"(head<<8|drive)
        : "memory" );  // memory clobber was missing from @fuz's version

    *status = result >> 8;
    *sectors_read = result >> 0;
}

Compiles as follows, with GCC10.1 -O2 -m16 on Godbolt:

read:
        pushl   %ebx
        movzbl  12(%esp), %ecx
        movzbl  16(%esp), %edx
        movzbl  24(%esp), %ebx      # load some stack args
        sall    $8, %ecx
        movzbl  8(%esp), %eax
        orl     %edx, %ecx          # shift and merge into CL,CH instead of writing partial regs
        movzbl  20(%esp), %edx
        orb     $2, %ah
        sall    $8, %edx
        orl     %ebx, %edx
        movl    28(%esp), %ebx     # the pointer arg
        int $0x13                  # from the inline asm statement
        movl    32(%esp), %edx     # load output pointer arg
        movl    %eax, %ecx
        shrw    $8, %cx
        movb    %cl, (%edx)
        movl    36(%esp), %edx
        movb    %al, (%edx)
        popl    %ebx
        ret

It might be possible to use register u8 track asm("ch") or something to get the compiler to just write partial regs instead of shift/OR.


If you don't want to understand how constraints work, don't use GNU C inline asm. You could instead write stand-alone functions that you call from C, which accept args according to the calling convention the compiler uses (e.g. gcc -mregparm=3, or just everything on the stack with the traditional inefficient calling convention.)

You could do a better job than GCC's above code-gen, but note that the inline asm could optimize into surrounding code and avoid some of the actual copying to memory for passing args via the stack.

这篇关于错误:整数寄存器的大小不受支持的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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