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

时间:2016-05-19 06:41:42

标签: gcc visual-c++ assembly x86 code-conversion

我正在尝试在 GCC 中使用以下代码。抛出错误(我猜是因为__asm)。为什么这种简单易用的格式在 GCC 中不起作用?此处提供Syntax of extended assembly。当在内联汇编中使用更多变量时,我感到困惑。有些人可以将以下程序转换为适当的形式,并在有变量使用的地方给出必要的解释。

    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
    }

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

2 个答案:

答案 0 :(得分:5)

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

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

您使用的代码是20年前Pentium II的documented by Intel变体。描述了该处理器的内容。不同之处在于您使用的代码不会执行该文档中描述的预热。我不相信这种机制在现代处理器和操作系统上会运行得很好(被警告)。

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

汇编程序模板使用input constraintsoutput constraints将数据传入和传出模板(与MSVC不同)。它还使用clobber list来指定可能已被更改的寄存器#39; t显示为输入或输出。默认情况下, GCC 内联汇编使用ATT syntax而不是 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;
}

答案 1 :(得分:1)

gcc,icc和visual c,它们对于内联汇编程序的语法都非常不同(这不是C标准的一部分)。 GCC有点复杂,但也更高效,因为你告诉编译器哪些寄存器用于什么,以及哪些寄存器被破坏(使用)。

https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html

https://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html

http://asm.sourceforge.net/articles/rmiyagi-inline-asm.txt

我的gcc汇编程序有点生疏(自从我使用它以来的几年),所以可能会有一些错误

int main(int argc, char *argv[])
{
  int time=0, subtime = 100;
  const float x = 5.0f;
  asm (
    "xorl    %%eax, %%eax        \n" /* make sure eax is a known value befeore cpuid */
    "cpuid                       \n"
    "rdtsc                       \n"
    "movl    %%eax, %[aSubtime]  \n"
    "cpuid                       \n"
    "rdtsc                       \n"
    "subl    %[aSubtime], %%eax  \n"
   // subtime should now represent the overhead cost of the
   // MOV and CPUID instructions
    "fld     %[ax]               \n"
    "fld     %[ax]               \n"
    "cpuid                       \n"   // Serialize execution
    "rdtsc                       \n"   // Read time stamp to EAX
    "movl    %%eax, %[atime]     \n"
    "fdivp                       \n"   // Perform division
    "cpuid                       \n"   // Serialize again for time-stamp read
    "rdtsc                       \n"
    "subl    %[atime], %%eax     \n"
//  "movl    %%eax, %2    \n"   Not needed, since we tell the compiler that asm exists with time in eax
      : "=a" (time) /* time is outputed in eax */
      : [aSubtime] "m" (subtime),
        [ax]       "m" (x),
        [atime]    "m" (time)
      : "ebx", "ecx", "edx"
    );
 /* FPU is currently left in a pushed state here */

  return 0;
}