Cygwin:用asm标签编译cpp文件

时间:2016-12-18 10:33:43

标签: 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

如何解决此错误?

2 个答案:

答案 0 :(得分:2)

正如其他人所说,您正在查看Microsoft的编译器文档,这些文档的内联汇编形式与GCC使用的形式完全不同。实际上,it is a substantially less powerful form在很多方面都有,但它确实具有更容易学习使用的优点。

您需要查阅Gnu内联汇编语法的文档,可用here。对于更温和的介绍,有一个很好的教程here,我特别喜欢David Wohlferd的回答here。虽然这是一个不相关的问题,但如果你只是按照他的解释,他会对内联汇编的基础知识进行非常好的介绍。


无论如何,关于你的具体问题。几个直接的问题:

  1. 代码很可能不会按照您的想法执行。您的代码实际所做的是将pi添加到flp2_num,然后将该结果放入flp_rslt1。它与flp1_num没有任何关系。

    如果我不得不猜测,我想你想要将flp1_num,pi和flp2_num加在一起,然后在flp_rslt1中返回结果。 (但也许不是;它不是很清楚,因为你没有任何评论说明你的意图,也没有描述性的功能名称。)

  2. 您的代码也已损坏,因为它无法正确清理浮点堆栈。你有两个&#34; load&#34;说明,但没有弹出说明!您推送/加载到浮点堆栈的所有内容都必须弹出/卸载,否则会导致浮点堆栈失衡,从而导致出现重大问题。

  3. 因此,在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形式的内存操作数;这会导致值隐式加载到堆栈中,但看起来像是作为单个指令执行。但是,有很多不同的方法可以写出来。重要的是平衡推送次数和弹出次数。有明确弹出的指令(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内联汇编语法的高级功能,特别是能够将已经加载到浮点堆栈上的值作为输入使用。


    但最重要的是,不要错过the reasons why you should not use inline assembly(也是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
    

    使用fldpi与使用像GCC一样的常量加载值没有速度胜利。如果有的话,强制使用这条指令实际上是一种悲观,因为这意味着你的代码无法利用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
    

答案 1 :(得分:0)

请查看此文档:How to embed assembler code in gcc

使用gcc / g ++时,需要以不同的方式嵌入汇编代码:

int src = 1;
int dst;    
asm ("mov %1, %0\n\t"
     "add $1, %0"
      : "=r" (dst)
      : "r" (src));

cygwin是gcc的Windows端口。