X86 指令顺序会发生跨函数调用吗?

X86 指令顺序会发生跨函数调用吗?,x86,cpu-architecture,X86,Cpu Architecture,假设我有如下伪C代码: int x = 0; int y = 0; int __attribute__ ((noinline)) func1(void) { int prev = x; (1) x |= FLAG; (2) return prev; (3) } int main(void) { int tmp; ... y = 5; (4) compiler_mem_barrier(); func1(); compi

假设我有如下伪C代码:

int x = 0;
int y = 0;

int __attribute__ ((noinline)) func1(void)
{ 
  int prev = x;  (1)

   x |= FLAG;    (2)

   return prev;  (3)
}

int main(void)
{  
  int tmp;

   ...
   y = 5;   (4)
   compiler_mem_barrier();
   func1();
   compiler_mem_barrier();
   tmp = y;  (5)
   ...
}
假设这是一个单线程进程,所以我们不必担心 关于锁。假设代码在x86系统上运行。我们还假设编译器不进行任何重新排序

我知道x86系统只能对写/读指令进行重新排序 (读操作可以通过旧的写操作重新排序到不同的位置,但是 不适用于对同一位置的较旧写入)。但我不清楚 如果call/ret指令被认为是写/读指令 说明书下面是我的问题:

  • 在x86系统上,“调用”是否被视为写入指令?我假设是这样,因为调用会将地址推送到堆栈中。但我没有找到官方文件正式这么说。所以请帮忙确认一下

  • 出于同样的原因,“ret”是否被视为读取指令(因为它从堆栈中弹出地址)

  • 实际上,“ret”指令可以在函数中重新排序。例如,在下面的ASM代码中,(3)可以在(2)之前执行吗?这对我来说没有意义,但“ret”不是序列化指令。我在英特尔手册中没有找到任何地方说“ret”不能重新排序

  • 在上面的代码中,(1)可以在(4)之前执行吗?据推测,读指令(1)可以在写指令(4)之前重新排序。“call”指令可能有一个“jmp”部分,但执行过程是推测性的。。。。所以我觉得这是可能的,但我希望更熟悉这个问题的人能够证实这一点

  • 在上面的代码中,(5)可以在(2)之前执行吗?如果“ret”被认为是一条读指令,那么我假设它不会发生。但我还是希望有人能证实这一点

  • 如果需要func1()的汇编代码,它应该类似于:

    mov    %gs:0x24,%eax          (1)                                                                                                                                                                                                
    orl    $0x8,%gs:0x24          (2)                                                                                                                                                                                                
    retq                          (3)
    

    请帮忙。谢谢

    无序执行可以对任何东西重新排序,但它保留了代码按程序顺序执行的假象。OoOE的基本规则是不要破坏单线程程序。硬件跟踪依赖关系,因此指令可以在输入和执行单元准备就绪后立即执行,但保留了一切都按程序顺序发生的假象


    您似乎混淆了单个核心上的OoOE与加载/存储对其他核心全局可见的顺序。()

    如果有一个线程观察另一个线程在另一个内核上运行的堆栈内存,那么是的,
    call
    (推送返回地址)生成的存储将与其他存储一起订购

    但是,在运行此代码的线程中无序执行实际上可以在存储因缓存未命中而延迟时或在执行长依赖链时执行
    调用
    ret
    指令。
    多个缓存未命中可以同时执行。内存顺序缓冲区只需确保在之前的存储之后,后面的存储才真正成为全局可见的,以保留x86的内存顺序语义


    如果您有关于硬件重新排序的特定问题,您可能应该发布asm代码,而不是C代码,因为在为x86这样的强顺序目标编译时,这一点不会改变

    另请参见(一个Java问题,但我的答案不是Java特有的)


    re:您的编辑

    这个答案已经假设您的函数是
    noinline
    ,并且您所说的ASM看起来像您的C,而不是编译器实际从您的代码生成的东西

    mov    %gs:0x24,%eax          (1)                                                                                                                                                                                                
    orl    $0x8,%gs:0x24          (2)                                                                                                                                                                                                
    retq                          (3)
    

    因此
    x
    实际上在线程本地存储中,而不是一个普通的全局
    intx
    。然而,这对于无序执行实际上并不重要;带有
    %gs
    段重写的加载仍然是加载。

    问问自己:编译器可以内联函数吗?假设函数没有内联。如果函数是内联的,我知道会发生什么。当然,函数是编译器重新排序的障碍。但是对于处理器afaik来说,
    call
    ret
    都没有强制序列化。请参阅8.3序列化说明。只要重新排序不改变应用程序的顺序(即,假设规则),编译器就可以自由地重新排序和重新排列,以优化代码。请注意,编译器执行的重新排序与处理器本身执行的无序执行非常不同。您的实际问题是什么?另请参见:,以及其他可通过搜索功能查找的问题。