Skip to content

Commit 5ab6ee7

Browse files
committed
Fix a variety of bugs with nil-receiver checks when targeting
non-Darwin ObjC runtimes: - Use the same logic the Darwin runtime does for inferring that a receiver is non-null and therefore doesn't require null checks. Previously we weren't skipping these for non-super dispatch. - Emit a null check when there's a consumed parameter so that we can destroy the argument if the call doesn't happen. This mostly involves extracting some common logic from the Darwin-runtime code. - Generate a zero aggregate by zeroing the same memory that was used in the method call instead of zeroing separate memory and then merging them with a phi. This uses less memory and avoids unnecessary copies. - Emit zero initialization, and generate zero values in phis, using the proper zero-value routines instead of assuming that the zero value of the result type has a bitwise-zero representation.
1 parent 35ebe4c commit 5ab6ee7

File tree

8 files changed

+345
-121
lines changed

8 files changed

+345
-121
lines changed

clang/include/clang/AST/DeclObjC.h

+3
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,9 @@ class ObjCMethodDecl : public NamedDecl, public DeclContext {
487487
/// True if the method is tagged as objc_direct
488488
bool isDirectMethod() const;
489489

490+
/// True if the method has a parameter that's destroyed in the callee.
491+
bool hasParamDestroyedInCallee() const;
492+
490493
/// Returns the property associated with this method's selector.
491494
///
492495
/// Note that even if this particular method is not marked as a property

clang/lib/AST/Decl.cpp

+6-2
Original file line numberDiff line numberDiff line change
@@ -2789,11 +2789,15 @@ SourceRange ParmVarDecl::getSourceRange() const {
27892789
}
27902790

27912791
bool ParmVarDecl::isDestroyedInCallee() const {
2792+
// ns_consumed only affects code generation in ARC
27922793
if (hasAttr<NSConsumedAttr>())
2793-
return true;
2794+
return getASTContext().getLangOpts().ObjCAutoRefCount;
27942795

2796+
// FIXME: isParamDestroyedInCallee() should probably imply
2797+
// isDestructedType()
27952798
auto *RT = getType()->getAs<RecordType>();
2796-
if (RT && RT->getDecl()->isParamDestroyedInCallee())
2799+
if (RT && RT->getDecl()->isParamDestroyedInCallee() &&
2800+
getType().isDestructedType())
27972801
return true;
27982802

27992803
return false;

clang/lib/AST/DeclObjC.cpp

+8
Original file line numberDiff line numberDiff line change
@@ -855,6 +855,14 @@ bool ObjCMethodDecl::isDesignatedInitializerForTheInterface(
855855
return false;
856856
}
857857

858+
bool ObjCMethodDecl::hasParamDestroyedInCallee() const {
859+
for (auto param : parameters()) {
860+
if (param->isDestroyedInCallee())
861+
return true;
862+
}
863+
return false;
864+
}
865+
858866
Stmt *ObjCMethodDecl::getBody() const {
859867
return Body.get(getASTContext().getExternalSource());
860868
}

clang/lib/CodeGen/CGObjCGNU.cpp

+119-46
Original file line numberDiff line numberDiff line change
@@ -2651,35 +2651,6 @@ CGObjCGNU::GenerateMessageSend(CodeGenFunction &CGF,
26512651
}
26522652
}
26532653

2654-
// If the return type is something that goes in an integer register, the
2655-
// runtime will handle 0 returns. For other cases, we fill in the 0 value
2656-
// ourselves.
2657-
//
2658-
// The language spec says the result of this kind of message send is
2659-
// undefined, but lots of people seem to have forgotten to read that
2660-
// paragraph and insist on sending messages to nil that have structure
2661-
// returns. With GCC, this generates a random return value (whatever happens
2662-
// to be on the stack / in those registers at the time) on most platforms,
2663-
// and generates an illegal instruction trap on SPARC. With LLVM it corrupts
2664-
// the stack.
2665-
bool isPointerSizedReturn = (ResultType->isAnyPointerType() ||
2666-
ResultType->isIntegralOrEnumerationType() || ResultType->isVoidType());
2667-
2668-
llvm::BasicBlock *startBB = nullptr;
2669-
llvm::BasicBlock *messageBB = nullptr;
2670-
llvm::BasicBlock *continueBB = nullptr;
2671-
2672-
if (!isPointerSizedReturn) {
2673-
startBB = Builder.GetInsertBlock();
2674-
messageBB = CGF.createBasicBlock("msgSend");
2675-
continueBB = CGF.createBasicBlock("continue");
2676-
2677-
llvm::Value *isNil = Builder.CreateICmpEQ(Receiver,
2678-
llvm::Constant::getNullValue(Receiver->getType()));
2679-
Builder.CreateCondBr(isNil, continueBB, messageBB);
2680-
CGF.EmitBlock(messageBB);
2681-
}
2682-
26832654
IdTy = cast<llvm::PointerType>(CGM.getTypes().ConvertType(ASTIdTy));
26842655
llvm::Value *cmd;
26852656
if (Method)
@@ -2703,6 +2674,96 @@ CGObjCGNU::GenerateMessageSend(CodeGenFunction &CGF,
27032674

27042675
MessageSendInfo MSI = getMessageSendInfo(Method, ResultType, ActualArgs);
27052676

2677+
// Message sends are expected to return a zero value when the
2678+
// receiver is nil. At one point, this was only guaranteed for
2679+
// simple integer and pointer types, but expectations have grown
2680+
// over time.
2681+
//
2682+
// Given a nil receiver, the GNU runtime's message lookup will
2683+
// return a stub function that simply sets various return-value
2684+
// registers to zero and then returns. That's good enough for us
2685+
// if and only if (1) the calling conventions of that stub are
2686+
// compatible with the signature we're using and (2) the registers
2687+
// it sets are sufficient to produce a zero value of the return type.
2688+
// Rather than doing a whole target-specific analysis, we assume it
2689+
// only works for void, integer, and pointer types, and in all
2690+
// other cases we do an explicit nil check is emitted code. In
2691+
// addition to ensuring we produe a zero value for other types, this
2692+
// sidesteps the few outright CC incompatibilities we know about that
2693+
// could otherwise lead to crashes, like when a method is expected to
2694+
// return on the x87 floating point stack or adjust the stack pointer
2695+
// because of an indirect return.
2696+
bool hasParamDestroyedInCallee = false;
2697+
bool requiresExplicitZeroResult = false;
2698+
bool requiresNilReceiverCheck = [&] {
2699+
// We never need a check if we statically know the receiver isn't nil.
2700+
if (!canMessageReceiverBeNull(CGF, Method, /*IsSuper*/ false,
2701+
Class, Receiver))
2702+
return false;
2703+
2704+
// If there's a consumed argument, we need a nil check.
2705+
if (Method && Method->hasParamDestroyedInCallee()) {
2706+
hasParamDestroyedInCallee = true;
2707+
}
2708+
2709+
// If the return value isn't flagged as unused, and the result
2710+
// type isn't in our narrow set where we assume compatibility,
2711+
// we need a nil check to ensure a nil value.
2712+
if (!Return.isUnused()) {
2713+
if (ResultType->isVoidType()) {
2714+
// void results are definitely okay.
2715+
} else if (ResultType->hasPointerRepresentation() &&
2716+
CGM.getTypes().isZeroInitializable(ResultType)) {
2717+
// Pointer types should be fine as long as they have
2718+
// bitwise-zero null pointers. But do we need to worry
2719+
// about unusual address spaces?
2720+
} else if (ResultType->isIntegralOrEnumerationType()) {
2721+
// Bitwise zero should always be zero for integral types.
2722+
// FIXME: we probably need a size limit here, but we've
2723+
// never imposed one before
2724+
} else {
2725+
// Otherwise, use an explicit check just to be sure.
2726+
requiresExplicitZeroResult = true;
2727+
}
2728+
}
2729+
2730+
return hasParamDestroyedInCallee || requiresExplicitZeroResult;
2731+
}();
2732+
2733+
// We will need to explicitly zero-initialize an aggregate result slot
2734+
// if we generally require explicit zeroing and we have an aggregate
2735+
// result.
2736+
bool requiresExplicitAggZeroing =
2737+
requiresExplicitZeroResult && CGF.hasAggregateEvaluationKind(ResultType);
2738+
2739+
// The block we're going to end up in after any message send or nil path.
2740+
llvm::BasicBlock *continueBB = nullptr;
2741+
// The block that eventually branched to continueBB along the nil path.
2742+
llvm::BasicBlock *nilPathBB = nullptr;
2743+
// The block to do explicit work in along the nil path, if necessary.
2744+
llvm::BasicBlock *nilCleanupBB = nullptr;
2745+
2746+
// Emit the nil-receiver check.
2747+
if (requiresNilReceiverCheck) {
2748+
llvm::BasicBlock *messageBB = CGF.createBasicBlock("msgSend");
2749+
continueBB = CGF.createBasicBlock("continue");
2750+
2751+
// If we need to zero-initialize an aggregate result or destroy
2752+
// consumed arguments, we'll need a separate cleanup block.
2753+
// Otherwise we can just branch directly to the continuation block.
2754+
if (requiresExplicitAggZeroing || hasParamDestroyedInCallee) {
2755+
nilCleanupBB = CGF.createBasicBlock("nilReceiverCleanup");
2756+
} else {
2757+
nilPathBB = Builder.GetInsertBlock();
2758+
}
2759+
2760+
llvm::Value *isNil = Builder.CreateICmpEQ(Receiver,
2761+
llvm::Constant::getNullValue(Receiver->getType()));
2762+
Builder.CreateCondBr(isNil, nilCleanupBB ? nilCleanupBB : continueBB,
2763+
messageBB);
2764+
CGF.EmitBlock(messageBB);
2765+
}
2766+
27062767
// Get the IMP to call
27072768
llvm::Value *imp;
27082769

@@ -2744,36 +2805,48 @@ CGObjCGNU::GenerateMessageSend(CodeGenFunction &CGF,
27442805
RValue msgRet = CGF.EmitCall(MSI.CallInfo, callee, Return, ActualArgs, &call);
27452806
call->setMetadata(msgSendMDKind, node);
27462807

2747-
2748-
if (!isPointerSizedReturn) {
2749-
messageBB = CGF.Builder.GetInsertBlock();
2808+
if (requiresNilReceiverCheck) {
2809+
llvm::BasicBlock *nonNilPathBB = CGF.Builder.GetInsertBlock();
27502810
CGF.Builder.CreateBr(continueBB);
2811+
2812+
// Emit the nil path if we decided it was necessary above.
2813+
if (nilCleanupBB) {
2814+
CGF.EmitBlock(nilCleanupBB);
2815+
2816+
if (hasParamDestroyedInCallee) {
2817+
destroyCalleeDestroyedArguments(CGF, Method, CallArgs);
2818+
}
2819+
2820+
if (requiresExplicitAggZeroing) {
2821+
assert(msgRet.isAggregate());
2822+
Address addr = msgRet.getAggregateAddress();
2823+
CGF.EmitNullInitialization(addr, ResultType);
2824+
}
2825+
2826+
nilPathBB = CGF.Builder.GetInsertBlock();
2827+
CGF.Builder.CreateBr(continueBB);
2828+
}
2829+
2830+
// Enter the continuation block and emit a phi if required.
27512831
CGF.EmitBlock(continueBB);
27522832
if (msgRet.isScalar()) {
27532833
llvm::Value *v = msgRet.getScalarVal();
27542834
llvm::PHINode *phi = Builder.CreatePHI(v->getType(), 2);
2755-
phi->addIncoming(v, messageBB);
2756-
phi->addIncoming(llvm::Constant::getNullValue(v->getType()), startBB);
2835+
phi->addIncoming(v, nonNilPathBB);
2836+
phi->addIncoming(CGM.EmitNullConstant(ResultType), nilPathBB);
27572837
msgRet = RValue::get(phi);
27582838
} else if (msgRet.isAggregate()) {
2759-
Address v = msgRet.getAggregateAddress();
2760-
llvm::PHINode *phi = Builder.CreatePHI(v.getType(), 2);
2761-
llvm::Type *RetTy = v.getElementType();
2762-
Address NullVal = CGF.CreateTempAlloca(RetTy, v.getAlignment(), "null");
2763-
CGF.InitTempAlloca(NullVal, llvm::Constant::getNullValue(RetTy));
2764-
phi->addIncoming(v.getPointer(), messageBB);
2765-
phi->addIncoming(NullVal.getPointer(), startBB);
2766-
msgRet = RValue::getAggregate(Address(phi, v.getAlignment()));
2839+
// Aggregate zeroing is handled in nilCleanupBB when it's required.
27672840
} else /* isComplex() */ {
27682841
std::pair<llvm::Value*,llvm::Value*> v = msgRet.getComplexVal();
27692842
llvm::PHINode *phi = Builder.CreatePHI(v.first->getType(), 2);
2770-
phi->addIncoming(v.first, messageBB);
2843+
phi->addIncoming(v.first, nonNilPathBB);
27712844
phi->addIncoming(llvm::Constant::getNullValue(v.first->getType()),
2772-
startBB);
2845+
nilPathBB);
27732846
llvm::PHINode *phi2 = Builder.CreatePHI(v.second->getType(), 2);
2774-
phi2->addIncoming(v.second, messageBB);
2847+
phi2->addIncoming(v.second, nonNilPathBB);
27752848
phi2->addIncoming(llvm::Constant::getNullValue(v.second->getType()),
2776-
startBB);
2849+
nilPathBB);
27772850
msgRet = RValue::getComplex(phi, phi2);
27782851
}
27792852
}

clang/lib/CodeGen/CGObjCMac.cpp

+6-73
Original file line numberDiff line numberDiff line change
@@ -1754,37 +1754,9 @@ struct NullReturnState {
17541754
// Okay, start emitting the null-receiver block.
17551755
CGF.EmitBlock(NullBB);
17561756

1757-
// Release any consumed arguments we've got.
1757+
// Destroy any consumed arguments we've got.
17581758
if (Method) {
1759-
CallArgList::const_iterator I = CallArgs.begin();
1760-
for (ObjCMethodDecl::param_const_iterator i = Method->param_begin(),
1761-
e = Method->param_end(); i != e; ++i, ++I) {
1762-
const ParmVarDecl *ParamDecl = (*i);
1763-
if (ParamDecl->hasAttr<NSConsumedAttr>()) {
1764-
RValue RV = I->getRValue(CGF);
1765-
assert(RV.isScalar() &&
1766-
"NullReturnState::complete - arg not on object");
1767-
CGF.EmitARCRelease(RV.getScalarVal(), ARCImpreciseLifetime);
1768-
} else {
1769-
QualType QT = ParamDecl->getType();
1770-
auto *RT = QT->getAs<RecordType>();
1771-
if (RT && RT->getDecl()->isParamDestroyedInCallee()) {
1772-
RValue RV = I->getRValue(CGF);
1773-
QualType::DestructionKind DtorKind = QT.isDestructedType();
1774-
switch (DtorKind) {
1775-
case QualType::DK_cxx_destructor:
1776-
CGF.destroyCXXObject(CGF, RV.getAggregateAddress(), QT);
1777-
break;
1778-
case QualType::DK_nontrivial_c_struct:
1779-
CGF.destroyNonTrivialCStruct(CGF, RV.getAggregateAddress(), QT);
1780-
break;
1781-
default:
1782-
llvm_unreachable("unexpected dtor kind");
1783-
break;
1784-
}
1785-
}
1786-
}
1787-
}
1759+
CGObjCRuntime::destroyCalleeDestroyedArguments(CGF, Method, CallArgs);
17881760
}
17891761

17901762
// The phi code below assumes that we haven't needed any control flow yet.
@@ -2151,15 +2123,6 @@ CodeGen::RValue CGObjCMac::GenerateMessageSend(CodeGen::CodeGenFunction &CGF,
21512123
Method, Class, ObjCTypes);
21522124
}
21532125

2154-
static bool isWeakLinkedClass(const ObjCInterfaceDecl *ID) {
2155-
do {
2156-
if (ID->isWeakImported())
2157-
return true;
2158-
} while ((ID = ID->getSuperClass()));
2159-
2160-
return false;
2161-
}
2162-
21632126
CodeGen::RValue
21642127
CGObjCCommonMac::EmitMessageSend(CodeGen::CodeGenFunction &CGF,
21652128
ReturnValueSlot Return,
@@ -2200,32 +2163,8 @@ CGObjCCommonMac::EmitMessageSend(CodeGen::CodeGenFunction &CGF,
22002163
CGM.getContext().getCanonicalType(ResultType) &&
22012164
"Result type mismatch!");
22022165

2203-
bool ReceiverCanBeNull = true;
2204-
2205-
// Super dispatch assumes that self is non-null; even the messenger
2206-
// doesn't have a null check internally.
2207-
if (IsSuper) {
2208-
ReceiverCanBeNull = false;
2209-
2210-
// If this is a direct dispatch of a class method, check whether the class,
2211-
// or anything in its hierarchy, was weak-linked.
2212-
} else if (ClassReceiver && Method && Method->isClassMethod()) {
2213-
ReceiverCanBeNull = isWeakLinkedClass(ClassReceiver);
2214-
2215-
// If we're emitting a method, and self is const (meaning just ARC, for now),
2216-
// and the receiver is a load of self, then self is a valid object.
2217-
} else if (auto CurMethod =
2218-
dyn_cast_or_null<ObjCMethodDecl>(CGF.CurCodeDecl)) {
2219-
auto Self = CurMethod->getSelfDecl();
2220-
if (Self->getType().isConstQualified()) {
2221-
if (auto LI = dyn_cast<llvm::LoadInst>(Arg0->stripPointerCasts())) {
2222-
llvm::Value *SelfAddr = CGF.GetAddrOfLocalVar(Self).getPointer();
2223-
if (SelfAddr == LI->getPointerOperand()) {
2224-
ReceiverCanBeNull = false;
2225-
}
2226-
}
2227-
}
2228-
}
2166+
bool ReceiverCanBeNull =
2167+
canMessageReceiverBeNull(CGF, Method, IsSuper, ClassReceiver, Arg0);
22292168

22302169
bool RequiresNullCheck = false;
22312170

@@ -2261,14 +2200,8 @@ CGObjCCommonMac::EmitMessageSend(CodeGen::CodeGenFunction &CGF,
22612200
RequiresNullCheck = false;
22622201

22632202
// Emit a null-check if there's a consumed argument other than the receiver.
2264-
if (!RequiresNullCheck && CGM.getLangOpts().ObjCAutoRefCount && Method) {
2265-
for (const auto *ParamDecl : Method->parameters()) {
2266-
if (ParamDecl->isDestroyedInCallee()) {
2267-
RequiresNullCheck = true;
2268-
break;
2269-
}
2270-
}
2271-
}
2203+
if (!RequiresNullCheck && Method && Method->hasParamDestroyedInCallee())
2204+
RequiresNullCheck = true;
22722205

22732206
NullReturnState nullReturn;
22742207
if (RequiresNullCheck) {

0 commit comments

Comments
 (0)