]> granicus.if.org Git - clang/commitdiff
[objc] Don't require null-check and don't emit memset when result is ignored for...
authorKuba Mracek <mracek@apple.com>
Fri, 30 Jun 2017 16:28:15 +0000 (16:28 +0000)
committerKuba Mracek <mracek@apple.com>
Fri, 30 Jun 2017 16:28:15 +0000 (16:28 +0000)
This fixes an issue with the emission of lifetime markers for struct-returning Obj-C msgSend calls. When the result of a struct-returning call is ignored, the temporary storage is only marked with lifetime markers in one of the two branches of the nil-receiver-check. The check is, however, not required when the result is unused. If we still need to emit the check (due to consumer arguments), let's not emit the memset to zero out the result if it's unused. This fixes a use-after-scope false positive with AddressSanitizer.

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

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@306837 91177308-0d34-0410-b5e6-96231b3b80d8

lib/CodeGen/CGObjCMac.cpp
test/CodeGenObjC/stret-1.m
test/CodeGenObjC/stret-lifetime.m [new file with mode: 0644]

index 224d2d6606a2faf80aa64ac0c1b8cb8c35c11dd4..98435fefbd2eafec2158447c991fd0e8a47db57f 100644 (file)
@@ -1678,7 +1678,10 @@ struct NullReturnState {
 
   /// Complete the null-return operation.  It is valid to call this
   /// regardless of whether 'init' has been called.
-  RValue complete(CodeGenFunction &CGF, RValue result, QualType resultType,
+  RValue complete(CodeGenFunction &CGF,
+                  ReturnValueSlot returnSlot,
+                  RValue result,
+                  QualType resultType,
                   const CallArgList &CallArgs,
                   const ObjCMethodDecl *Method) {
     // If we never had to do a null-check, just use the raw result.
@@ -1745,7 +1748,8 @@ struct NullReturnState {
     // memory or (2) agg values in registers.
     if (result.isAggregate()) {
       assert(result.isAggregate() && "null init of non-aggregate result?");
-      CGF.EmitNullInitialization(result.getAggregateAddress(), resultType);
+      if (!returnSlot.isUnused())
+        CGF.EmitNullInitialization(result.getAggregateAddress(), resultType);
       if (contBB) CGF.EmitBlock(contBB);
       return result;
     }
@@ -2117,11 +2121,11 @@ CGObjCCommonMac::EmitMessageSend(CodeGen::CodeGenFunction &CGF,
     }
   }
 
-  NullReturnState nullReturn;
+  bool RequiresNullCheck = false;
 
   llvm::Constant *Fn = nullptr;
   if (CGM.ReturnSlotInterferesWithArgs(MSI.CallInfo)) {
-    if (ReceiverCanBeNull) nullReturn.init(CGF, Arg0);
+    if (ReceiverCanBeNull) RequiresNullCheck = true;
     Fn = (ObjCABI == 2) ?  ObjCTypes.getSendStretFn2(IsSuper)
       : ObjCTypes.getSendStretFn(IsSuper);
   } else if (CGM.ReturnTypeUsesFPRet(ResultType)) {
@@ -2134,23 +2138,30 @@ CGObjCCommonMac::EmitMessageSend(CodeGen::CodeGenFunction &CGF,
     // arm64 uses objc_msgSend for stret methods and yet null receiver check
     // must be made for it.
     if (ReceiverCanBeNull && CGM.ReturnTypeUsesSRet(MSI.CallInfo))
-      nullReturn.init(CGF, Arg0);
+      RequiresNullCheck = true;
     Fn = (ObjCABI == 2) ? ObjCTypes.getSendFn2(IsSuper)
       : ObjCTypes.getSendFn(IsSuper);
   }
 
+  // We don't need to emit a null check to zero out an indirect result if the
+  // result is ignored.
+  if (Return.isUnused())
+    RequiresNullCheck = false;
+
   // Emit a null-check if there's a consumed argument other than the receiver.
-  bool RequiresNullCheck = false;
-  if (ReceiverCanBeNull && CGM.getLangOpts().ObjCAutoRefCount && Method) {
+  if (!RequiresNullCheck && CGM.getLangOpts().ObjCAutoRefCount && Method) {
     for (const auto *ParamDecl : Method->parameters()) {
       if (ParamDecl->hasAttr<NSConsumedAttr>()) {
-        if (!nullReturn.NullBB)
-          nullReturn.init(CGF, Arg0);
         RequiresNullCheck = true;
         break;
       }
     }
   }
+
+  NullReturnState nullReturn;
+  if (RequiresNullCheck) {
+    nullReturn.init(CGF, Arg0);
+  }
   
   llvm::Instruction *CallSite;
   Fn = llvm::ConstantExpr::getBitCast(Fn, MSI.MessengerType);
@@ -2164,7 +2175,7 @@ CGObjCCommonMac::EmitMessageSend(CodeGen::CodeGenFunction &CGF,
     llvm::CallSite(CallSite).setDoesNotReturn();
   }
 
-  return nullReturn.complete(CGF, rvalue, ResultType, CallArgs,
+  return nullReturn.complete(CGF, Return, rvalue, ResultType, CallArgs,
                              RequiresNullCheck ? Method : nullptr);
 }
 
@@ -7073,7 +7084,7 @@ CGObjCNonFragileABIMac::EmitVTableMessageSend(CodeGenFunction &CGF,
   CGCallee callee(CGCalleeInfo(), calleePtr);
 
   RValue result = CGF.EmitCall(MSI.CallInfo, callee, returnSlot, args);
-  return nullReturn.complete(CGF, result, resultType, formalArgs,
+  return nullReturn.complete(CGF, returnSlot, result, resultType, formalArgs,
                              requiresnullCheck ? method : nullptr);
 }
 
index a7bcdda48b14c16d2603259134f9e1c50094237c..2314d5a9ecf5dcfd523d2f4e1297956fe39237c4 100644 (file)
@@ -12,11 +12,20 @@ struct stret one = {{1}};
 
 int main(int argc, const char **argv)
 {
-    [(id)(argc&~255) method];
+    struct stret s;
+    s = [(id)(argc&~255) method];
     // CHECK: call void bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to void (%struct.stret*, i8*, i8*)*)(%struct.stret* sret [[T0:%[^,]+]]
     // CHECK: [[T0P:%.*]] = bitcast %struct.stret* [[T0]] to i8*
     // CHECK: call void @llvm.memset.p0i8.i64(i8* [[T0P]], i8 0, i64 400, i32 4, i1 false)
 
+    s = [Test method];
+    // CHECK: call void bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to void (%struct.stret*, i8*, i8*)*)(%struct.stret* sret [[T1:%[^,]+]]
+    // CHECK-NOT: call void @llvm.memset.p0i8.i64(
+
+    [(id)(argc&~255) method];
+    // CHECK: call void bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to void (%struct.stret*, i8*, i8*)*)(%struct.stret* sret [[T1:%[^,]+]]
+    // CHECK-NOT: call void @llvm.memset.p0i8.i64(
+
     [Test method];
     // CHECK: call void bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to void (%struct.stret*, i8*, i8*)*)(%struct.stret* sret [[T1:%[^,]+]]
     // CHECK-NOT: call void @llvm.memset.p0i8.i64(
diff --git a/test/CodeGenObjC/stret-lifetime.m b/test/CodeGenObjC/stret-lifetime.m
new file mode 100644 (file)
index 0000000..d81ef34
--- /dev/null
@@ -0,0 +1,34 @@
+// RUN: %clang_cc1 -triple arm64-apple-darwin    -S -emit-llvm -o - -O2 -disable-llvm-passes %s | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -S -emit-llvm -o - -O2 -disable-llvm-passes %s | FileCheck %s
+// RUN: %clang_cc1 -triple arm64-apple-darwin    -fobjc-arc -S -emit-llvm -o - -O2 -disable-llvm-passes %s | FileCheck %s --check-prefixes=CHECK,ARC
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-arc -S -emit-llvm -o - -O2 -disable-llvm-passes %s | FileCheck %s --check-prefixes=CHECK,ARC
+
+struct stret { int x[100]; };
+struct stret one = {{1}};
+
+@interface Test
++(struct stret) method;
++(struct stret) methodConsuming:(id __attribute__((ns_consumed)))consumed;
+@end
+
+void foo(id o, id p) {
+  [o method];
+  // CHECK: @llvm.lifetime.start
+  // CHECK: call void bitcast {{.*}} @objc_msgSend
+  // CHECK: @llvm.lifetime.end
+  // CHECK-NOT: call void @llvm.memset
+
+  [o methodConsuming:p];
+  // ARC: [[T0:%.*]] = icmp eq i8*
+  // ARC: br i1 [[T0]]
+
+  // CHECK: @llvm.lifetime.start
+  // CHECK: call void bitcast {{.*}} @objc_msgSend
+  // CHECK: @llvm.lifetime.end
+  // ARC: br label
+
+  // ARC: call void @objc_release
+  // ARC: br label
+
+  // CHECK-NOT: call void @llvm.memset
+}