小编yac*_*c45的帖子

指令顺序可以发生跨函数调用吗?

假设我有如下伪 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)
   ...
}
Run Code Online (Sandbox Code Playgroud)

假设这是一个单线程进程,所以我们不需要担心锁。假设代码在 x86 系统上运行。我们还假设编译器不进行任何重新排序。

据我了解,x86 系统只能重新排序写入/读取指令(读取可能会与较旧的写入重新排序到不同的位置,但不能与较旧的写入重新排序到同一位置)。但我不清楚 call/ret 指令是否被视为 WRITE/READ 指令。这是我的问题:

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

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

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

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

  5. 上面的代码中,(5)可以先于(2)执行吗?如果“ret”被认为是一个READ指令,那么我认为它不会发生。但我再次希望有人能证实这一点。

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

mov    %gs:0x24,%eax          (1) 
orl    $0x8,%gs:0x24 …
Run Code Online (Sandbox Code Playgroud)

x86 assembly cpu-architecture speculative-execution

6
推荐指数
1
解决办法
1128
查看次数

仅使用互斥锁来保护对齐的 uint64 访问是否有意义?

我在一些生产代码中看到过如下所示的 C++ 代码。假设 x 与 uint64 对齐并且代码在 X86 系统上运行。我的理解是,在 x86 系统上读取/写入对齐的 uint64 值是原子的。那么这里额外的互斥体的目的是什么?

当调用 GetX() 时持有互斥锁时,其他一些线程可以在 GetX() 之前/之后获取互斥锁并更改 X。所以我不认为额外的互斥体会让代码更安全。

uint64 GetX()
{
   Mutex l(&mutex); (1)
   return x;
}
Run Code Online (Sandbox Code Playgroud)

c++ x86 synchronization mutex locking

1
推荐指数
1
解决办法
103
查看次数