vsk created this revision.

Given a load of a member variable from an instance method ('this->x'),
ubsan inserts a null check for 'this', and another null check for
'&this->x', before allowing the load to occur. Both of these checks are
redundant, because 'this' must have been null-checked before the method
is called.

Similarly, given a call to a method from another method bound to the
same instance ('this->foo()'), ubsan inserts a redundant null check for
'this'. There is also a redundant null check in the case where the
object pointer is a reference ('Ref.foo()').

This patch teaches ubsan to remove the redundant null checks identified
above. I'm not sure I've gone about this in the way suggested in PR27581,
and would appreciate any advice/corrections.

Testing: check-clang and check-ubsan. I also compiled X86FastISel.cpp
with -fsanitize=null using patched/unpatched clangs based on r293572.
Here are the number of null checks emitted in various setups:

| Setup          | # of null checks |
| unpatched, -O0 | 21767            |
| unpatched, -O3 | 11862            |
| patched, -O0   | 13178            |
| patched, -O3   | 5547             |


https://reviews.llvm.org/D29530

Files:
  lib/CodeGen/CGExpr.cpp
  lib/CodeGen/CGExprCXX.cpp
  test/CodeGenCXX/ubsan-suppress-null-checks.cpp


Index: test/CodeGenCXX/ubsan-suppress-null-checks.cpp
===================================================================
--- /dev/null
+++ test/CodeGenCXX/ubsan-suppress-null-checks.cpp
@@ -0,0 +1,38 @@
+// RUN: %clang_cc1 -std=c++11 -triple x86_64-apple-darwin10 -emit-llvm -o - %s 
-fsanitize=null | FileCheck %s
+
+struct A {
+  int foo;
+
+  // CHECK-LABEL: define linkonce_odr i32 @_ZN1A5func1Ev
+  int func1() {
+    // CHECK-NOT: ubsan_handler_type_mismatch
+    return foo;
+  }
+
+  // CHECK-LABEL: define linkonce_odr i32 @_ZN1A5func2Ev
+  int func2() {
+    // CHECK-NOT: ubsan_handler_type_mismatch
+    return func1();
+  }
+
+  // CHECK-LABEL: define linkonce_odr void @_ZN1A5func3Ev
+  void func3() {
+    // CHECK-NOT: ubsan_handler_type_mismatch
+    foo = 0;
+  }
+
+  // CHECK-LABEL: define linkonce_odr i32 @_ZN1A5func4ERS_
+  static int func4(A &a) {
+    // CHECK-NOT: ubsan_handler_type_mismatch
+    return a.func1();
+  }
+};
+
+// Force clang to emit IR for A's methods.
+void bar() {
+  A *a;
+  a->func1();
+  a->func2();
+  a->func3();
+  A::func4(*a);
+}
Index: lib/CodeGen/CGExprCXX.cpp
===================================================================
--- lib/CodeGen/CGExprCXX.cpp
+++ lib/CodeGen/CGExprCXX.cpp
@@ -290,10 +290,16 @@
   if (CE)
     CallLoc = CE->getExprLoc();
 
-  EmitTypeCheck(isa<CXXConstructorDecl>(CalleeDecl)
-                ? CodeGenFunction::TCK_ConstructorCall
-                : CodeGenFunction::TCK_MemberCall,
-                CallLoc, This.getPointer(), 
C.getRecordType(CalleeDecl->getParent()));
+  bool SkipNullCheck = false;
+  if (const auto *CMCE = dyn_cast<CXXMemberCallExpr>(CE)) {
+    Expr *IOA = CMCE->getImplicitObjectArgument();
+    SkipNullCheck = isa<CXXThisExpr>(IOA) || isa<DeclRefExpr>(IOA);
+  }
+  EmitTypeCheck(
+      isa<CXXConstructorDecl>(CalleeDecl) ? 
CodeGenFunction::TCK_ConstructorCall
+                                          : CodeGenFunction::TCK_MemberCall,
+      CallLoc, This.getPointer(), C.getRecordType(CalleeDecl->getParent()),
+      /*Alignment=*/CharUnits::Zero(), SkipNullCheck);
 
   // FIXME: Uses of 'MD' past this point need to be audited. We may need to use
   // 'CalleeDecl' instead.
Index: lib/CodeGen/CGExpr.cpp
===================================================================
--- lib/CodeGen/CGExpr.cpp
+++ lib/CodeGen/CGExpr.cpp
@@ -953,9 +953,13 @@
     LV = EmitArraySubscriptExpr(cast<ArraySubscriptExpr>(E), /*Accessed*/true);
   else
     LV = EmitLValue(E);
-  if (!isa<DeclRefExpr>(E) && !LV.isBitField() && LV.isSimple())
+  if (!isa<DeclRefExpr>(E) && !LV.isBitField() && LV.isSimple()) {
+    bool SkipNullCheck = false;
+    if (const auto *ME = dyn_cast<MemberExpr>(E))
+      SkipNullCheck = isa<CXXThisExpr>(ME->getBase());
     EmitTypeCheck(TCK, E->getExprLoc(), LV.getPointer(),
-                  E->getType(), LV.getAlignment());
+                  E->getType(), LV.getAlignment(), SkipNullCheck);
+  }
   return LV;
 }
 
@@ -3335,7 +3339,9 @@
     AlignmentSource AlignSource;
     Address Addr = EmitPointerWithAlignment(BaseExpr, &AlignSource);
     QualType PtrTy = BaseExpr->getType()->getPointeeType();
-    EmitTypeCheck(TCK_MemberAccess, E->getExprLoc(), Addr.getPointer(), PtrTy);
+    bool SkipNullCheck = isa<CXXThisExpr>(BaseExpr);
+    EmitTypeCheck(TCK_MemberAccess, E->getExprLoc(), Addr.getPointer(), PtrTy,
+                  /*Alignment=*/CharUnits::Zero(), SkipNullCheck);
     BaseLV = MakeAddrLValue(Addr, PtrTy, AlignSource);
   } else
     BaseLV = EmitCheckedLValue(BaseExpr, TCK_MemberAccess);


Index: test/CodeGenCXX/ubsan-suppress-null-checks.cpp
===================================================================
--- /dev/null
+++ test/CodeGenCXX/ubsan-suppress-null-checks.cpp
@@ -0,0 +1,38 @@
+// RUN: %clang_cc1 -std=c++11 -triple x86_64-apple-darwin10 -emit-llvm -o - %s -fsanitize=null | FileCheck %s
+
+struct A {
+  int foo;
+
+  // CHECK-LABEL: define linkonce_odr i32 @_ZN1A5func1Ev
+  int func1() {
+    // CHECK-NOT: ubsan_handler_type_mismatch
+    return foo;
+  }
+
+  // CHECK-LABEL: define linkonce_odr i32 @_ZN1A5func2Ev
+  int func2() {
+    // CHECK-NOT: ubsan_handler_type_mismatch
+    return func1();
+  }
+
+  // CHECK-LABEL: define linkonce_odr void @_ZN1A5func3Ev
+  void func3() {
+    // CHECK-NOT: ubsan_handler_type_mismatch
+    foo = 0;
+  }
+
+  // CHECK-LABEL: define linkonce_odr i32 @_ZN1A5func4ERS_
+  static int func4(A &a) {
+    // CHECK-NOT: ubsan_handler_type_mismatch
+    return a.func1();
+  }
+};
+
+// Force clang to emit IR for A's methods.
+void bar() {
+  A *a;
+  a->func1();
+  a->func2();
+  a->func3();
+  A::func4(*a);
+}
Index: lib/CodeGen/CGExprCXX.cpp
===================================================================
--- lib/CodeGen/CGExprCXX.cpp
+++ lib/CodeGen/CGExprCXX.cpp
@@ -290,10 +290,16 @@
   if (CE)
     CallLoc = CE->getExprLoc();
 
-  EmitTypeCheck(isa<CXXConstructorDecl>(CalleeDecl)
-                ? CodeGenFunction::TCK_ConstructorCall
-                : CodeGenFunction::TCK_MemberCall,
-                CallLoc, This.getPointer(), C.getRecordType(CalleeDecl->getParent()));
+  bool SkipNullCheck = false;
+  if (const auto *CMCE = dyn_cast<CXXMemberCallExpr>(CE)) {
+    Expr *IOA = CMCE->getImplicitObjectArgument();
+    SkipNullCheck = isa<CXXThisExpr>(IOA) || isa<DeclRefExpr>(IOA);
+  }
+  EmitTypeCheck(
+      isa<CXXConstructorDecl>(CalleeDecl) ? CodeGenFunction::TCK_ConstructorCall
+                                          : CodeGenFunction::TCK_MemberCall,
+      CallLoc, This.getPointer(), C.getRecordType(CalleeDecl->getParent()),
+      /*Alignment=*/CharUnits::Zero(), SkipNullCheck);
 
   // FIXME: Uses of 'MD' past this point need to be audited. We may need to use
   // 'CalleeDecl' instead.
Index: lib/CodeGen/CGExpr.cpp
===================================================================
--- lib/CodeGen/CGExpr.cpp
+++ lib/CodeGen/CGExpr.cpp
@@ -953,9 +953,13 @@
     LV = EmitArraySubscriptExpr(cast<ArraySubscriptExpr>(E), /*Accessed*/true);
   else
     LV = EmitLValue(E);
-  if (!isa<DeclRefExpr>(E) && !LV.isBitField() && LV.isSimple())
+  if (!isa<DeclRefExpr>(E) && !LV.isBitField() && LV.isSimple()) {
+    bool SkipNullCheck = false;
+    if (const auto *ME = dyn_cast<MemberExpr>(E))
+      SkipNullCheck = isa<CXXThisExpr>(ME->getBase());
     EmitTypeCheck(TCK, E->getExprLoc(), LV.getPointer(),
-                  E->getType(), LV.getAlignment());
+                  E->getType(), LV.getAlignment(), SkipNullCheck);
+  }
   return LV;
 }
 
@@ -3335,7 +3339,9 @@
     AlignmentSource AlignSource;
     Address Addr = EmitPointerWithAlignment(BaseExpr, &AlignSource);
     QualType PtrTy = BaseExpr->getType()->getPointeeType();
-    EmitTypeCheck(TCK_MemberAccess, E->getExprLoc(), Addr.getPointer(), PtrTy);
+    bool SkipNullCheck = isa<CXXThisExpr>(BaseExpr);
+    EmitTypeCheck(TCK_MemberAccess, E->getExprLoc(), Addr.getPointer(), PtrTy,
+                  /*Alignment=*/CharUnits::Zero(), SkipNullCheck);
     BaseLV = MakeAddrLValue(Addr, PtrTy, AlignSource);
   } else
     BaseLV = EmitCheckedLValue(BaseExpr, TCK_MemberAccess);
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to