[cfe-dev] Clarification on undefined behaviour and/or how to get warned for these

Eloy Durán via cfe-dev cfe-dev at lists.llvm.org
Tue Oct 6 05:07:31 PDT 2015


Hi,

I’m fairly new to the world of C compiler edge cases and their undefined behaviour or what the C standards say about this particular one, but as it’s behaviour changed ‘recently’ I’d like to ask for some clarification nonetheless.

Since moving the build process of our iOS app to Xcode 7, an animation that was expected to run for a long time suddenly ran for a very short period. The issue turned out to be a change in how `HUGE_VAL` as an argument was converted to a `long`, which is the parameter type the function expected. The full case in question can be found here https://github.com/artsy/eigen/issues/838#issuecomment-145600551 <https://github.com/artsy/eigen/issues/838#issuecomment-145600551>, but I have reduced it to the following example for ease of understanding.

——

~/tmp » cat huge-vail.c
// This could use the HUGE_VAL macro instead of __builtin_huge_val, but I’m
// using the built-in one here, because that’s what the macro would use under
// the hood.

#include <math.h>
#include <assert.h>

void succeed(double value) { assert(value > 43); }
void fail(long value)      { assert(value > 43); }

int main(void)
{
  asm volatile ("movq $42, %%rdi" :);

  // When passing to a function with the correct parameter type, this works as expected.
  succeed(__builtin_huge_val());
  // When passing to a function that changes the type, the value in the $rdi register is passed as-is.
  fail(__builtin_huge_val());

  return 0;
}

——

~/tmp » clang -v
Apple LLVM version 7.0.0 (clang-700.0.72)
Target: x86_64-apple-darwin14.5.0
Thread model: posix
~/tmp » clang huge-vail.c -o huge-vail
~/tmp » ./huge-vail 
Assertion failed: (value > 43), function fail, file huge-vail.c, line 9.
fish: Job 1, './huge-vail ' terminated by signal SIGABRT (Abort)

When looking at the LLVM IR generated for this file, there’s a clear difference in what gets passed to the function. Specifically the undef part, which is considered a ‘undefined variable’:

define i32 @main() #0 {
  %1 = alloca i32, align 4
  store i32 0, i32* %1
  call void asm sideeffect "movq $$42, %rdi", "~{dirflag},~{fpsr},~{flags}"() #3, !srcloc !1
  call void @succeed(double 0x7FF0000000000000)
  call void @fail(i64 undef)
  ret i32 0
}

——

With an older clang this works as I would (naively) expect and how it has been working for the past 3 years:

~/tmp » /Applications/Xcode-6.4.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -v
Apple LLVM version 6.1.0 (clang-602.0.53) (based on LLVM 3.6.0svn)
Target: x86_64-apple-darwin14.5.0
Thread model: posix
~/tmp » /Applications/Xcode-6.4.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang huge-vail.c -o huge-vail
~/tmp » ./huge-vail 
~/tmp » echo $status
0

The LLVM IR for that is:

define i32 @main() #0 {
  %1 = alloca i32, align 4
  store i32 0, i32* %1
  call void asm sideeffect "movq $$42, %rdi", "~{dirflag},~{fpsr},~{flags}"() #4, !srcloc !1
  call void @succeed(double 0x7FF0000000000000)
  call void @fail(i64 9223372036854775807)
  ret i32 0
}

——

I understand that `HUGE_VAL` is actually a `double` and so the `double` to `long` conversion is probably where this behaviour comes from, but I wonder:
* What the reason is to mark it as undefined now vs the large value used previously?
* If there is a compiler flag that would have turned on checks for this undefined behaviour? I’ve tried the flags listed here in the manual, but none of those triggered a warning for me: http://clang.llvm.org/docs/UsersManual.html#controlling-code-generation <http://clang.llvm.org/docs/UsersManual.html#controlling-code-generation>

Simply pointing me to a doc that explains this case is perfectly fine too!

Kind regards,
Eloy Durán

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20151006/3ab59316/attachment.html>


More information about the cfe-dev mailing list