1

I have the following code

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;
...

Compiling with

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

Not important what I'm doing here, but the problem is that the compiler rearranges the code and places assignment to draw_new after memcpy, so that in out stream I see address from vtable_old instead of vtable_new! This happens with -O3 and even -O2. If I uncomment the first output, everything is back to normal.

What's this - expected behaviour, a bug in clang or am I missing something? How to fix it?

EDIT

Adding volatile to vtable_new declaration

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

helped. -fno-strict-aliasing and asm volatile ("" : : : "memory") barrier didn't. I still don't understand what the compiler is doing here.

pronvit
  • 4,169
  • 1
  • 18
  • 27
  • 2
    See here: http://stackoverflow.com/questions/98650/what-is-the-strict-aliasing-rule/99010#99010 – PaulMcKenzie Oct 11 '14 at 10:26
  • 1
    To debug weird problems you really need to post a MCVE otherwise we are just guessing. – M.M Oct 11 '14 at 11:37
  • 2
    as well as the aliasing violation , writing to `vtable_new` causes UB in many ways (you seem to be trying to fiddle with an object's vtable) – M.M Oct 11 '14 at 11:45
  • @MattMcNabb Yes, that's what I have to do (modifying vtable). – pronvit Oct 11 '14 at 11:46

1 Answers1

0

As others have mentioned, I think the compiler is taking advantage of the strict aliasing rule. Try replacing:

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

with:

void **vtable_old;
void **vtable_new;

memcpy( &vtable_old, oldr, sizeof(vtable_old));
memcpy( &vtable_new, newr, sizeof(vtable_new));
Michael Burr
  • 333,147
  • 50
  • 533
  • 760
  • 1
    Doesn't help. Also I doubt it's related to strict aliasing rule for two reasons. First, `-fno-strict-aliasing` doesn't help. And second, I thought strict aliasing rule is causing problems if I use one pointer to modify data, and another pointer to the same data to access it, so that compiler doesn't understand that using one pointer may affect another and doesn't reload this data. But here `vtable_new` only is used in all places. – pronvit Oct 12 '14 at 07:29
  • Someone on LLVM IRC channel mentioned that the compiler "can assume that the pointer [to vtable] never ever changes after the object is constructed". This somewhat makes sense. But can it really keep track of such things when using this memcpy construction? – pronvit Oct 12 '14 at 07:47
  • Interesting. This feels like a compiler bug to me, but you're doing things that wouldn't surprise me that the compiler has some leeway to handle in non-obvious ways. I wonder if gcc behaves the same? Maybe you would get some support or response from clang devs? http://clang.llvm.org/get_involved.html – Michael Burr Oct 12 '14 at 08:25
  • 1
    GCC works fine. What's more interesting is that I actually have the same code in one more place used with another "newr" class and slightly different set of new vmethods. And there it never caused problems. If I compile with -S I can clearly see in one case assignments correctly placed before memcpy and in another case after memcpy. – pronvit Oct 12 '14 at 09:27
  • 1
    So I think I'll try to post to cfe-def mailing list indeed. – pronvit Oct 12 '14 at 09:28