Hardware-assisted AddressSanitizer (llvm part).

Summary:
This is LLVM instrumentation for the new HWASan tool. It is basically
a stripped down copy of ASan at this point, w/o stack or global
support. Instrumenation adds a global constructor + runtime callbacks
for every load and store.

HWASan comes with its own IR attribute.

A brief design document can be found in
clang/docs/HardwareAssistedAddressSanitizerDesign.rst (submitted earlier).

Reviewers: kcc, pcc, alekseyshl

Subscribers: srhines, mehdi_amini, mgorny, javed.absar, eraman, llvm-commits, hiraditya

Differential Revision: https://reviews.llvm.org/D40932

llvm-svn: 320217
diff --git a/llvm/test/Bitcode/attributes.ll b/llvm/test/Bitcode/attributes.ll
index 18aa12c..8c74b3f 100644
--- a/llvm/test/Bitcode/attributes.ll
+++ b/llvm/test/Bitcode/attributes.ll
@@ -204,7 +204,7 @@
 ; CHECK: define void @f34()
 {
         call void @nobuiltin() nobuiltin
-; CHECK: call void @nobuiltin() #34
+; CHECK: call void @nobuiltin() #35
         ret void;
 }
 
@@ -339,6 +339,12 @@
   ret void
 }
 
+; CHECK: define void @f58() #34
+define void @f58() sanitize_hwaddress
+{
+        ret void;
+}
+
 ; CHECK: attributes #0 = { noreturn }
 ; CHECK: attributes #1 = { nounwind }
 ; CHECK: attributes #2 = { readnone }
@@ -373,4 +379,5 @@
 ; CHECK: attributes #31 = { allocsize(0,1) }
 ; CHECK: attributes #32 = { writeonly }
 ; CHECK: attributes #33 = { speculatable }
-; CHECK: attributes #34 = { nobuiltin }
+; CHECK: attributes #34 = { sanitize_hwaddress }
+; CHECK: attributes #35 = { nobuiltin }
diff --git a/llvm/test/Instrumentation/HWAddressSanitizer/atomic.ll b/llvm/test/Instrumentation/HWAddressSanitizer/atomic.ll
new file mode 100644
index 0000000..67ddac7
--- /dev/null
+++ b/llvm/test/Instrumentation/HWAddressSanitizer/atomic.ll
@@ -0,0 +1,26 @@
+; Test basic address sanitizer instrumentation.
+;
+; RUN: opt < %s -hwasan -S | FileCheck %s
+
+target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"
+target triple = "aarch64--linux-android"
+
+define void @atomicrmw(i64* %ptr) sanitize_hwaddress {
+; CHECK-LABEL: @atomicrmw(
+; CHECK: %[[A:[^ ]*]] = ptrtoint i64* %ptr to i64
+; CHECK: call void @__hwasan_store8(i64 %[[A]])
+
+entry:
+  %0 = atomicrmw add i64* %ptr, i64 1 seq_cst
+  ret void
+}
+
+define void @cmpxchg(i64* %ptr, i64 %compare_to, i64 %new_value) sanitize_hwaddress {
+; CHECK-LABEL: @cmpxchg(
+; CHECK: %[[A:[^ ]*]] = ptrtoint i64* %ptr to i64
+; CHECK: call void @__hwasan_store8(i64 %[[A]])
+
+entry:
+  %0 = cmpxchg i64* %ptr, i64 %compare_to, i64 %new_value seq_cst seq_cst
+  ret void
+}
diff --git a/llvm/test/Instrumentation/HWAddressSanitizer/basic.ll b/llvm/test/Instrumentation/HWAddressSanitizer/basic.ll
new file mode 100644
index 0000000..93129a5
--- /dev/null
+++ b/llvm/test/Instrumentation/HWAddressSanitizer/basic.ll
@@ -0,0 +1,190 @@
+; Test basic address sanitizer instrumentation.
+;
+; RUN: opt < %s -hwasan -S | FileCheck %s
+
+target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"
+target triple = "aarch64--linux-android"
+
+define i8 @test_load8(i8* %a) sanitize_hwaddress {
+; CHECK-LABEL: @test_load8(
+; CHECK: %[[A:[^ ]*]] = ptrtoint i8* %a to i64
+; CHECK: call void @__hwasan_load1(i64 %[[A]])
+; CHECK: %[[B:[^ ]*]] = load i8, i8* %a
+; CHECK: ret i8 %[[B]]
+
+entry:
+  %b = load i8, i8* %a, align 4
+  ret i8 %b
+}
+
+define i16 @test_load16(i16* %a) sanitize_hwaddress {
+; CHECK-LABEL: @test_load16(
+; CHECK: %[[A:[^ ]*]] = ptrtoint i16* %a to i64
+; CHECK: call void @__hwasan_load2(i64 %[[A]])
+; CHECK: %[[B:[^ ]*]] = load i16, i16* %a
+; CHECK: ret i16 %[[B]]
+
+entry:
+  %b = load i16, i16* %a, align 4
+  ret i16 %b
+}
+
+define i32 @test_load32(i32* %a) sanitize_hwaddress {
+; CHECK-LABEL: @test_load32(
+; CHECK: %[[A:[^ ]*]] = ptrtoint i32* %a to i64
+; CHECK: call void @__hwasan_load4(i64 %[[A]])
+; CHECK: %[[B:[^ ]*]] = load i32, i32* %a
+; CHECK: ret i32 %[[B]]
+
+entry:
+  %b = load i32, i32* %a, align 4
+  ret i32 %b
+}
+
+define i64 @test_load64(i64* %a) sanitize_hwaddress {
+; CHECK-LABEL: @test_load64(
+; CHECK: %[[A:[^ ]*]] = ptrtoint i64* %a to i64
+; CHECK: call void @__hwasan_load8(i64 %[[A]])
+; CHECK: %[[B:[^ ]*]] = load i64, i64* %a
+; CHECK: ret i64 %[[B]]
+
+entry:
+  %b = load i64, i64* %a, align 8
+  ret i64 %b
+}
+
+define i128 @test_load128(i128* %a) sanitize_hwaddress {
+; CHECK-LABEL: @test_load128(
+; CHECK: %[[A:[^ ]*]] = ptrtoint i128* %a to i64
+; CHECK: call void @__hwasan_load16(i64 %[[A]])
+; CHECK: %[[B:[^ ]*]] = load i128, i128* %a
+; CHECK: ret i128 %[[B]]
+
+entry:
+  %b = load i128, i128* %a, align 16
+  ret i128 %b
+}
+
+define i40 @test_load40(i40* %a) sanitize_hwaddress {
+; CHECK-LABEL: @test_load40(
+; CHECK: %[[A:[^ ]*]] = ptrtoint i40* %a to i64
+; CHECK: call void @__hwasan_load(i64 %[[A]], i64 5)
+; CHECK: %[[B:[^ ]*]] = load i40, i40* %a
+; CHECK: ret i40 %[[B]]
+
+entry:
+  %b = load i40, i40* %a, align 4
+  ret i40 %b
+}
+
+define void @test_store8(i8* %a, i8 %b) sanitize_hwaddress {
+; CHECK-LABEL: @test_store8(
+; CHECK: %[[A:[^ ]*]] = ptrtoint i8* %a to i64
+; CHECK: call void @__hwasan_store1(i64 %[[A]])
+; CHECK: store i8 %b, i8* %a
+; CHECK: ret void
+
+entry:
+  store i8 %b, i8* %a, align 4
+  ret void
+}
+
+define void @test_store16(i16* %a, i16 %b) sanitize_hwaddress {
+; CHECK-LABEL: @test_store16(
+; CHECK: %[[A:[^ ]*]] = ptrtoint i16* %a to i64
+; CHECK: call void @__hwasan_store2(i64 %[[A]])
+; CHECK: store i16 %b, i16* %a
+; CHECK: ret void
+
+entry:
+  store i16 %b, i16* %a, align 4
+  ret void
+}
+
+define void @test_store32(i32* %a, i32 %b) sanitize_hwaddress {
+; CHECK-LABEL: @test_store32(
+; CHECK: %[[A:[^ ]*]] = ptrtoint i32* %a to i64
+; CHECK: call void @__hwasan_store4(i64 %[[A]])
+; CHECK: store i32 %b, i32* %a
+; CHECK: ret void
+
+entry:
+  store i32 %b, i32* %a, align 4
+  ret void
+}
+
+define void @test_store64(i64* %a, i64 %b) sanitize_hwaddress {
+; CHECK-LABEL: @test_store64(
+; CHECK: %[[A:[^ ]*]] = ptrtoint i64* %a to i64
+; CHECK: call void @__hwasan_store8(i64 %[[A]])
+; CHECK: store i64 %b, i64* %a
+; CHECK: ret void
+
+entry:
+  store i64 %b, i64* %a, align 4
+  ret void
+}
+
+define void @test_store128(i128* %a, i128 %b) sanitize_hwaddress {
+; CHECK-LABEL: @test_store128(
+; CHECK: %[[A:[^ ]*]] = ptrtoint i128* %a to i64
+; CHECK: call void @__hwasan_store16(i64 %[[A]])
+; CHECK: store i128 %b, i128* %a
+; CHECK: ret void
+
+entry:
+  store i128 %b, i128* %a, align 4
+  ret void
+}
+
+define void @test_store40(i40* %a, i40 %b) sanitize_hwaddress {
+; CHECK-LABEL: @test_store40(
+; CHECK: %[[A:[^ ]*]] = ptrtoint i40* %a to i64
+; CHECK: call void @__hwasan_store(i64 %[[A]], i64 5)
+; CHECK: store i40 %b, i40* %a
+; CHECK: ret void
+
+entry:
+  store i40 %b, i40* %a, align 4
+  ret void
+}
+
+define i8 @test_load_noattr(i8* %a) {
+; CHECK-LABEL: @test_load_noattr(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: %[[B:[^ ]*]] = load i8, i8* %a
+; CHECK-NEXT: ret i8 %[[B]]
+
+entry:
+  %b = load i8, i8* %a, align 4
+  ret i8 %b
+}
+
+define i8 @test_load_notmyattr(i8* %a) sanitize_address {
+; CHECK-LABEL: @test_load_notmyattr(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: %[[B:[^ ]*]] = load i8, i8* %a
+; CHECK-NEXT: ret i8 %[[B]]
+
+entry:
+  %b = load i8, i8* %a, align 4
+  ret i8 %b
+}
+
+define i8 @test_load_addrspace(i8 addrspace(256)* %a) sanitize_hwaddress {
+; CHECK-LABEL: @test_load_addrspace(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: %[[B:[^ ]*]] = load i8, i8 addrspace(256)* %a
+; CHECK-NEXT: ret i8 %[[B]]
+
+entry:
+  %b = load i8, i8 addrspace(256)* %a, align 4
+  ret i8 %b
+}
+
+; CHECK: declare void @__hwasan_init()
+
+; CHECK:      define internal void @hwasan.module_ctor() {
+; CHECK-NEXT:   call void @__hwasan_init()
+; CHECK-NEXT:   ret void
+; CHECK-NEXT: }
diff --git a/llvm/test/Transforms/GVN/no_speculative_loads_with_asan.ll b/llvm/test/Transforms/GVN/no_speculative_loads_with_asan.ll
index a83d7b6..72e0b4e 100644
--- a/llvm/test/Transforms/GVN/no_speculative_loads_with_asan.ll
+++ b/llvm/test/Transforms/GVN/no_speculative_loads_with_asan.ll
@@ -53,3 +53,30 @@
 ; CHECK-NOT: %[[LOAD:[^ ]+]] = load i32
 ; CHECK: {{.*}} = phi
 
+
+define i32 @TestHWAsan() sanitize_hwaddress {
+  %1 = tail call noalias i8* @_Znam(i64 2)
+  %2 = getelementptr inbounds i8, i8* %1, i64 1
+  store i8 0, i8* %2, align 1
+  store i8 0, i8* %1, align 1
+  %3 = bitcast i8* %1 to i16*
+  %4 = load i16, i16* %3, align 4
+  %5 = icmp eq i16 %4, 0
+  br i1 %5, label %11, label %6
+
+; <label>:6                                       ; preds = %0
+  %7 = getelementptr inbounds i8, i8* %1, i64 2
+  %8 = bitcast i8* %7 to i16*
+  %9 = load i16, i16* %8, align 2
+  %10 = sext i16 %9 to i32
+  br label %11
+
+; <label>:11                                      ; preds = %0, %6
+  %12 = phi i32 [ %10, %6 ], [ 0, %0 ]
+  ret i32 %12
+}
+
+; CHECK-LABEL: @TestHWAsan
+; CHECK-NOT: %[[LOAD:[^ ]+]] = load i32
+; CHECK: {{.*}} = phi
+
diff --git a/llvm/test/Transforms/Inline/attributes.ll b/llvm/test/Transforms/Inline/attributes.ll
index 1cc64b7..0df3cfa 100644
--- a/llvm/test/Transforms/Inline/attributes.ll
+++ b/llvm/test/Transforms/Inline/attributes.ll
@@ -10,6 +10,10 @@
   ret i32 %i
 }
 
+define i32 @sanitize_hwaddress_callee(i32 %i) sanitize_hwaddress {
+  ret i32 %i
+}
+
 define i32 @sanitize_thread_callee(i32 %i) sanitize_thread {
   ret i32 %i
 }
@@ -30,6 +34,10 @@
   ret i32 %i
 }
 
+define i32 @alwaysinline_sanitize_hwaddress_callee(i32 %i) alwaysinline sanitize_hwaddress {
+  ret i32 %i
+}
+
 define i32 @alwaysinline_sanitize_thread_callee(i32 %i) alwaysinline sanitize_thread {
   ret i32 %i
 }
@@ -59,6 +67,17 @@
 ; CHECK-NEXT: ret i32
 }
 
+define i32 @test_no_sanitize_hwaddress(i32 %arg) {
+  %x1 = call i32 @noattr_callee(i32 %arg)
+  %x2 = call i32 @sanitize_hwaddress_callee(i32 %x1)
+  %x3 = call i32 @alwaysinline_callee(i32 %x2)
+  %x4 = call i32 @alwaysinline_sanitize_hwaddress_callee(i32 %x3)
+  ret i32 %x4
+; CHECK-LABEL: @test_no_sanitize_hwaddress(
+; CHECK-NEXT: @sanitize_hwaddress_callee
+; CHECK-NEXT: ret i32
+}
+
 define i32 @test_no_sanitize_memory(i32 %arg) {
   %x1 = call i32 @noattr_callee(i32 %arg)
   %x2 = call i32 @sanitize_memory_callee(i32 %x1)
@@ -98,6 +117,17 @@
 ; CHECK-NEXT: ret i32
 }
 
+define i32 @test_sanitize_hwaddress(i32 %arg) sanitize_hwaddress {
+  %x1 = call i32 @noattr_callee(i32 %arg)
+  %x2 = call i32 @sanitize_hwaddress_callee(i32 %x1)
+  %x3 = call i32 @alwaysinline_callee(i32 %x2)
+  %x4 = call i32 @alwaysinline_sanitize_hwaddress_callee(i32 %x3)
+  ret i32 %x4
+; CHECK-LABEL: @test_sanitize_hwaddress(
+; CHECK-NEXT: @noattr_callee
+; CHECK-NEXT: ret i32
+}
+
 define i32 @test_sanitize_memory(i32 %arg) sanitize_memory {
   %x1 = call i32 @noattr_callee(i32 %arg)
   %x2 = call i32 @sanitize_memory_callee(i32 %x1)
diff --git a/llvm/test/Transforms/InstCombine/lifetime-asan.ll b/llvm/test/Transforms/InstCombine/lifetime-asan.ll
index 7fdc1fc..e7b996d 100644
--- a/llvm/test/Transforms/InstCombine/lifetime-asan.ll
+++ b/llvm/test/Transforms/InstCombine/lifetime-asan.ll
@@ -19,6 +19,20 @@
   ret void
 }
 
+define void @hwasan() sanitize_hwaddress {
+entry:
+  ; CHECK-LABEL: @hwasan(
+  %text = alloca i8, align 1
+
+  call void @llvm.lifetime.start.p0i8(i64 1, i8* %text)
+  call void @llvm.lifetime.end.p0i8(i64 1, i8* %text)
+  ; CHECK: call void @llvm.lifetime.start
+  ; CHECK-NEXT: call void @llvm.lifetime.end
+
+  call void @foo(i8* %text) ; Keep alloca alive
+
+  ret void
+}
 
 define void @no_asan() {
 entry:
diff --git a/llvm/test/Transforms/NewGVN/no_speculative_loads_with_asan.ll b/llvm/test/Transforms/NewGVN/no_speculative_loads_with_asan.ll
index a83d7b6..e1e5e4a 100644
--- a/llvm/test/Transforms/NewGVN/no_speculative_loads_with_asan.ll
+++ b/llvm/test/Transforms/NewGVN/no_speculative_loads_with_asan.ll
@@ -53,3 +53,29 @@
 ; CHECK-NOT: %[[LOAD:[^ ]+]] = load i32
 ; CHECK: {{.*}} = phi
 
+define i32 @TestHWAsan() sanitize_hwaddress {
+  %1 = tail call noalias i8* @_Znam(i64 2)
+  %2 = getelementptr inbounds i8, i8* %1, i64 1
+  store i8 0, i8* %2, align 1
+  store i8 0, i8* %1, align 1
+  %3 = bitcast i8* %1 to i16*
+  %4 = load i16, i16* %3, align 4
+  %5 = icmp eq i16 %4, 0
+  br i1 %5, label %11, label %6
+
+; <label>:6                                       ; preds = %0
+  %7 = getelementptr inbounds i8, i8* %1, i64 2
+  %8 = bitcast i8* %7 to i16*
+  %9 = load i16, i16* %8, align 2
+  %10 = sext i16 %9 to i32
+  br label %11
+
+; <label>:11                                      ; preds = %0, %6
+  %12 = phi i32 [ %10, %6 ], [ 0, %0 ]
+  ret i32 %12
+}
+
+; CHECK-LABEL: @TestHWAsan
+; CHECK-NOT: %[[LOAD:[^ ]+]] = load i32
+; CHECK: {{.*}} = phi
+
diff --git a/llvm/test/Transforms/SimplifyCFG/no_speculative_loads_with_asan.ll b/llvm/test/Transforms/SimplifyCFG/no_speculative_loads_with_asan.ll
index 063bde8..dfd0d71 100644
--- a/llvm/test/Transforms/SimplifyCFG/no_speculative_loads_with_asan.ll
+++ b/llvm/test/Transforms/SimplifyCFG/no_speculative_loads_with_asan.ll
@@ -38,3 +38,22 @@
 ; CHECK: br label
 ; CHECK: ret i32
 }
+
+define i32 @TestHWAsan(i32 %cond) nounwind readonly uwtable sanitize_hwaddress {
+entry:
+  %tobool = icmp eq i32 %cond, 0
+  br i1 %tobool, label %return, label %if.then
+
+if.then:                                          ; preds = %entry
+  %0 = load i32, i32* @g, align 4
+  br label %return
+
+return:                                           ; preds = %entry, %if.then
+  %retval = phi i32 [ %0, %if.then ], [ 0, %entry ]
+  ret i32 %retval
+; CHECK-LABEL: @TestHWAsan
+; CHECK: br i1
+; CHECK: load i32, i32* @g
+; CHECK: br label
+; CHECK: ret i32
+}