【问题标题】:LLVM-clang compiler optimizer rearranges code in a very weird way, what to do?LLVM-clang 编译器优化器以一种非常奇怪的方式重新排列代码,该怎么办?
【发布时间】:2014-10-11 10:19:11
【问题描述】:

我有以下代码

renderer_opengl *oldr = (renderer_opengl*)enabler->renderer;
renderer *newr = new renderer;

void **vtable_old = ((void ***)oldr)[0];
void **vtable_new = ((void ***)newr)[0];

...

void *draw_new             = vtable_new[IDX_draw];
void *reshape_gl_new       = vtable_new[IDX_reshape_gl];
void *update_tile_new      = vtable_new[IDX_update_tile];    

// out << draw_new << std::endl;

p.verifyAccess(vtable_new, sizeof(void*)*32, true);
memcpy(vtable_new, vtable_old, sizeof(void*)*32);

out << draw_new << std::endl;

vtable_new[IDX_draw] = draw_new;
...

编译

Apple LLVM version 6.0 (clang-600.0.51) (based on LLVM 3.5svn)

我在这里做什么并不重要,但问题是编译器重新排列代码并将分配分配给draw_new 之后 memcpy,以便在输出流中我看到地址来自vtable_old 而不是 vtable_new!这发生在 -O3 甚至 -O2 上。如果我取消注释第一个输出,一切都会恢复正常。

这是什么 - 预期的行为,clang 中的错误还是我遗漏了什么?如何解决?

编辑

volatile 添加到vtable_new 声明

void ** volatile vtable_new = ((void ***)newr)[0];

帮助。 -fno-strict-aliasingasm volatile ("" : : : "memory") 屏障没有。我还是不明白编译器在这里做了什么。

【问题讨论】:

  • 要调试奇怪的问题,您确实需要发布 MCVE,否则我们只是猜测。
  • 以及别名冲突,写入vtable_new会在很多方面导致UB(你似乎试图摆弄一个对象的vtable)
  • @MattMcNabb 是的,这就是我必须做的(修改 vtable)。

标签: c++ llvm llvm-clang


【解决方案1】:

正如其他人所提到的,我认为编译器正在利用严格的别名规则。尝试替换:

void **vtable_old = ((void ***)oldr)[0];
void **vtable_new = ((void ***)newr)[0];

与:

void **vtable_old;
void **vtable_new;

memcpy( &vtable_old, oldr, sizeof(vtable_old));
memcpy( &vtable_new, newr, sizeof(vtable_new));

【讨论】:

  • 没有帮助。另外我怀疑它与严格的别名规则有关,原因有两个。首先,-fno-strict-aliasing 没有帮助。其次,如果我使用一个指针来修改数据,而另一个指向相同数据的指针来访问它,我认为严格的别名规则会导致问题,因此编译器不明白使用一个指针可能会影响另一个指针并且不会重新加载这个数据。但是这里vtable_new只在所有地方使用。
  • LLVM IRC 频道上有人提到编译器“可以假设 [指向 vtable] 的指针在对象构建后永远不会改变”。这多少有点道理。但是在使用这种 memcpy 构造时,它真的可以跟踪这些事情吗?
  • 有趣。这对我来说感觉像是一个编译器错误,但你正在做的事情不会让我感到惊讶,因为编译器有一些余地可以以非显而易见的方式处理。我想知道 gcc 的行为是否相同?也许你会从 clang 开发者那里得到一些支持或回应? clang.llvm.org/get_involved.html
  • GCC 工作正常。更有趣的是,我实际上在另一个地方使用了相同的代码,与另一个“新”类和一组略有不同的新 vmethods 一起使用。而且它从来没有引起问题。如果我使用 -S 进行编译,我可以清楚地看到在一种情况下分配正确放置在 memcpy 之前,在另一种情况下在 memcpy 之后。
  • 所以我想我会尝试发布到 cfe-def 邮件列表。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多