【问题标题】:multiplication instruction error in inline assembly内联汇编中的乘法指令错误
【发布时间】:2017-02-16 04:04:35
【问题描述】:

考虑以下程序:

#include <stdio.h>
int main(void) {
        int foo = 10, bar = 15;
        __asm__ __volatile__("add  %%ebx,%%eax"
                             :"=a"(foo)
                             :"a"(foo), "b"(bar)
                             );
        printf("foo+bar=%d\n", foo);
}

我知道add 指令用于加法,sub 指令用于减法等等。但我不明白这些行:

 __asm__ __volatile__("add  %%ebx,%%eax"
                             :"=a"(foo)
                             :"a"(foo), "b"(bar)
                             );

:"=a"(foo) :"a"(foo), "b"(bar) ); 的确切含义是什么?它能做什么 ?当我尝试在这里使用mul 指令时,我收到以下程序的以下错误:

#include <stdio.h>
int main(void) {
        int foo = 10, bar = 15;
        __asm__ __volatile__("mul  %%ebx,%%eax"
                             :"=a"(foo)
                             :"a"(foo), "b"(bar)
                             );
        printf("foo*bar=%d\n", foo);
}

错误:“mul”的操作数数量不匹配

那么,为什么我会收到此错误?我该如何解决这个错误?我在谷歌上搜索过这些,但我找不到我的问题的解决方案。我使用的是 Windows 10 操作系统,处理器是 intel core i3。

【问题讨论】:

    标签: c assembly syntax-error inline-assembly


    【解决方案1】:

    :"=a"(foo) 的确切含义是什么 :"a"(foo), "b"(bar) );

    有详细的说明如何将参数传递给asm指令here。简而言之,这就是说bar进入ebx寄存器,foo进入eax,在asm执行后,eax将包含foo的更新值。

    错误:“mul”的操作数数量不匹配

    是的,这不是 mul 的正确语法。也许您应该花一些时间阅读 x86 汇编器参考手册(例如,this)。

    我还要补充一点,使用内联 asm 通常是 bad idea


    编辑:我无法在评论中回复您的问题。

    我不太确定从哪里开始。这些问题似乎表明您根本没有很好地掌握汇编程序的工作原理。试图在 SO 答案中教你 asm 编程并不实际。

    但我可以为你指明正确的方向。

    首先,考虑一下这段asm代码:

    movl $10, %eax
    movl $15, %ebx
    addl %ebx, %eax
    

    你明白这是做什么的吗?完成后,eax 中会包含什么? ebx 中会有什么?现在,将其与此进行比较:

    int foo = 10, bar = 15;
    __asm__ __volatile__("add  %%ebx,%%eax"
                         :"=a"(foo)
                         :"a"(foo), "b"(bar)
                         );
    

    通过使用“a”约束,您要求 gcc 将 foo 的值移动到 eax 中。通过使用“b”约束,您要求它将bar 移动到ebx。它执行此操作,然后执行 asm 的指令(即add)。从 asm 退出时,foo 的新值将在 eax 中。明白了吗?

    现在,让我们看看mul。根据我链接到的文档,我们知道语法是mul value。这看起来很奇怪,不是吗? mul 怎么可能只有一个参数? 的乘积是什么?

    但如果您继续阅读,您会看到“总是将 EAX 乘以一个值”。啊。所以这里总是隐含“eax”寄存器。所以如果你写mul %ebx,那真的是mul ebx, eax,但是因为它总是必须是eax,所以写出来没有任何意义。

    但是,它比这要复杂一些。 ebx 可以保存一个 32 位值的数字。由于我们使用的是整数(而不是无符号整数),这意味着 ebx 可以有一个大到 2,147,483,647 的数字。但是等等,如果你乘以 2,147,483,647 * 10 会发生什么?好吧,因为 2,147,483,647 已经是一个可以存储在寄存器中的大数字,所以结果太大而无法放入 eax。所以乘法(总是)使用 2 个寄存器从mul 输出结果。这就是该链接在提到“将结果存储在 EDX:EAX 中”时的含义。

    所以,你可以这样写你的乘法:

    int foo = 10, bar = 15;
    int upper;
    __asm__ ("mul %%ebx"
             :"=a"(foo), "=d"(upper)
             :"a"(foo), "b"(bar)
             :"cc"
             );
    

    和以前一样,这会将 bar 放入 ebx 并将 foo 放入 eax,然后执行乘法指令。

    asm 完成后,eax 将包含结果的下半部分,edx 将包含上半部分。如果 foo * bar upper 将为零。否则,事情会变得更复杂。

    但这是我愿意做的。除此之外,参加 asm 课程。读一本书。

    PS 您还可以查看this 的答案以及随后的 3 个 cmets,这说明了为什么即使您的“添加”示例也是“错误的”。

    PPS 如果此答案解决了您的问题,请不要忘记单击它旁边的复选标记,以便我获得我的业力积分。

    【讨论】:

    • 这里的“a”和“b”是什么意思?
    • 如果您阅读我为 asm 链接的文档,您会看到引用的字符串是“约束”,描述了如何将 C 语言变量映射到 asm 可以理解的内容(参见 i386 部分 @ 987654325@)。 “a”指的是 eax 寄存器。 “b”指的是 ebx。
    • 好的,谢谢。我得到了它。但我还有一个问题。我想将 foo 和 bar 变量的值相乘并将结果存储在 foo 中。我已经访问了您提供的有关 mul 指令的链接,但我仍然很难知道我应该怎么做才能实现这一目标?
    • GCC 有寄存器对 EDX:EAX 的简写 A,因此 long long out; asm("mul %2" : "=A"(out) : "a"(foo), "r"(bar) : "cc"); 允许您将整个结果视为 64 位值。
    • @DavidWohlferd:好的,感谢您的详细解释。如果 foo * bar > 2,147,483,647 的结果会发生什么?我认为由于有符号整数溢出,这将是未定义的行为。
    猜你喜欢
    • 2011-04-02
    • 1970-01-01
    • 2016-09-06
    • 2018-06-19
    • 2014-06-19
    • 1970-01-01
    • 2023-03-10
    • 2021-01-19
    • 1970-01-01
    相关资源
    最近更新 更多