<div dir="ltr">I discovered a bug in which LLVM/Clang can miscompile C code that copies a union. This occurs on x86 when using x87 instructions for floating point operations.<div><div><br></div><div>The following program's assertion fails when compiled with "-O1 -mno-sse" (or when passing "-mcpu=blah" to llc). do_copy() gets compiled using x87 load+store instructions, which don't preserve exact byte values:</div>
<div><br></div><div><div>#include <assert.h></div><div>#include <stdarg.h></div><div>#include <stdio.h></div><div><br></div><div>union U {</div><div> double f;</div><div> unsigned long long i;</div><div>
};</div><div><br></div><div>__attribute__((noinline))</div><div>void do_copy(union U *dest, union U *src) {</div><div> *dest = *src;</div><div>}</div><div><br></div><div>int main() {</div><div> unsigned long long test_val = 0xfff0000000000001;</div>
<div> union U val, dest;</div><div> val.i = test_val;</div><div> do_copy(&dest, &val);</div><div> printf("%llx\n", dest.i);</div><div> assert(dest.i == test_val);</div><div> return 0;</div><div>}</div>
</div><div><br></div><div><div>$ clang copy_union_bug.c -o copy_union_bug -O1 -mno-sse -m32</div><div>$ ./copy_union_bug</div><div>fff8000000000001</div><div>copy_union_bug: copy_union_bug.c:22: int main(): Assertion `dest.i == test_val' failed.</div>
</div><div><br></div><div><br></div><div>Initially I thought the bug was that Clang compiles the union type to "%union.U = type { double }".</div><div><br></div><div>However, I think it's really the optimiser that's at fault. InstCombine converts do_copy()'s llvm.memcpy call to a load+store of a double. llvm.memcpy is meant to copy bytes faithfully. llvm.memcpy takes i8* arguments, and it shouldn't really assume anything about the data it's copying based on the pointer types that its i8* arguments were bitcast from.</div>
<div><br></div><div>This would be a problem for Emscripten, where double loads/stores are compiled to operations on a Javascript typed array, which aren't required to preserve bit patterns of NaNs (<a href="https://www.khronos.org/registry/typedarray/specs/latest/#4">https://www.khronos.org/registry/typedarray/specs/latest/#4</a>) and which sometimes get compiled to x87 FP. This could be a problem for PNaCl depending on what guarantees PNaCl provides about double loads/stores (it's currently not well-defined -- <a href="https://code.google.com/p/nativeclient/issues/detail?id=3822">https://code.google.com/p/nativeclient/issues/detail?id=3822</a>).</div>
<div><br></div><div>Should I change InstCombine to not convert the llvm.memcpy to a double load+store?</div><div><br></div><div>Cheers,</div><div>Mark</div><div><br></div></div></div>