May 20, 2018

[C++][compiler][UB] Undefined Behavior and Compiler Optimizations


C++Now 2018: John Regehr “Closing Keynote: Undefined Behavior and Compiler Optimizations”

Note:

  • Inside function stack, make sure always, always init auto variable.
  • UB facilitates optimizations by allowing safety checks to be decoupled from unsafe operations.
  • All-or-nothing semantics for UB is not appropriate in a compiler IR.
  • Compiler must never make code less defined.
  • Deferred UB can work.
    • A tool we can use to justify desirable compiler optimizations
  • Flat memory model is easy to reason.
  • We need two types of pointers:
    • logical pointer - originate at allocations
      i.e malloc/new
      It use dataflow-based provenance.
      If originating at different allocations never alias.
      i.e pointer points to the location that is pass the complete object and compare that location to another complete object is unspecified.
    • Physical pointers - originate at casts from integer to pointer
Reference:
Pointers are literal types. They can be constexpr under certain conditions:

[expr.const]
... [a pointer is constexpr if] it contains the address of an object with static storage duration, the address past the end of such an object (5.7), the address of a function, or a null pointer value.
  1. static storage duration
  2. the address past the end of such an object 
  3. the address of a function
  4. null pointer value
int x;

int main()
{
    constexpr int *ptr = &x; // Compiles.

    // Doesn't compile: `error: '& foo' is not a constant expression`
    // int foo;
    // constexpr int *bar = &foo;
}

  • Fun consequences of control-flow-based pointer provenance:
    • assume p is a logical pointer
    • p and (int *)(int)p are not necessarily the same...
LLVM wants to optimize (int *)(int)p to p
WRONG! Provenance information can be lost.
Both LLVM/GCC can miscompile code like above...

  • It's only valid to compare pointeres with overlapping liveness
    • Potentially illegal to trim liveness ranges
  • Moving allocations around should be careful!

char *p = malloc(4);
char *q = malloc(4);

//valid
if (p == q) {...;}

free(p);
char *p = malloc(4);
free(p);

char *q = malloc(4);

// UB
if (p == q) {...}

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.