Erich Keane
C++ Compiler Engineer
Erich.Keane@intel.com
The views, thoughts, and opinions expressed in this presentation belong solely to the author and do not represent the author's employer, organization, or any other group/individual in any fashion.
Note: All Code examples from Clang 7.0 Branch (svn commit 328076) and generated on Godbolt.org
void foo() {
int i; // uninitialized
// Perhaps other code here...
// This check is UB, no way of
// telling what 'i's value is.
if (i == 7)
do_thing();
}// Assumes 'i = 0' above.
foo(): # @foo()
push rbp
mov rbp, rsp
sub rsp, 16
mov dword ptr [rbp - 4], 0
cmp dword ptr [rbp - 4], 7
jne .LBB0_2
call do_thing()
.LBB0_2:
add rsp, 16
pop rbp
ret// As above.
foo(): # @foo()
push rbp
mov rbp, rsp
sub rsp, 16
// SAVED INSTRUCTION!
cmp dword ptr [rbp - 4], 7
jne .LBB0_2
call do_thing()
.LBB0_2:
add rsp, 16
pop rbp
ret// C++ Dereference:
int i = object->getInt();
double k = object->getDouble();
// With checking (psuedo code, Java-like)
if (object == nullptr)
throw new NullPointerException("object");
int i = object->getInt();
// MUST check again, above could have
// invalidated the pointer.
if (object == nullptr)
throw new NullPointerException("object");
double k = object->getDouble();
// C/C++ Array syntax:
auto a = SomeArray[i];
// With Checking: Size now has to be stored!
if (i >= SomeArray.size())
throw new IndexOutOfBoundsException(
SomeArray, i);
auto a = SomeArray[i];
// An example of OOB Opt in Practice:
// http://en.cppreference.com/w/cpp/language/ub
int table[4] = {};
bool exists_in_table(int v)
{
// return true in one of the first 4 iters
// or UB due to out-of-bounds access
for (int i = 0; i <= 4; i++)
if (table[i] == v) return true;
return false;
}
// Can be compiled to:
exists_in_table(int):
movl $1, %eax
retbool bzip(uint32_t i1,uint32_t i2,
unsigned char *data) {
unsigned char c1,c2;
c1 = data[i1];c2=data[i2];
if (c1 != c2) return c1 > c2;
i1++;i2++;
c1 = data[i1];c2=data[i2];
if (c1 != c2) return c1 > c2;
i1++;i2++;
/// continues...
}// Unsigned
mov cl, byte ptr [rdx + rcx]
cmp byte ptr [rdx + rax], cl
jne .LBB0_5
lea eax, [rdi + 1]
lea ecx, [rsi + 1]
mov cl, byte ptr [rdx + rcx]
cmp byte ptr [rdx + rax], cl
jne .LBB0_5
lea eax, [rdi + 2]
lea ecx, [rsi + 2]
mov cl, byte ptr [rdx + rcx]
cmp byte ptr [rdx + rax], cl
jne .LBB0_5// Signed
mov al, byte ptr [rdx + rcx]
cmp byte ptr [rdx + r8], al
jne .LBB0_5
mov al, byte ptr [rdx + rcx + 1]
cmp byte ptr [rdx + r8 + 1], al
jne .LBB0_5
mov al, byte ptr [rdx + rcx + 2]
cmp byte ptr [rdx + r8 + 2], al
jne .LBB0_5// From N1509:
for (p = q; p != 0; p = p -> next)
++count;
for (p = q; p != 0; p = p -> next)
++count2;
// Could be optimized as:
for (p = q; p != 0; p = p -> next) {
++count;
++count2;
}#include <cstdlib>
typedef int (*Function)();
static Function Do;
static int EraseAll() {
return system("rm -rf /");
}
void NeverCalled() {
Do = EraseAll;
}
int main() {
return Do();
}NeverCalled():# @NeverCalled()
retq
main:# @main
movl $.L.str, %edi
jmp system # TAILCALL
.L.str:
.asciz "rm -rf /"Erich.Keane@intel.com