编译器如何知道不将关键部分内的代码移动到关键部分的外部?

Chr*_*her 1 c winapi

根据我的理解:

  • 编译器可以对我的代码执行任何重新排序.

  • 临界区内的代码不会移动到临界区的外部.

现在说我有以下代码:

printf("Hi");

EnterCriticalSection(&CriticalSection);
printf("Inside the critical section");
printf("Also inside the critical section");
LeaveCriticalSection(&CriticalSection);

printf("Bye");
Run Code Online (Sandbox Code Playgroud)

现在,编译器是否会实际查找函数EnterCriticalSection(),LeaveCriticalSection()而不是将它们内部的代码移到外面?

Die*_*Epp 6

编译器可以对我的代码执行任何重新排序.

这是不正确的.编译器是有限的.

临界区内的代码不会移动到临界区的外部.

这也是不正确的,具体取决于关键部分内/外的代码.

优化和约束

编译器对函数中的每段代码都有一堆约束.这些约束可能是输入和输出(除非先执行X,否则不能执行Y)或更常见的语句,如"这会影响某处的内存内容".编译代码时,编译器将保留这些约束.如果编译器不知道函数是什么,它将使用最重的约束集.

通常,这意味着编译器不会翻转两个函数调用的顺序1.

f(); // Maybe affects memory somewhere.
g(); // Maybe affects memory somewhere.
Run Code Online (Sandbox Code Playgroud)

这也意味着通常,如果访问内存,那些内存访问必须相对于函数调用进行排序.

void myfunc(X *ptr) {
    my_lock(ptr->mutex);   // Maybe affects memory somewhere.
    ptr->field++;          // Definitely affects memory at ptr->field.
    my_unlock(ptr->mutex); // Maybe affects memory somewhere.
}
Run Code Online (Sandbox Code Playgroud)

但是,它可以从关键部分重新排序:

int i = 5;
my_lock();   // Maybe affects memory somewhere.
i++;         // This is not "memory somewhere", this is my variable,
             // it's a register or on the stack and nobody else can
             // change it.
my_unlock(); // Maybe affects memory somewhere.
Run Code Online (Sandbox Code Playgroud)

所以它可以重新排序为:

int i = 6;
my_lock();
my_unlock();
Run Code Online (Sandbox Code Playgroud)

上面的代码可以重新排序,因为编译器具有关于允许修改谁的特殊知识i.如果你在其他地方有一些特殊的代码,它们会在堆栈中试图创建指针,i即使&i从未出现在你的程序中,你也会破坏与编译器的契约(也就是你的程序有未定义的行为).

我希望这能澄清事情.

脚注

1:您可以添加__declspec(noalias)更改此规则的注释,LTO /整个程序优化/过程间优化也可以改变.