6

我有一个关于编译器可能做的优化的问题。

下面的代码不言自明(这是一个例子):

typedef struct  test
{
  short i;
}               s_test;

int function1(char *bin)
{
  s_test foo;

  lock(gmutex);
  foo.i = *(int*)bin * 8;
  unlock(gmutex);

  sleep(5);
  //
  // Here anything can happen to *bin in another thread
  // an inline example here could be: *(volatile int *)bin = 42;
  //

  int b = foo.i + sizeof(char*);

  return (b > 1000);
}

编译器能否将最后几行替换为

return ((*(int*)bin * 8 + sizeof(char*)) > 1000);

在 gcc 4.4 中使用 -O2 或 -O3 似乎不是这种情况,但其他编译器和其他编译标志可能是这种情况吗?

4

3 回答 3

5

我不认为编译器会做这种优化。

unlock(gmutex)

这是函数,编译器不能假设 bin 指向的值会在解锁函数中改变。

例如,也许 bin 来自一个地球仪。所以 bin 的优化不能跨越函数调用。

于 2012-06-05T07:31:55.173 回答
4

您的示例不必要地复杂,因为您正在阅读bin与声明不同的类型。别名规则很复杂,char甚至很特殊,我不会对此发表评论。

假设您的声明将是int* bin(因此您不必强制转换指针)编译器将无权跨函数调用重新排序语句,它们形成所谓的序列点。*bin调用之前和之后的值unlock可能不同,所以它必须在调用之后加载值。

编辑:正如 slartibartfast 所指出的,对于这个参数,它unlock是(或包含)一个函数调用,而不仅仅是一个由编译器解析为一系列操作的宏。

于 2012-06-05T07:45:21.013 回答
1

正如我在直接回复中所说:您的互斥锁没有保护任何东西恕我直言。*bin 指向的 char 数组可以在 int 访问期间被修改,因此根据您的机器,您甚至无法获得关于您想要访问的内存的一致视图。回到您的问题:编译器不会将源代码转换为您设想的序列,但它很可能会生成机器语言,实际上它的行为方式与您的源代码一样。如果它能够检查函数 lock、unlock 和 sleep(这似乎是宏)并且可以推断出所涉及的内存位置没有副作用并且没有实现定义的调用例如 sleep() 的含义将呈现临时(“缓存”,尽管标准没有 t 使用这个术语)值无效,那么它有权产生一个指令序列,就像你给的那样。C(最高 C99)本质上是单线程的,编译器可以采用它想要的任何优化策略,只要代码表现得“好像”它会在理想的假设 C 机器上运行。Jens 提到的序列点不会影响单线程条件下的正确性:编译器可以在整个函数期间将 foo 保存在寄存器中,甚至可以将 foo.i 别名为 *bin 指向的内存位置,所以这段代码本质上是危险的(尽管我认为它不会在大多数编译器上表现出这种行为)。C(最高 C99)本质上是单线程的,编译器可以采用它想要的任何优化策略,只要代码表现得“好像”它会在理想的假设 C 机器上运行。Jens 提到的序列点不会影响单线程条件下的正确性:编译器可以在整个函数期间将 foo 保存在寄存器中,甚至可以将 foo.i 别名为 *bin 指向的内存位置,所以这段代码本质上是危险的(尽管我认为它不会在大多数编译器上表现出这种行为)。C(最高 C99)本质上是单线程的,编译器可以采用它想要的任何优化策略,只要代码表现得“好像”它会在理想的假设 C 机器上运行。Jens 提到的序列点不会影响单线程条件下的正确性:编译器可以在整个函数期间将 foo 保存在寄存器中,甚至可以将 foo.i 别名为 *bin 指向的内存位置,所以这段代码本质上是危险的(尽管我认为它不会在大多数编译器上表现出这种行为)。

于 2012-06-05T08:38:51.737 回答