forked from OSchip/llvm-project
[CodeGen] Emit destructor calls to destruct compound literals
Fix a bug in IRGen where it wasn't destructing compound literals in C that are ObjC pointer arrays or non-trivial structs. Also diagnose jumps that enter or exit the lifetime of the compound literals. rdar://problem/51867864 Differential Revision: https://reviews.llvm.org/D64464
This commit is contained in:
parent
ddfcda0256
commit
40568fec7e
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include "clang/AST/DeclBase.h"
|
||||
#include "clang/AST/DeclarationName.h"
|
||||
#include "clang/AST/ExprCXX.h"
|
||||
#include "clang/AST/NestedNameSpecifier.h"
|
||||
#include "clang/AST/TemplateName.h"
|
||||
#include "clang/AST/Type.h"
|
||||
|
@ -349,6 +350,10 @@ class TypeSourceInfo;
|
|||
return ToOrErr.takeError();
|
||||
}
|
||||
|
||||
/// Import cleanup objects owned by ExprWithCleanup.
|
||||
llvm::Expected<ExprWithCleanups::CleanupObject>
|
||||
Import(ExprWithCleanups::CleanupObject From);
|
||||
|
||||
/// Import the given type from the "from" context into the "to"
|
||||
/// context. A null type is imported as a null type (no error).
|
||||
///
|
||||
|
|
|
@ -3321,13 +3321,15 @@ public:
|
|||
/// literal is the extent of the enclosing scope.
|
||||
class ExprWithCleanups final
|
||||
: public FullExpr,
|
||||
private llvm::TrailingObjects<ExprWithCleanups, BlockDecl *> {
|
||||
private llvm::TrailingObjects<
|
||||
ExprWithCleanups,
|
||||
llvm::PointerUnion<BlockDecl *, CompoundLiteralExpr *>> {
|
||||
public:
|
||||
/// The type of objects that are kept in the cleanup.
|
||||
/// It's useful to remember the set of blocks; we could also
|
||||
/// remember the set of temporaries, but there's currently
|
||||
/// no need.
|
||||
using CleanupObject = BlockDecl *;
|
||||
/// It's useful to remember the set of blocks and block-scoped compound
|
||||
/// literals; we could also remember the set of temporaries, but there's
|
||||
/// currently no need.
|
||||
using CleanupObject = llvm::PointerUnion<BlockDecl *, CompoundLiteralExpr *>;
|
||||
|
||||
private:
|
||||
friend class ASTStmtReader;
|
||||
|
|
|
@ -184,6 +184,7 @@ public:
|
|||
void dumpBareDeclRef(const Decl *D);
|
||||
void dumpName(const NamedDecl *ND);
|
||||
void dumpAccessSpecifier(AccessSpecifier AS);
|
||||
void dumpCleanupObject(const ExprWithCleanups::CleanupObject &C);
|
||||
|
||||
void dumpDeclRef(const Decl *D, StringRef Label = {});
|
||||
|
||||
|
|
|
@ -5543,6 +5543,8 @@ def note_enters_block_captures_weak : Note<
|
|||
def note_enters_block_captures_non_trivial_c_struct : Note<
|
||||
"jump enters lifetime of block which captures a C struct that is non-trivial "
|
||||
"to destroy">;
|
||||
def note_enters_compound_literal_scope : Note<
|
||||
"jump enters lifetime of a compound literal that is non-trivial to destruct">;
|
||||
|
||||
def note_exits_cleanup : Note<
|
||||
"jump exits scope of variable with __attribute__((cleanup))">;
|
||||
|
@ -5586,6 +5588,8 @@ def note_exits_block_captures_weak : Note<
|
|||
def note_exits_block_captures_non_trivial_c_struct : Note<
|
||||
"jump exits lifetime of block which captures a C struct that is non-trivial "
|
||||
"to destroy">;
|
||||
def note_exits_compound_literal_scope : Note<
|
||||
"jump exits lifetime of a compound literal that is non-trivial to destruct">;
|
||||
|
||||
def err_func_returning_qualified_void : ExtWarn<
|
||||
"function cannot return qualified void type %0">,
|
||||
|
|
|
@ -613,9 +613,8 @@ public:
|
|||
CleanupInfo Cleanup;
|
||||
|
||||
/// ExprCleanupObjects - This is the stack of objects requiring
|
||||
/// cleanup that are created by the current full expression. The
|
||||
/// element type here is ExprWithCleanups::Object.
|
||||
SmallVector<BlockDecl*, 8> ExprCleanupObjects;
|
||||
/// cleanup that are created by the current full expression.
|
||||
SmallVector<ExprWithCleanups::CleanupObject, 8> ExprCleanupObjects;
|
||||
|
||||
/// Store a set of either DeclRefExprs or MemberExprs that contain a reference
|
||||
/// to a variable (constant) that may or may not be odr-used in this Expr, and
|
||||
|
|
|
@ -1900,6 +1900,9 @@ namespace serialization {
|
|||
CTOR_INITIALIZER_INDIRECT_MEMBER
|
||||
};
|
||||
|
||||
/// Kinds of cleanup objects owned by ExprWithCleanups.
|
||||
enum CleanupObjectKind { COK_Block, COK_CompoundLiteral };
|
||||
|
||||
/// Describes the redeclarations of a declaration.
|
||||
struct LocalRedeclarationsInfo {
|
||||
// The ID of the first declaration
|
||||
|
|
|
@ -7910,6 +7910,18 @@ void ASTImporter::RegisterImportedDecl(Decl *FromD, Decl *ToD) {
|
|||
MapImported(FromD, ToD);
|
||||
}
|
||||
|
||||
llvm::Expected<ExprWithCleanups::CleanupObject>
|
||||
ASTImporter::Import(ExprWithCleanups::CleanupObject From) {
|
||||
if (auto *CLE = From.dyn_cast<CompoundLiteralExpr *>()) {
|
||||
if (Expected<Expr *> R = Import(CLE))
|
||||
return ExprWithCleanups::CleanupObject(cast<CompoundLiteralExpr>(*R));
|
||||
}
|
||||
|
||||
// FIXME: Handle BlockDecl when we implement importing BlockExpr in
|
||||
// ASTNodeImporter.
|
||||
return make_error<ImportError>(ImportError::UnsupportedConstruct);
|
||||
}
|
||||
|
||||
Expected<QualType> ASTImporter::Import(QualType FromT) {
|
||||
if (FromT.isNull())
|
||||
return QualType{};
|
||||
|
|
|
@ -1334,7 +1334,16 @@ void JSONNodeDumper::VisitExprWithCleanups(const ExprWithCleanups *EWC) {
|
|||
if (EWC->getNumObjects()) {
|
||||
JOS.attributeArray("cleanups", [this, EWC] {
|
||||
for (const ExprWithCleanups::CleanupObject &CO : EWC->getObjects())
|
||||
JOS.value(createBareDeclRef(CO));
|
||||
if (auto *BD = CO.dyn_cast<BlockDecl *>()) {
|
||||
JOS.value(createBareDeclRef(BD));
|
||||
} else if (auto *CLE = CO.dyn_cast<CompoundLiteralExpr *>()) {
|
||||
llvm::json::Object Obj;
|
||||
Obj["id"] = createPointerRepresentation(CLE);
|
||||
Obj["kind"] = CLE->getStmtClassName();
|
||||
JOS.value(std::move(Obj));
|
||||
} else {
|
||||
llvm_unreachable("unexpected cleanup object type");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -448,6 +448,23 @@ void TextNodeDumper::dumpAccessSpecifier(AccessSpecifier AS) {
|
|||
}
|
||||
}
|
||||
|
||||
void TextNodeDumper::dumpCleanupObject(
|
||||
const ExprWithCleanups::CleanupObject &C) {
|
||||
if (auto *BD = C.dyn_cast<BlockDecl *>())
|
||||
dumpDeclRef(BD, "cleanup");
|
||||
else if (auto *CLE = C.dyn_cast<CompoundLiteralExpr *>())
|
||||
AddChild([=] {
|
||||
OS << "cleanup ";
|
||||
{
|
||||
ColorScope Color(OS, ShowColors, StmtColor);
|
||||
OS << CLE->getStmtClassName();
|
||||
}
|
||||
dumpPointer(CLE);
|
||||
});
|
||||
else
|
||||
llvm_unreachable("unexpected cleanup type");
|
||||
}
|
||||
|
||||
void TextNodeDumper::dumpDeclRef(const Decl *D, StringRef Label) {
|
||||
if (!D)
|
||||
return;
|
||||
|
@ -950,7 +967,7 @@ void TextNodeDumper::VisitMaterializeTemporaryExpr(
|
|||
|
||||
void TextNodeDumper::VisitExprWithCleanups(const ExprWithCleanups *Node) {
|
||||
for (unsigned i = 0, e = Node->getNumObjects(); i != e; ++i)
|
||||
dumpDeclRef(Node->getObject(i), "cleanup");
|
||||
dumpCleanupObject(Node->getObject(i));
|
||||
}
|
||||
|
||||
void TextNodeDumper::VisitSizeOfPackExpr(const SizeOfPackExpr *Node) {
|
||||
|
|
|
@ -860,13 +860,13 @@ static void enterBlockScope(CodeGenFunction &CGF, BlockDecl *block) {
|
|||
}
|
||||
|
||||
/// Enter a full-expression with a non-trivial number of objects to
|
||||
/// clean up. This is in this file because, at the moment, the only
|
||||
/// kind of cleanup object is a BlockDecl*.
|
||||
/// clean up.
|
||||
void CodeGenFunction::enterNonTrivialFullExpression(const FullExpr *E) {
|
||||
if (const auto EWC = dyn_cast<ExprWithCleanups>(E)) {
|
||||
assert(EWC->getNumObjects() != 0);
|
||||
for (const ExprWithCleanups::CleanupObject &C : EWC->getObjects())
|
||||
enterBlockScope(*this, C);
|
||||
if (auto *BD = C.dyn_cast<BlockDecl *>())
|
||||
enterBlockScope(*this, BD);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4268,6 +4268,14 @@ LValue CodeGenFunction::EmitCompoundLiteralLValue(const CompoundLiteralExpr *E){
|
|||
EmitAnyExprToMem(InitExpr, DeclPtr, E->getType().getQualifiers(),
|
||||
/*Init*/ true);
|
||||
|
||||
// Block-scope compound literals are destroyed at the end of the enclosing
|
||||
// scope in C.
|
||||
if (!getLangOpts().CPlusPlus)
|
||||
if (QualType::DestructionKind DtorKind = E->getType().isDestructedType())
|
||||
pushLifetimeExtendedDestroy(getCleanupKind(DtorKind), DeclPtr,
|
||||
E->getType(), getDestroyer(DtorKind),
|
||||
DtorKind & EHCleanup);
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
|
|
|
@ -659,7 +659,21 @@ AggExprEmitter::VisitCompoundLiteralExpr(CompoundLiteralExpr *E) {
|
|||
}
|
||||
|
||||
AggValueSlot Slot = EnsureSlot(E->getType());
|
||||
|
||||
// Block-scope compound literals are destroyed at the end of the enclosing
|
||||
// scope in C.
|
||||
bool Destruct =
|
||||
!CGF.getLangOpts().CPlusPlus && !Slot.isExternallyDestructed();
|
||||
if (Destruct)
|
||||
Slot.setExternallyDestructed();
|
||||
|
||||
CGF.EmitAggExpr(E->getInitializer(), Slot);
|
||||
|
||||
if (Destruct)
|
||||
if (QualType::DestructionKind DtorKind = E->getType().isDestructedType())
|
||||
CGF.pushLifetimeExtendedDestroy(
|
||||
CGF.getCleanupKind(DtorKind), Slot.getAddress(), E->getType(),
|
||||
CGF.getDestroyer(DtorKind), DtorKind & EHCleanup);
|
||||
}
|
||||
|
||||
/// Attempt to look through various unimportant expressions to find a
|
||||
|
|
|
@ -556,6 +556,11 @@ public:
|
|||
Value *VisitMemberExpr(MemberExpr *E);
|
||||
Value *VisitExtVectorElementExpr(Expr *E) { return EmitLoadOfLValue(E); }
|
||||
Value *VisitCompoundLiteralExpr(CompoundLiteralExpr *E) {
|
||||
// Strictly speaking, we shouldn't be calling EmitLoadOfLValue, which
|
||||
// transitively calls EmitCompoundLiteralLValue, here in C++ since compound
|
||||
// literals aren't l-values in C++. We do so simply because that's the
|
||||
// cleanest way to handle compound literals in C++.
|
||||
// See the discussion here: https://reviews.llvm.org/D64464
|
||||
return EmitLoadOfLValue(E);
|
||||
}
|
||||
|
||||
|
|
|
@ -75,6 +75,7 @@ private:
|
|||
void BuildScopeInformation(Decl *D, unsigned &ParentScope);
|
||||
void BuildScopeInformation(VarDecl *D, const BlockDecl *BDecl,
|
||||
unsigned &ParentScope);
|
||||
void BuildScopeInformation(CompoundLiteralExpr *CLE, unsigned &ParentScope);
|
||||
void BuildScopeInformation(Stmt *S, unsigned &origParentScope);
|
||||
|
||||
void VerifyJumps();
|
||||
|
@ -276,6 +277,16 @@ void JumpScopeChecker::BuildScopeInformation(VarDecl *D,
|
|||
}
|
||||
}
|
||||
|
||||
/// Build scope information for compound literals of C struct types that are
|
||||
/// non-trivial to destruct.
|
||||
void JumpScopeChecker::BuildScopeInformation(CompoundLiteralExpr *CLE,
|
||||
unsigned &ParentScope) {
|
||||
unsigned InDiag = diag::note_enters_compound_literal_scope;
|
||||
unsigned OutDiag = diag::note_exits_compound_literal_scope;
|
||||
Scopes.push_back(GotoScope(ParentScope, InDiag, OutDiag, CLE->getExprLoc()));
|
||||
ParentScope = Scopes.size() - 1;
|
||||
}
|
||||
|
||||
/// BuildScopeInformation - The statements from CI to CE are known to form a
|
||||
/// coherent VLA scope with a specified parent node. Walk through the
|
||||
/// statements, adding any labels or gotos to LabelAndGotoScopes and recursively
|
||||
|
@ -529,11 +540,15 @@ void JumpScopeChecker::BuildScopeInformation(Stmt *S,
|
|||
// implementable but a lot of work which we haven't felt up to doing.
|
||||
ExprWithCleanups *EWC = cast<ExprWithCleanups>(S);
|
||||
for (unsigned i = 0, e = EWC->getNumObjects(); i != e; ++i) {
|
||||
const BlockDecl *BDecl = EWC->getObject(i);
|
||||
for (const auto &CI : BDecl->captures()) {
|
||||
VarDecl *variable = CI.getVariable();
|
||||
BuildScopeInformation(variable, BDecl, origParentScope);
|
||||
}
|
||||
if (auto *BDecl = EWC->getObject(i).dyn_cast<BlockDecl *>())
|
||||
for (const auto &CI : BDecl->captures()) {
|
||||
VarDecl *variable = CI.getVariable();
|
||||
BuildScopeInformation(variable, BDecl, origParentScope);
|
||||
}
|
||||
else if (auto *CLE = EWC->getObject(i).dyn_cast<CompoundLiteralExpr *>())
|
||||
BuildScopeInformation(CLE, origParentScope);
|
||||
else
|
||||
llvm_unreachable("unexpected cleanup object type");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -6251,14 +6251,24 @@ Sema::BuildCompoundLiteralExpr(SourceLocation LParenLoc, TypeSourceInfo *TInfo,
|
|||
return ExprError();
|
||||
}
|
||||
|
||||
// Compound literals that have automatic storage duration are destroyed at
|
||||
// the end of the scope. Emit diagnostics if it is or contains a C union type
|
||||
// that is non-trivial to destruct.
|
||||
if (!isFileScope)
|
||||
if (!isFileScope && !getLangOpts().CPlusPlus) {
|
||||
// Compound literals that have automatic storage duration are destroyed at
|
||||
// the end of the scope in C; in C++, they're just temporaries.
|
||||
|
||||
// Emit diagnostics if it is or contains a C union type that is non-trivial
|
||||
// to destruct.
|
||||
if (E->getType().hasNonTrivialToPrimitiveDestructCUnion())
|
||||
checkNonTrivialCUnion(E->getType(), E->getExprLoc(),
|
||||
NTCUC_CompoundLiteral, NTCUK_Destruct);
|
||||
|
||||
// Diagnose jumps that enter or exit the lifetime of the compound literal.
|
||||
if (literalType.isDestructedType()) {
|
||||
Cleanup.setExprNeedsCleanups(true);
|
||||
ExprCleanupObjects.push_back(E);
|
||||
getCurFunction()->setHasBranchProtectedScope();
|
||||
}
|
||||
}
|
||||
|
||||
if (E->getType().hasNonTrivialToPrimitiveDefaultInitializeCUnion() ||
|
||||
E->getType().hasNonTrivialToPrimitiveCopyCUnion())
|
||||
checkNonTrivialCUnionInInitializer(E->getInitializer(),
|
||||
|
|
|
@ -1819,9 +1819,17 @@ void ASTStmtReader::VisitExprWithCleanups(ExprWithCleanups *E) {
|
|||
|
||||
unsigned NumObjects = Record.readInt();
|
||||
assert(NumObjects == E->getNumObjects());
|
||||
for (unsigned i = 0; i != NumObjects; ++i)
|
||||
E->getTrailingObjects<BlockDecl *>()[i] =
|
||||
readDeclAs<BlockDecl>();
|
||||
for (unsigned i = 0; i != NumObjects; ++i) {
|
||||
unsigned CleanupKind = Record.readInt();
|
||||
ExprWithCleanups::CleanupObject Obj;
|
||||
if (CleanupKind == COK_Block)
|
||||
Obj = readDeclAs<BlockDecl>();
|
||||
else if (CleanupKind == COK_CompoundLiteral)
|
||||
Obj = cast<CompoundLiteralExpr>(Record.readSubExpr());
|
||||
else
|
||||
llvm_unreachable("unexpected cleanup object type");
|
||||
E->getTrailingObjects<ExprWithCleanups::CleanupObject>()[i] = Obj;
|
||||
}
|
||||
|
||||
E->ExprWithCleanupsBits.CleanupsHaveSideEffects = Record.readInt();
|
||||
E->SubExpr = Record.readSubExpr();
|
||||
|
|
|
@ -1721,8 +1721,15 @@ void ASTStmtWriter::VisitCXXPseudoDestructorExpr(CXXPseudoDestructorExpr *E) {
|
|||
void ASTStmtWriter::VisitExprWithCleanups(ExprWithCleanups *E) {
|
||||
VisitExpr(E);
|
||||
Record.push_back(E->getNumObjects());
|
||||
for (unsigned i = 0, e = E->getNumObjects(); i != e; ++i)
|
||||
Record.AddDeclRef(E->getObject(i));
|
||||
for (auto &Obj : E->getObjects()) {
|
||||
if (auto *BD = Obj.dyn_cast<BlockDecl *>()) {
|
||||
Record.push_back(serialization::COK_Block);
|
||||
Record.AddDeclRef(BD);
|
||||
} else if (auto *CLE = Obj.dyn_cast<CompoundLiteralExpr *>()) {
|
||||
Record.push_back(serialization::COK_CompoundLiteral);
|
||||
Record.AddStmt(CLE);
|
||||
}
|
||||
}
|
||||
|
||||
Record.push_back(E->cleanupsHaveSideEffects());
|
||||
Record.AddStmt(E->getSubExpr());
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -x objective-c -fobjc-arc -ast-dump=json -ast-dump-filter Test %s | FileCheck %s
|
||||
|
||||
typedef struct {
|
||||
id f;
|
||||
} S;
|
||||
|
||||
id TestCompoundLiteral(id a) {
|
||||
return ((S){ .f = a }).f;
|
||||
}
|
||||
|
||||
// CHECK: "kind": "ExprWithCleanups",
|
||||
// CHECK-NEXT: "range": {
|
||||
// CHECK-NEXT: "begin": {
|
||||
// CHECK-NEXT: "offset": 202,
|
||||
// CHECK-NEXT: "col": 10,
|
||||
// CHECK-NEXT: "tokLen": 1
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: "end": {
|
||||
// CHECK-NEXT: "offset": 218,
|
||||
// CHECK-NEXT: "col": 26,
|
||||
// CHECK-NEXT: "tokLen": 1
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: "type": {
|
||||
// CHECK-NEXT: "desugaredQualType": "id",
|
||||
// CHECK-NEXT: "qualType": "id",
|
||||
// CHECK-NEXT: "typeAliasDeclId": "0x{{.*}}"
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: "valueCategory": "rvalue",
|
||||
// CHECK-NEXT: "cleanupsHaveSideEffects": true,
|
||||
// CHECK-NEXT: "cleanups": [
|
||||
// CHECK-NEXT: {
|
||||
// CHECK-NEXT: "id": "0x{{.*}}",
|
||||
// CHECK-NEXT: "kind": "CompoundLiteralExpr"
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: ],
|
|
@ -1,4 +1,4 @@
|
|||
// RUN: %clang_cc1 -Wno-unused -fblocks -fobjc-exceptions -ast-dump -ast-dump-filter Test %s | FileCheck -strict-whitespace %s
|
||||
// RUN: %clang_cc1 -Wno-unused -fobjc-arc -fblocks -fobjc-exceptions -ast-dump -ast-dump-filter Test %s | FileCheck -strict-whitespace %s
|
||||
|
||||
void TestBlockExpr(int x) {
|
||||
^{ x; };
|
||||
|
@ -34,3 +34,16 @@ void TestObjCAtCatchStmt() {
|
|||
// CHECK-NEXT: ObjCAtCatchStmt{{.*}} catch all
|
||||
// CHECK-NEXT: CompoundStmt
|
||||
// CHECK-NEXT: ObjCAtFinallyStmt
|
||||
|
||||
typedef struct {
|
||||
id f;
|
||||
} S;
|
||||
|
||||
id TestCompoundLiteral(id a) {
|
||||
return ((S){ .f = a }).f;
|
||||
}
|
||||
|
||||
// CHECK: FunctionDecl{{.*}}TestCompoundLiteral
|
||||
// CHECK: ExprWithCleanups
|
||||
// CHECK-NEXT: cleanup CompoundLiteralExpr
|
||||
// CHECK: CompoundLiteralExpr{{.*}}'S':'S' lvalue
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -fblocks -fobjc-arc -fobjc-runtime-has-weak -O2 -disable-llvm-passes -o - %s | FileCheck %s
|
||||
|
||||
id g0, g1;
|
||||
|
||||
void test0(_Bool cond) {
|
||||
id test0_helper(void) __attribute__((ns_returns_retained));
|
||||
|
||||
|
@ -147,4 +149,58 @@ void test2(int cond) {
|
|||
// CHECK: call void @llvm.objc.release(i8* [[RESULT]])
|
||||
}
|
||||
|
||||
void test3(int cond) {
|
||||
__strong id *p = cond ? (__strong id[]){g0, g1} : (__strong id[]){g1, g0};
|
||||
test2(cond);
|
||||
|
||||
// CHECK: define void @test3(
|
||||
// CHECK: %[[P:.*]] = alloca i8**, align 8
|
||||
// CHECK: %[[_COMPOUNDLITERAL:.*]] = alloca [2 x i8*], align 8
|
||||
// CHECK: %[[CLEANUP_COND:.*]] = alloca i1, align 1
|
||||
// CHECK: %[[_COMPOUNDLITERAL1:.*]] = alloca [2 x i8*], align 8
|
||||
// CHECK: %[[CLEANUP_COND4:.*]] = alloca i1, align 1
|
||||
|
||||
// CHECK: %[[ARRAYINIT_BEGIN:.*]] = getelementptr inbounds [2 x i8*], [2 x i8*]* %[[_COMPOUNDLITERAL]], i64 0, i64 0
|
||||
// CHECK: %[[V2:.*]] = load i8*, i8** @g0, align 8
|
||||
// CHECK: %[[V3:.*]] = call i8* @llvm.objc.retain(i8* %[[V2]])
|
||||
// CHECK: store i8* %[[V3]], i8** %[[ARRAYINIT_BEGIN]], align 8
|
||||
// CHECK: %[[ARRAYINIT_ELEMENT:.*]] = getelementptr inbounds i8*, i8** %[[ARRAYINIT_BEGIN]], i64 1
|
||||
// CHECK: %[[V4:.*]] = load i8*, i8** @g1, align 8
|
||||
// CHECK: %[[V5:.*]] = call i8* @llvm.objc.retain(i8* %[[V4]])
|
||||
// CHECK: store i8* %[[V5]], i8** %[[ARRAYINIT_ELEMENT]], align 8
|
||||
// CHECK: store i1 true, i1* %[[CLEANUP_COND]], align 1
|
||||
// CHECK: %[[ARRAYDECAY:.*]] = getelementptr inbounds [2 x i8*], [2 x i8*]* %[[_COMPOUNDLITERAL]], i64 0, i64 0
|
||||
|
||||
// CHECK: %[[ARRAYINIT_BEGIN2:.*]] = getelementptr inbounds [2 x i8*], [2 x i8*]* %[[_COMPOUNDLITERAL1]], i64 0, i64 0
|
||||
// CHECK: %[[V6:.*]] = load i8*, i8** @g1, align 8
|
||||
// CHECK: %[[V7:.*]] = call i8* @llvm.objc.retain(i8* %[[V6]])
|
||||
// CHECK: store i8* %[[V7]], i8** %[[ARRAYINIT_BEGIN2]], align 8
|
||||
// CHECK: %[[ARRAYINIT_ELEMENT3:.*]] = getelementptr inbounds i8*, i8** %[[ARRAYINIT_BEGIN2]], i64 1
|
||||
// CHECK: %[[V8:.*]] = load i8*, i8** @g0, align 8
|
||||
// CHECK: %[[V9:.*]] = call i8* @llvm.objc.retain(i8* %[[V8]])
|
||||
// CHECK: store i8* %[[V9]], i8** %[[ARRAYINIT_ELEMENT3]], align 8
|
||||
// CHECK: store i1 true, i1* %[[CLEANUP_COND4]], align 1
|
||||
// CHECK: %[[ARRAYDECAY5:.*]] = getelementptr inbounds [2 x i8*], [2 x i8*]* %[[_COMPOUNDLITERAL1]], i64 0, i64 0
|
||||
|
||||
// CHECK: %[[COND6:.*]] = phi i8** [ %[[ARRAYDECAY]], %{{.*}} ], [ %[[ARRAYDECAY5]], %{{.*}} ]
|
||||
// CHECK: store i8** %[[COND6]], i8*** %[[P]], align 8
|
||||
// CHECK: call void @test2(
|
||||
|
||||
// CHECK: %[[ARRAY_BEGIN:.*]] = getelementptr inbounds [2 x i8*], [2 x i8*]* %[[_COMPOUNDLITERAL1]], i32 0, i32 0
|
||||
// CHECK: %[[V11:.*]] = getelementptr inbounds i8*, i8** %[[ARRAY_BEGIN]], i64 2
|
||||
|
||||
// CHECK: %[[ARRAYDESTROY_ELEMENTPAST:.*]] = phi i8** [ %[[V11]], %{{.*}} ], [ %[[ARRAYDESTROY_ELEMENT:.*]], %{{.*}} ]
|
||||
// CHECK: %[[ARRAYDESTROY_ELEMENT]] = getelementptr inbounds i8*, i8** %[[ARRAYDESTROY_ELEMENTPAST]], i64 -1
|
||||
// CHECK: %[[V12:.*]] = load i8*, i8** %[[ARRAYDESTROY_ELEMENT]], align 8
|
||||
// CHECK: call void @llvm.objc.release(i8* %[[V12]])
|
||||
|
||||
// CHECK: %[[ARRAY_BEGIN10:.*]] = getelementptr inbounds [2 x i8*], [2 x i8*]* %[[_COMPOUNDLITERAL]], i32 0, i32 0
|
||||
// CHECK: %[[V13:.*]] = getelementptr inbounds i8*, i8** %[[ARRAY_BEGIN10]], i64 2
|
||||
|
||||
// CHECK: %[[ARRAYDESTROY_ELEMENTPAST12:.*]] = phi i8** [ %[[V13]], %{{.*}} ], [ %[[ARRAYDESTROY_ELEMENT13:.*]], %{{.*}} ]
|
||||
// CHECK: %[[ARRAYDESTROY_ELEMENT13]] = getelementptr inbounds i8*, i8** %[[ARRAYDESTROY_ELEMENTPAST12]], i64 -1
|
||||
// CHECK: %[[V14:.*]] = load i8*, i8** %[[ARRAYDESTROY_ELEMENT13]], align 8
|
||||
// CHECK: call void @llvm.objc.release(i8* %[[V14]])
|
||||
}
|
||||
|
||||
// CHECK: attributes [[NUW]] = { nounwind }
|
||||
|
|
|
@ -1557,6 +1557,43 @@ void test71(void) {
|
|||
getAggDtor();
|
||||
}
|
||||
|
||||
// Check that no extra release calls are emitted to detruct the compond literal.
|
||||
|
||||
// CHECK: define void @test72(i8* %[[A:.*]], i8* %[[B:.*]])
|
||||
// CHECK: %[[A_ADDR:.*]] = alloca i8*, align 8
|
||||
// CHECK: %[[B_ADDR:.*]] = alloca i8*, align 8
|
||||
// CHECK: %[[T:.*]] = alloca [2 x i8*], align 16
|
||||
// CHECK: %[[V0:.*]] = call i8* @llvm.objc.retain(i8* %[[A]])
|
||||
// CHECK: %[[V1:.*]] = call i8* @llvm.objc.retain(i8* %[[B]]) #2
|
||||
// CHECK: %[[ARRAYINIT_BEGIN:.*]] = getelementptr inbounds [2 x i8*], [2 x i8*]* %[[T]], i64 0, i64 0
|
||||
// CHECK: %[[V3:.*]] = load i8*, i8** %[[A_ADDR]], align 8, !tbaa !7
|
||||
// CHECK: %[[V4:.*]] = call i8* @llvm.objc.retain(i8* %[[V3]]) #2
|
||||
// CHECK: store i8* %[[V4]], i8** %[[ARRAYINIT_BEGIN]], align 8, !tbaa !7
|
||||
// CHECK: %[[ARRAYINIT_ELEMENT:.*]] = getelementptr inbounds i8*, i8** %[[ARRAYINIT_BEGIN]], i64 1
|
||||
// CHECK: %[[V5:.*]] = load i8*, i8** %[[B_ADDR]], align 8, !tbaa !7
|
||||
// CHECK: %[[V6:.*]] = call i8* @llvm.objc.retain(i8* %[[V5]]) #2
|
||||
// CHECK: store i8* %[[V6]], i8** %[[ARRAYINIT_ELEMENT]], align 8, !tbaa !7
|
||||
// CHECK: %[[ARRAY_BEGIN:.*]] = getelementptr inbounds [2 x i8*], [2 x i8*]* %[[T]], i32 0, i32 0
|
||||
// CHECK: %[[V7:.*]] = getelementptr inbounds i8*, i8** %[[ARRAY_BEGIN]], i64 2
|
||||
|
||||
// CHECK-NOT: call void @llvm.objc.release
|
||||
|
||||
// CHECK: %[[ARRAYDESTROY_ELEMENTPAST:.*]] = phi i8** [ %[[V7]], %{{.*}} ], [ %[[ARRAYDESTROY_ELEMENT:.*]], %{{.*}} ]
|
||||
// CHECK: %[[ARRAYDESTROY_ELEMENT]] = getelementptr inbounds i8*, i8** %[[ARRAYDESTROY_ELEMENTPAST]], i64 -1
|
||||
// CHECK: %[[V8:.*]] = load i8*, i8** %[[ARRAYDESTROY_ELEMENT]], align 8
|
||||
// CHECK: call void @llvm.objc.release(i8* %[[V8]]) #2, !clang.imprecise_release !10
|
||||
|
||||
// CHECK-NOT: call void @llvm.objc.release
|
||||
|
||||
// CHECK: %[[V10:.*]] = load i8*, i8** %[[B_ADDR]], align 8
|
||||
// CHECK: call void @llvm.objc.release(i8* %[[V10]]) #2, !clang.imprecise_release !10
|
||||
// CHECK: %[[V11:.*]] = load i8*, i8** %[[A_ADDR]], align 8
|
||||
// CHECK: call void @llvm.objc.release(i8* %[[V11]]) #2, !clang.imprecise_release !10
|
||||
|
||||
void test72(id a, id b) {
|
||||
__strong id t[] = (__strong id[]){a, b};
|
||||
}
|
||||
|
||||
// ARC-ALIEN: attributes [[NLB]] = { nonlazybind }
|
||||
// ARC-NATIVE: attributes [[NLB]] = { nonlazybind }
|
||||
// CHECK: attributes [[NUW]] = { nounwind }
|
||||
|
|
|
@ -709,4 +709,103 @@ void test_copy_constructor_VolatileArray(VolatileArray *a) {
|
|||
VolatileArray t = *a;
|
||||
}
|
||||
|
||||
// CHECK: define void @test_compound_literal0(
|
||||
// CHECK: %[[P:.*]] = alloca %[[STRUCT_STRONGSMALL]]*, align 8
|
||||
// CHECK: %[[_COMPOUNDLITERAL:.*]] = alloca %[[STRUCT_STRONGSMALL]], align 8
|
||||
// CHECK: %[[CLEANUP_COND:.*]] = alloca i1, align 1
|
||||
// CHECK: %[[_COMPOUNDLITERAL1:.*]] = alloca %[[STRUCT_STRONGSMALL]], align 8
|
||||
// CHECK: %[[CLEANUP_COND4:.*]] = alloca i1, align 1
|
||||
|
||||
// CHECK: %[[I:.*]] = getelementptr inbounds %[[STRUCT_STRONGSMALL]], %[[STRUCT_STRONGSMALL]]* %[[_COMPOUNDLITERAL]], i32 0, i32 0
|
||||
// CHECK: store i32 1, i32* %[[I]], align 8
|
||||
// CHECK: %[[F1:.*]] = getelementptr inbounds %[[STRUCT_STRONGSMALL]], %[[STRUCT_STRONGSMALL]]* %[[_COMPOUNDLITERAL]], i32 0, i32 1
|
||||
// CHECK: store i8* null, i8** %[[F1]], align 8
|
||||
// CHECK: store i1 true, i1* %[[CLEANUP_COND]], align 1
|
||||
|
||||
// CHECK: %[[I2:.*]] = getelementptr inbounds %[[STRUCT_STRONGSMALL]], %[[STRUCT_STRONGSMALL]]* %[[_COMPOUNDLITERAL1]], i32 0, i32 0
|
||||
// CHECK: store i32 2, i32* %[[I2]], align 8
|
||||
// CHECK: %[[F13:.*]] = getelementptr inbounds %[[STRUCT_STRONGSMALL]], %[[STRUCT_STRONGSMALL]]* %[[_COMPOUNDLITERAL1]], i32 0, i32 1
|
||||
// CHECK: store i8* null, i8** %[[F13]], align 8
|
||||
// CHECK: store i1 true, i1* %[[CLEANUP_COND4]], align 1
|
||||
|
||||
// CHECK: %[[COND:.*]] = phi %[[STRUCT_STRONGSMALL]]* [ %[[_COMPOUNDLITERAL]], %{{.*}} ], [ %[[_COMPOUNDLITERAL1]], %{{.*}} ]
|
||||
// CHECK: store %[[STRUCT_STRONGSMALL]]* %[[COND]], %[[STRUCT_STRONGSMALL]]** %[[P]], align 8
|
||||
// CHECK: call void @func(
|
||||
|
||||
// CHECK: %[[V1:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[_COMPOUNDLITERAL1]] to i8**
|
||||
// CHECK: call void @__destructor_8_s8(i8** %[[V1]])
|
||||
|
||||
// CHECK: %[[V2:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[_COMPOUNDLITERAL]] to i8**
|
||||
// CHECK: call void @__destructor_8_s8(i8** %[[V2]])
|
||||
|
||||
void test_compound_literal0(int c) {
|
||||
StrongSmall *p = c ? &(StrongSmall){ 1, 0 } : &(StrongSmall){ 2, 0 };
|
||||
func(0);
|
||||
}
|
||||
|
||||
// Check that there is only one destructor call, which destructs 't'.
|
||||
|
||||
// CHECK: define void @test_compound_literal1(
|
||||
// CHECK: %[[T:.*]] = alloca %[[STRUCT_STRONGSMALL]], align 8
|
||||
|
||||
// CHECK: %[[I:.*]] = getelementptr inbounds %[[STRUCT_STRONGSMALL]], %[[STRUCT_STRONGSMALL]]* %[[T]], i32 0, i32 0
|
||||
// CHECK: store i32 1, i32* %[[I]], align 8
|
||||
// CHECK: %[[F1:.*]] = getelementptr inbounds %[[STRUCT_STRONGSMALL]], %[[STRUCT_STRONGSMALL]]* %[[T]], i32 0, i32 1
|
||||
// CHECK: store i8* null, i8** %[[F1]], align 8
|
||||
|
||||
// CHECK: %[[I1:.*]] = getelementptr inbounds %[[STRUCT_STRONGSMALL]], %[[STRUCT_STRONGSMALL]]* %[[T]], i32 0, i32 0
|
||||
// CHECK: store i32 2, i32* %[[I1]], align 8
|
||||
// CHECK: %[[F12:.*]] = getelementptr inbounds %[[STRUCT_STRONGSMALL]], %[[STRUCT_STRONGSMALL]]* %[[T]], i32 0, i32 1
|
||||
// CHECK: store i8* null, i8** %[[F12]], align 8
|
||||
|
||||
// CHECK: call void @func(
|
||||
// CHECK-NOT: call void
|
||||
// CHECK: %[[V1:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[T]] to i8**
|
||||
// CHECK: call void @__destructor_8_s8(i8** %[[V1]])
|
||||
// CHECK-NOT: call void
|
||||
|
||||
void test_compound_literal1(int c) {
|
||||
StrongSmall t = c ? (StrongSmall){ 1, 0 } : (StrongSmall){ 2, 0 };
|
||||
func(0);
|
||||
}
|
||||
|
||||
// CHECK: define void @test_compound_literal2(
|
||||
// CHECK: %[[P_ADDR:.*]] = alloca %[[STRUCT_STRONGSMALL]]*, align 8
|
||||
// CHECK: %[[_COMPOUNDLITERAL:.*]] = alloca %[[STRUCT_STRONGSMALL]], align 8
|
||||
// CHECK: %[[CLEANUP_COND:.*]] = alloca i1, align 1
|
||||
// CHECK: %[[_COMPOUNDLITERAL1:.*]] = alloca %[[STRUCT_STRONGSMALL]], align 8
|
||||
// CHECK: %[[CLEANUP_COND4:.*]] = alloca i1, align 1
|
||||
// CHECK: %[[V0:.*]] = load %[[STRUCT_STRONGSMALL]]*, %[[STRUCT_STRONGSMALL]]** %[[P_ADDR]], align 8
|
||||
|
||||
// CHECK: %[[I:.*]] = getelementptr inbounds %[[STRUCT_STRONGSMALL]], %[[STRUCT_STRONGSMALL]]* %[[_COMPOUNDLITERAL]], i32 0, i32 0
|
||||
// CHECK: store i32 1, i32* %[[I]], align 8
|
||||
// CHECK: %[[F1:.*]] = getelementptr inbounds %[[STRUCT_STRONGSMALL]], %[[STRUCT_STRONGSMALL]]* %[[_COMPOUNDLITERAL]], i32 0, i32 1
|
||||
// CHECK: store i8* null, i8** %[[F1]], align 8
|
||||
// CHECK: store i1 true, i1* %[[CLEANUP_COND]], align 1
|
||||
// CHECK: %[[V2:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[V0]] to i8**
|
||||
// CHECK: %[[V3:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[_COMPOUNDLITERAL]] to i8**
|
||||
// CHECK: call void @__copy_assignment_8_8_t0w4_s8(i8** %[[V2]], i8** %[[V3]])
|
||||
|
||||
// CHECK: %[[I2:.*]] = getelementptr inbounds %[[STRUCT_STRONGSMALL]], %[[STRUCT_STRONGSMALL]]* %[[_COMPOUNDLITERAL1]], i32 0, i32 0
|
||||
// CHECK: store i32 2, i32* %[[I2]], align 8
|
||||
// CHECK: %[[F13:.*]] = getelementptr inbounds %[[STRUCT_STRONGSMALL]], %[[STRUCT_STRONGSMALL]]* %[[_COMPOUNDLITERAL1]], i32 0, i32 1
|
||||
// CHECK: store i8* null, i8** %[[F13]], align 8
|
||||
// CHECK: store i1 true, i1* %[[CLEANUP_COND4]], align 1
|
||||
// CHECK: %[[V4:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[V0]] to i8**
|
||||
// CHECK: %[[V5:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[_COMPOUNDLITERAL1]] to i8**
|
||||
// CHECK: call void @__copy_assignment_8_8_t0w4_s8(i8** %[[V4]], i8** %[[V5]])
|
||||
|
||||
// CHECK: call void @func(
|
||||
|
||||
// CHECK: %[[V6:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[_COMPOUNDLITERAL1]] to i8**
|
||||
// CHECK: call void @__destructor_8_s8(i8** %[[V6]])
|
||||
|
||||
// CHECK: %[[V7:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[_COMPOUNDLITERAL]] to i8**
|
||||
// CHECK: call void @__destructor_8_s8(i8** %[[V7]])
|
||||
|
||||
void test_compound_literal2(int c, StrongSmall *p) {
|
||||
*p = c ? (StrongSmall){ 1, 0 } : (StrongSmall){ 2, 0 };
|
||||
func(0);
|
||||
}
|
||||
|
||||
#endif /* USESTRUCT */
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
typedef struct {
|
||||
id x;
|
||||
} S;
|
||||
|
||||
id getObj(int c, id a) {
|
||||
// Commenting out the following line because AST importer crashes when trying
|
||||
// to import a BlockExpr.
|
||||
// return c ? ^{ return a; }() : ((S){ .x = a }).x;
|
||||
return ((S){ .x = a }).x;
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
// RUN: clang-import-test -x objective-c -objc-arc -import %S/Inputs/cleanup-objects.m -dump-ast -expression %s | FileCheck %s
|
||||
|
||||
// CHECK: FunctionDecl {{.*}} getObj '
|
||||
// CHECK: ExprWithCleanups
|
||||
// CHECK-NEXT: cleanup CompoundLiteralExpr
|
||||
|
||||
void test(int c, id a) {
|
||||
(void)getObj(c, a);
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -x objective-c -fobjc-arc -emit-pch -o %t %s
|
||||
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -x objective-c -fobjc-arc -include-pch %t -emit-llvm -o - %s | FileCheck %s
|
||||
|
||||
#ifndef HEADER
|
||||
#define HEADER
|
||||
|
||||
typedef struct {
|
||||
id f;
|
||||
} S;
|
||||
|
||||
static inline id getObj(id a) {
|
||||
S *p = &(S){ .f = a };
|
||||
return p->f;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
// CHECK: %[[STRUCT_S:.*]] = type { i8* }
|
||||
|
||||
// CHECK: define internal i8* @getObj(
|
||||
// CHECK: %[[_COMPOUNDLITERAL:.*]] = alloca %[[STRUCT_S]],
|
||||
// CHECK: %[[V5:.*]] = bitcast %[[STRUCT_S]]* %[[_COMPOUNDLITERAL]] to i8**
|
||||
// CHECK: call void @__destructor_8_s0(i8** %[[V5]])
|
||||
|
||||
id test(id a) {
|
||||
return getObj(a);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -54,3 +54,21 @@ L0: // expected-note {{possible target of indirect goto}}
|
|||
func(^{ func2(x); });
|
||||
goto *ips; // expected-error {{cannot jump}}
|
||||
}
|
||||
|
||||
void test_compound_literal0(int cond, id x) {
|
||||
switch (cond) {
|
||||
case 0:
|
||||
(void)(Strong){ .a = x }; // expected-note {{jump enters lifetime of a compound literal that is non-trivial to destruct}}
|
||||
break;
|
||||
default: // expected-error {{cannot jump from switch statement to this case label}}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void test_compound_literal1(id x) {
|
||||
static void *ips[] = { &&L0 };
|
||||
L0: // expected-note {{possible target of indirect goto}}
|
||||
;
|
||||
(void)(Strong){ .a = x }; // expected-note {{jump exits lifetime of a compound literal that is non-trivial to destruct}}
|
||||
goto *ips; // expected-error {{cannot jump}}
|
||||
}
|
||||
|
|
|
@ -64,6 +64,9 @@ static llvm::cl::opt<std::string>
|
|||
llvm::cl::desc("The language to parse (default: c++)"),
|
||||
llvm::cl::init("c++"));
|
||||
|
||||
static llvm::cl::opt<bool> ObjCARC("objc-arc", llvm::cl::init(false),
|
||||
llvm::cl::desc("Emable ObjC ARC"));
|
||||
|
||||
static llvm::cl::opt<bool> DumpAST("dump-ast", llvm::cl::init(false),
|
||||
llvm::cl::desc("Dump combined AST"));
|
||||
|
||||
|
@ -183,6 +186,8 @@ std::unique_ptr<CompilerInstance> BuildCompilerInstance() {
|
|||
Inv->getLangOpts()->ObjC = 1;
|
||||
}
|
||||
}
|
||||
Inv->getLangOpts()->ObjCAutoRefCount = ObjCARC;
|
||||
|
||||
Inv->getLangOpts()->Bool = true;
|
||||
Inv->getLangOpts()->WChar = true;
|
||||
Inv->getLangOpts()->Blocks = true;
|
||||
|
|
Loading…
Reference in New Issue