c++ - Cygwin:使用asm标签编译cpp文件

标签 c++ gcc assembly x86 cygwin

我是汇编新手,目前正在尝试使用asm标签创建c++代码。我正在使用cygwin进行编译。这是我的代码:

#include <iostream>
using namespace std;

int main()  
{  
    float flp1_num, flp2_num, flp_rslt1;

    cin >>flp1_num >>flp2_num;

    __asm
    {
        FLD flp1_num
        FLDPI
        FADD flp2_num
        FST flp_rslt1
    }

    cout << flp_rslt1;
}  

使用的语法来自here

我正在使用g++ arq.cpp -o arq.exe进行编译,这给了我错误提示:
arq.cpp: In function ‘int main()’:
arq.cpp:13:5: error: expected ‘(’ before ‘{’ token
     {
     ^
arq.cpp:14:9: error: ‘FLD’ was not declared in this scope
         FLD flp1_num
         ^

然后我尝试将__asm {}更改为__asm(),这给了我不同的错误:
arq.cpp: In function ‘int main()’:
arq.cpp:14:9: error: expected string-literal before ‘FLD’
         FLD flp1_num

我四处搜寻,发现几乎没有其他可行的选择,但是它们对我没有用。例如__asm__("fld flp1_num");asm("fld flp1_num");都给我说/tmp/cccDDfUP.o:arq.cpp:(.text+0x32): undefined reference to flp1_num的错误。

如何解决此错误?

最佳答案

正如其他人所说,您正在寻找他们的编译器的Microsoft文档,该文档的内联汇编形式与GCC使用的内联汇编形式非常不同。实际上,it is a substantially less powerful form尽管在很多方面确实具有节省的优势,但易于使用。

您将需要查阅有关nut内联Gnu内联汇编语法的文档。为了使介绍更加平稳,这里有一个很好的教程here,我特别喜欢David Wohlferd的答案here。尽管涉及到一个无关紧要的问题,但是如果您只是跟随他的解释,他会很好地介绍内联汇编的基础知识。

无论如何,针对您的特定问题。几个直接的问题:

  • 该代码很可能不会执行您认为的操作。您的代码实际执行的操作是将pi添加到flp2_num中,然后将结果放入flp_rslt1中。它对flp1_num没有任何作用。

    如果我不得不猜测,我可以想象您想一起添加flp1_num,pi和flp2_num,然后将结果返回到flp_rslt1。 (但是可能不是;它还不是很清楚,因为您没有任何说明意图的注释,也没有描述性的函数名称。)
  • 您的代码也被破坏,因为它没有正确清理浮点堆栈。您有两个“加载”指令,但没有弹出指令!压入/加载到浮点堆栈上的所有内容都必须弹出/卸载,否则会导致浮点堆栈不平衡,从而导致严重问题。

  • 因此,在MSVC语法中,您的代码应该看起来类似于以下内容(为了方便和清楚起见,将其打包为一个函数):
    float SumPlusPi(float flp1_num, float flp2_num)
    {
        float flp_rslt1;
        __asm
        {
           fldpi                       ; load the constant PI onto the top of the FP stack
           fadd  DWORD PTR [flp2_num]  ; add flp2_num to PI, and leave the result on the top of the stack
           fadd  DWORD PTR [flp1_num]  ; add flp1_num to the top of the stack, again leaving the result there
           fstp  DWORD PTR [flp_rslt1] ; pop the top of the stack into flp_rslt1
        }
        return flp_rslt1;
    }
    

    我只按了一次(fldpi),所以只弹出了一次(fstp)。对于增加的内容,我使用了适用于内存操作数的fadd形式;这将导致该值隐式加载到堆栈上,但否则似乎是作为一条指令执行的。但是,您可以采用多种不同的方式编写此代码。重要的是要在 push 次数与弹出次数之间取得平衡。有些指令会显式弹出(fstp),还有其他指令会先执行操作然后弹出(例如faddp)。按照某些顺序的不同指令组合很可能比其他指令更优化,但是上面的代码确实有效。

    这是翻译成GAS语法的等效代码:
    float SumPlusPi(float flp1_num, float flp2_num)
    {
        float flp_rslt1;
        __asm__("fldpi        \n\t"
                "faddl %[two] \n\t"
                "faddl %[one]"
               : [result] "=t" (flp_rslt1)   // tell compiler result is left at the top of the floating-point stack,
                                             //  making an explicit pop unnecessary
               : [one]    "m" (flp1_num),    // input operand from memory (inefficient)
                 [two]    "m" (flp2_num));   // input operand from memory (inefficient)
        return flp_rslt1;
    }
    

    尽管这可行,但它也不是最优的,因为它没有利用GAS内联汇编语法的高级功能,特别是无法使用已经加载到浮点堆栈上的值作为输入的功能。

    不过,最重要的是,不要错过here(也是David Wohlferd的作品)!这是内联汇编的真正毫无意义的用法。 编译器将生成更好的代码,并且您的工作量将大大减少。 因此,更喜欢这样编写上述函数:
    #include <cmath>    // for M_PI constant
    
    float SumPlusPi(float flp1_num, float flp2_num)
    {
        return (flp1_num + flp2_num + static_cast<float>(M_PI));
    }
    

    请注意,如果您实际上想要实现与我之前设想的逻辑不同的逻辑,那么更改此代码以执行您想要的事情是微不足道的。

    如果您不相信我所产生的代码与内联汇编代码一样好(如果不是更好),这是GCC 6.2为上述功能生成的确切目标代码(Clang发出相同的代码):
    fld     DWORD PTR [flp2_num]  ; load flp2_num onto top of FPU stack
    fadd    DWORD PTR [flp1_num]  ; add flp1_num to value at top of FPU stack
    fadd    DWORD PTR [M_PI]      ; add constant M_PI to value at top of FPU stack
    ret                           ; return, with result at top of FPU stack
    

    像GCC一样,使用fldpi与从常量中加载值并没有速度上的胜利。如果有的话,强制使用此指令实际上是一种悲观,因为这意味着您的代码永远无法利用SSE / SSE2指令,该指令允许比旧的x87 FPU更有效地操纵浮点值。为上面的C代码启用SSE / SSE2就像抛出编译器开关一样简单(或指定一个支持它的目标体系结构,这将隐式地启用它)。这将为您提供以下内容:
    sub       esp, 4                      ; reserve space on the stack    
    movss     xmm0, DWORD PTR [M_PI]      ; load M_PI constant
    addss     xmm0, DWORD PTR [flp2_num]  ; add flp2_num
    addss     xmm0, DWORD PTR [flp1_num]  ; add flp1_num
    movss     DWORD PTR [esp], xmm0       ; store result in temporary space on stack
    fld       DWORD PTR [esp]             ; load result from stack to top of FPU stack
    add       esp, 4                      ; clean up stack space
    ret                                   ; return, with result at top of FPU stack
    

    关于c++ - Cygwin:使用asm标签编译cpp文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41207479/

    相关文章:

    c++ - 微软计算器使用的库

    c++ - CUDA - 没有 block ,只有未定义维度的线程

    c++ - MFC:如何将自定义控件包含到 Visual Studio 的工具箱中

    c++ - OpenCV waitKey() 函数在 mac 上总是返回 255

    c++ - 如何在 mingw 上安装 nasm 以使用 pjsip 库为 Windows 应用程序构建 openh264 库

    assembly - Bochs GDT 段限制以十六进制形式左移 3 次,并添加 0xFFF。这是正常的吗?

    c++ - 在 C++ 中使用 std::regex 匹配精确的子字符串

    assembly - GCC 为 ARM 上的未对齐浮点访问生成程序集

    python - 尝试部署到 Heroku 时如何修复 "fatal error: Carbon/Carbon.h: No such file or directory"- (Django)

    c - 将 Autotools 用于具有平台特定源代码的项目