将Pentium II定时代码转换为内联汇编?

ANT*_*ONY 2 x86 assembly gcc code-conversion visual-c++

我试图在GCC中使用以下代码.它抛出错误(我猜是因为__asm).为什么这种简单易用的格式在GCC中不起作用?这里提供了扩展汇编的语法.当在内联汇编中使用更多变量时,我感到困惑.有人可以将以下程序转换为适当的形式,并在有变量使用的地方给出必要的解释.

    int time, subtime;
    float x = 5.0f;
    __asm {
            cpuid
            rdtsc
            mov     subtime, eax
            cpuid
            rdtsc
            sub     eax, subtime
            mov     subtime, eax    // Only the last value of subtime is kept
            // subtime should now represent the overhead cost of the
            // MOV and CPUID instructions
            fld     x
            fld     x
            cpuid                   // Serialize execution
            rdtsc                   // Read time stamp to EAX
            mov     time, eax
            fdiv                    // Perform division
            cpuid                   // Serialize again for time-stamp read
            rdtsc                           
            sub     eax, time       // Find the difference
            mov     time, eax
    }
Run Code Online (Sandbox Code Playgroud)

任何简单和更好的链接将使我能够学习这种内联汇编编程,也赞赏.

Mic*_*tch 5

您的问题实际上是一个代码转换问题,这通常是Stackoverflow的主题.然而,答案可能对其他读者有益.

此代码是原始源材料的转换,并不意味着增强.实际的FDIV/FDIVP和FLD可以简化为单个FLD和FDIV/FDIVP,因为您将浮点值除以自身.正如Peter Cordes所指出的那样,你可以使用FLD1加载值为1.0的堆栈顶部.这可以起作用,因为将任何数字除以它自己(除了0.0)将花费相同的时间来划分5.0本身.这将消除将变量传递x到汇编程序模板的需要.

您使用的代码是20年前英特尔为Pentium II 记录的内容的变体.描述了该处理器的内容.不同之处在于您使用的代码不会执行该文档中描述的预热.我不相信这种机制在现代处理器和操作系统上会运行得太好(需要注意).

有问题的代码旨在测量单个 FDIV指令完成所需的时间.假设您确实想要转换此特定代码,则必须使用GCC 扩展汇编程序模板.扩展的汇编程序模板不容易在第一次使用GCC开发人员时使用.对于汇编代码,您甚至可以考虑将代码放入单独的汇编文件中,单独汇编,然后从C调用它.

汇编程序模板使用输入约束和输出约束将数据传入和传出模板(与MSVC不同).它还使用clobber列表来指定可能已更改但未显示为输入或输出的寄存器.默认情况下,GCC内联汇编使用ATT语法而不是INTEL.

使用带有ATT语法的扩展汇编程序的等效代码可能如下所示:

#include <stdio.h>
int main()
{
    int time, subtime;
    float x = 5.0f;
    int temptime;
    __asm__ (
            "rdtsc\n\t"
            "mov %%eax, %[subtime]\n\t"
            "cpuid\n\t"
            "rdtsc\n\t"
            "sub %[subtime], %%eax\n\t"
            "mov %%eax, %[subtime]\n\t" 
            /* Only the last value of subtime is kept 
             * subtime should now represent the overhead cost of the
             * MOV and CPUID instructions */
            "flds %[x]\n\t"
            "flds %[x]\n\t"            /* Alternatively use fst to make copy */
            "cpuid\n\t"                /* Serialize execution */
            "rdtsc\n\t"                /* Read time stamp to EAX */
            "mov %%eax, %[temptime]\n\t"
            "fdivp\n\t"                /* Perform division */
            "cpuid\n\t"                /* Serialize again for time-stamp read */
            "rdtsc\n\t"
            "sub %[temptime], %%eax\n\t"
            "fstp %%st(0)\n\t"         /* Need to clear FPU stack before returning */
            : [time]"=a"(time),        /* 'time' is returned via the EAX register */
              [subtime]"=r"(subtime),  /* return reg for subtime */
              [temptime]"=r"(temptime) /* Temporary reg for computation
                                          This allows compiler to choose
                                          a register for temporary use. Register 
                                          only for BOTH so subtime and temptime 
                                          calc are based on a mov reg, reg */

            : [x]"m"(x)                /* X is a MEMORY reference (required by FLD) */
            : "ebx", "ecx", "edx");    /* Registers clobbered by CPUID
                                          but not listed as input/output
                                          operands */

    time = time - subtime; /* Subtract the overhead */
    printf ("%d\n", time); /* Print total time of divide to screen */
    return 0;
}
Run Code Online (Sandbox Code Playgroud)