forked from OSchip/llvm-project
Update NRVO logic to support early return (Attempt 2)
Summary: This is the second attempt of r333500 (Update NRVO logic to support early return). The previous one was reverted for a miscompilation for an incorrect NRVO set up on templates such as: ``` struct Foo {}; template <typename T> T bar() { T t; if (false) return T(); return t; } ``` Where, `t` is marked as non-NRVO variable before its instantiation. However, while its instantiation, it's left an NRVO candidate, turned into an NRVO variable later. Reviewers: rsmith Reviewed By: rsmith Subscribers: cfe-commits Differential Revision: https://reviews.llvm.org/D47586 llvm-svn: 335019
This commit is contained in:
parent
c2965214ef
commit
b000a8860e
|
@ -879,6 +879,12 @@ protected:
|
|||
DAK_Normal
|
||||
};
|
||||
|
||||
enum NRVOMode {
|
||||
NRVO_Candidate,
|
||||
NRVO_Disabled,
|
||||
NRVO_Enabled,
|
||||
};
|
||||
|
||||
class ParmVarDeclBitfields {
|
||||
friend class ASTDeclReader;
|
||||
friend class ParmVarDecl;
|
||||
|
@ -931,7 +937,7 @@ protected:
|
|||
/// Whether this local variable could be allocated in the return
|
||||
/// slot of its function, enabling the named return value optimization
|
||||
/// (NRVO).
|
||||
unsigned NRVOVariable : 1;
|
||||
unsigned NRVOMode : 2;
|
||||
|
||||
/// Whether this variable is the for-range-declaration in a C++0x
|
||||
/// for-range statement.
|
||||
|
@ -1319,12 +1325,20 @@ public:
|
|||
/// return slot when returning from the function. Within the function body,
|
||||
/// each return that returns the NRVO object will have this variable as its
|
||||
/// NRVO candidate.
|
||||
NRVOMode getNRVOMode() const {
|
||||
if (isa<ParmVarDecl>(this))
|
||||
return NRVO_Disabled;
|
||||
return static_cast<NRVOMode>(NonParmVarDeclBits.NRVOMode);
|
||||
}
|
||||
bool isNRVOCandidate() const {
|
||||
return isa<ParmVarDecl>(this) ? false : NonParmVarDeclBits.NRVOMode == NRVO_Candidate;
|
||||
}
|
||||
bool isNRVOVariable() const {
|
||||
return isa<ParmVarDecl>(this) ? false : NonParmVarDeclBits.NRVOVariable;
|
||||
return isa<ParmVarDecl>(this) ? false : NonParmVarDeclBits.NRVOMode == NRVO_Enabled;
|
||||
}
|
||||
void setNRVOVariable(bool NRVO) {
|
||||
assert(!isa<ParmVarDecl>(this));
|
||||
NonParmVarDeclBits.NRVOVariable = NRVO;
|
||||
NonParmVarDeclBits.NRVOMode = NRVO ? NRVO_Enabled : NRVO_Disabled;
|
||||
}
|
||||
|
||||
/// Determine whether this variable is the for-range-declaration in
|
||||
|
|
|
@ -201,10 +201,6 @@ private:
|
|||
/// Used to determine if errors occurred in this scope.
|
||||
DiagnosticErrorTrap ErrorTrap;
|
||||
|
||||
/// A lattice consisting of undefined, a single NRVO candidate variable in
|
||||
/// this scope, or over-defined. The bit is true when over-defined.
|
||||
llvm::PointerIntPair<VarDecl *, 1, bool> NRVO;
|
||||
|
||||
void setFlags(Scope *Parent, unsigned F);
|
||||
|
||||
public:
|
||||
|
@ -466,23 +462,7 @@ public:
|
|||
UsingDirectives.end());
|
||||
}
|
||||
|
||||
void addNRVOCandidate(VarDecl *VD) {
|
||||
if (NRVO.getInt())
|
||||
return;
|
||||
if (NRVO.getPointer() == nullptr) {
|
||||
NRVO.setPointer(VD);
|
||||
return;
|
||||
}
|
||||
if (NRVO.getPointer() != VD)
|
||||
setNoNRVO();
|
||||
}
|
||||
|
||||
void setNoNRVO() {
|
||||
NRVO.setInt(true);
|
||||
NRVO.setPointer(nullptr);
|
||||
}
|
||||
|
||||
void mergeNRVOIntoParent();
|
||||
void setNRVOCandidate(VarDecl *Candidate);
|
||||
|
||||
/// Init - This is used by the parser to implement scope caching.
|
||||
void Init(Scope *parent, unsigned flags);
|
||||
|
|
|
@ -92,7 +92,6 @@ void Scope::Init(Scope *parent, unsigned flags) {
|
|||
UsingDirectives.clear();
|
||||
Entity = nullptr;
|
||||
ErrorTrap.reset();
|
||||
NRVO.setPointerAndInt(nullptr, 0);
|
||||
}
|
||||
|
||||
bool Scope::containedInPrototypeScope() const {
|
||||
|
@ -119,19 +118,15 @@ void Scope::AddFlags(unsigned FlagsToSet) {
|
|||
Flags |= FlagsToSet;
|
||||
}
|
||||
|
||||
void Scope::mergeNRVOIntoParent() {
|
||||
if (VarDecl *Candidate = NRVO.getPointer()) {
|
||||
if (isDeclScope(Candidate))
|
||||
Candidate->setNRVOVariable(true);
|
||||
void Scope::setNRVOCandidate(VarDecl *Candidate) {
|
||||
for (Decl *D : DeclsInScope) {
|
||||
VarDecl *VD = dyn_cast<VarDecl>(D);
|
||||
if (VD && VD != Candidate && VD->isNRVOCandidate())
|
||||
VD->setNRVOVariable(false);
|
||||
}
|
||||
|
||||
if (getEntity())
|
||||
return;
|
||||
|
||||
if (NRVO.getInt())
|
||||
getParent()->setNoNRVO();
|
||||
else if (NRVO.getPointer())
|
||||
getParent()->addNRVOCandidate(NRVO.getPointer());
|
||||
if (Scope *parent = getParent())
|
||||
parent->setNRVOCandidate(Candidate);
|
||||
}
|
||||
|
||||
LLVM_DUMP_METHOD void Scope::dump() const { dumpImpl(llvm::errs()); }
|
||||
|
@ -191,9 +186,4 @@ void Scope::dumpImpl(raw_ostream &OS) const {
|
|||
OS << "MSCurManglingNumber: " << getMSCurManglingNumber() << '\n';
|
||||
if (const DeclContext *DC = getEntity())
|
||||
OS << "Entity : (clang::DeclContext*)" << DC << '\n';
|
||||
|
||||
if (NRVO.getInt())
|
||||
OS << "NRVO not allowed\n";
|
||||
else if (NRVO.getPointer())
|
||||
OS << "NRVO candidate : (clang::VarDecl*)" << NRVO.getPointer() << '\n';
|
||||
}
|
||||
|
|
|
@ -1798,8 +1798,6 @@ static void CheckPoppedLabel(LabelDecl *L, Sema &S) {
|
|||
}
|
||||
|
||||
void Sema::ActOnPopScope(SourceLocation Loc, Scope *S) {
|
||||
S->mergeNRVOIntoParent();
|
||||
|
||||
if (S->decl_empty()) return;
|
||||
assert((S->getFlags() & (Scope::DeclScope | Scope::TemplateParamScope)) &&
|
||||
"Scope shouldn't contain decls!");
|
||||
|
@ -12563,21 +12561,24 @@ Decl *Sema::ActOnStartOfFunctionDef(Scope *FnBodyScope, Decl *D,
|
|||
/// optimization.
|
||||
///
|
||||
/// Each of the variables that is subject to the named return value
|
||||
/// optimization will be marked as NRVO variables in the AST, and any
|
||||
/// optimization will be marked as NRVO variable candidates in the AST, and any
|
||||
/// return statement that has a marked NRVO variable as its NRVO candidate can
|
||||
/// use the named return value optimization.
|
||||
///
|
||||
/// This function applies a very simplistic algorithm for NRVO: if every return
|
||||
/// statement in the scope of a variable has the same NRVO candidate, that
|
||||
/// candidate is an NRVO variable.
|
||||
/// This function applies a very simplistic algorithm for NRVO: if every
|
||||
/// reachable return statement in the scope of a variable has the same NRVO
|
||||
/// candidate, that candidate is an NRVO variable.
|
||||
void Sema::computeNRVO(Stmt *Body, FunctionScopeInfo *Scope) {
|
||||
ReturnStmt **Returns = Scope->Returns.data();
|
||||
for (ReturnStmt *Return : Scope->Returns) {
|
||||
const VarDecl *Candidate = Return->getNRVOCandidate();
|
||||
if (!Candidate)
|
||||
continue;
|
||||
|
||||
for (unsigned I = 0, E = Scope->Returns.size(); I != E; ++I) {
|
||||
if (const VarDecl *NRVOCandidate = Returns[I]->getNRVOCandidate()) {
|
||||
if (!NRVOCandidate->isNRVOVariable())
|
||||
Returns[I]->setNRVOCandidate(nullptr);
|
||||
}
|
||||
if (Candidate->isNRVOCandidate())
|
||||
const_cast<VarDecl*>(Candidate)->setNRVOVariable(true);
|
||||
|
||||
if (!Candidate->isNRVOVariable())
|
||||
Return->setNRVOCandidate(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -12712,12 +12713,8 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body,
|
|||
else if (CXXDestructorDecl *Destructor = dyn_cast<CXXDestructorDecl>(FD))
|
||||
MarkVTableUsed(FD->getLocation(), Destructor->getParent());
|
||||
|
||||
// Try to apply the named return value optimization. We have to check
|
||||
// if we can do this here because lambdas keep return statements around
|
||||
// to deduce an implicit return type.
|
||||
if (FD->getReturnType()->isRecordType() &&
|
||||
(!getLangOpts().CPlusPlus || !FD->isDependentContext()))
|
||||
computeNRVO(Body, getCurFunction());
|
||||
// Try to apply the named return value optimization.
|
||||
computeNRVO(Body, getCurFunction());
|
||||
}
|
||||
|
||||
// GNU warning -Wmissing-prototypes:
|
||||
|
|
|
@ -13385,13 +13385,9 @@ ExprResult Sema::ActOnBlockStmtExpr(SourceLocation CaretLoc,
|
|||
if (Body && getCurFunction()->HasPotentialAvailabilityViolations)
|
||||
DiagnoseUnguardedAvailabilityViolations(BSI->TheDecl);
|
||||
|
||||
// Try to apply the named return value optimization. We have to check again
|
||||
// if we can do this, though, because blocks keep return statements around
|
||||
// to deduce an implicit return type.
|
||||
if (getLangOpts().CPlusPlus && RetTy->isRecordType() &&
|
||||
!BSI->TheDecl->isDependentContext())
|
||||
computeNRVO(Body, BSI);
|
||||
|
||||
// Try to apply the named return value optimization.
|
||||
computeNRVO(Body, BSI);
|
||||
|
||||
BlockExpr *Result = new (Context) BlockExpr(BSI->TheDecl, BlockTy);
|
||||
AnalysisBasedWarnings::Policy WP = AnalysisWarnings.getDefaultPolicy();
|
||||
PopFunctionScopeInfo(&WP, Result->getBlockDecl(), Result);
|
||||
|
|
|
@ -3455,12 +3455,9 @@ Sema::ActOnReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp,
|
|||
ExpressionEvaluationContext::DiscardedStatement)
|
||||
return R;
|
||||
|
||||
if (VarDecl *VD =
|
||||
const_cast<VarDecl*>(cast<ReturnStmt>(R.get())->getNRVOCandidate())) {
|
||||
CurScope->addNRVOCandidate(VD);
|
||||
} else {
|
||||
CurScope->setNoNRVO();
|
||||
}
|
||||
VarDecl *VD =
|
||||
const_cast<VarDecl*>(cast<ReturnStmt>(R.get())->getNRVOCandidate());
|
||||
CurScope->setNRVOCandidate(VD);
|
||||
|
||||
CheckJumpOutOfSEHFinally(*this, ReturnLoc, *CurScope->getFnParent());
|
||||
|
||||
|
|
|
@ -740,12 +740,13 @@ Decl *TemplateDeclInstantiator::VisitVarDecl(VarDecl *D,
|
|||
SemaRef.BuildVariableInstantiation(Var, D, TemplateArgs, LateAttrs, Owner,
|
||||
StartingScope, InstantiatingVarTemplate);
|
||||
|
||||
bool NRVO = false;
|
||||
if (D->isNRVOVariable()) {
|
||||
QualType ReturnType = cast<FunctionDecl>(DC)->getReturnType();
|
||||
if (SemaRef.isCopyElisionCandidate(ReturnType, Var, Sema::CES_Strict))
|
||||
Var->setNRVOVariable(true);
|
||||
NRVO = true;
|
||||
}
|
||||
|
||||
Var->setNRVOVariable(NRVO);
|
||||
Var->setImplicit(D->isImplicit());
|
||||
|
||||
return Var;
|
||||
|
|
|
@ -1326,7 +1326,7 @@ ASTDeclReader::RedeclarableResult ASTDeclReader::VisitVarDeclImpl(VarDecl *VD) {
|
|||
VD->NonParmVarDeclBits.IsThisDeclarationADemotedDefinition =
|
||||
Record.readInt();
|
||||
VD->NonParmVarDeclBits.ExceptionVar = Record.readInt();
|
||||
VD->NonParmVarDeclBits.NRVOVariable = Record.readInt();
|
||||
VD->NonParmVarDeclBits.NRVOMode = Record.readInt();
|
||||
VD->NonParmVarDeclBits.CXXForRangeDecl = Record.readInt();
|
||||
VD->NonParmVarDeclBits.ObjCForDecl = Record.readInt();
|
||||
VD->NonParmVarDeclBits.ARCPseudoStrong = Record.readInt();
|
||||
|
|
|
@ -918,7 +918,7 @@ void ASTDeclWriter::VisitVarDecl(VarDecl *D) {
|
|||
if (!isa<ParmVarDecl>(D)) {
|
||||
Record.push_back(D->isThisDeclarationADemotedDefinition());
|
||||
Record.push_back(D->isExceptionVariable());
|
||||
Record.push_back(D->isNRVOVariable());
|
||||
Record.push_back(D->getNRVOMode());
|
||||
Record.push_back(D->isCXXForRangeDecl());
|
||||
Record.push_back(D->isObjCForDecl());
|
||||
Record.push_back(D->isARCPseudoStrong());
|
||||
|
@ -2031,7 +2031,7 @@ void ASTWriter::WriteDeclAbbrevs() {
|
|||
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 2)); // InitStyle
|
||||
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // IsThisDeclarationADemotedDefinition
|
||||
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // isExceptionVariable
|
||||
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // isNRVOVariable
|
||||
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 2)); // NRVOMode
|
||||
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // isCXXForRangeDecl
|
||||
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // isObjCForDecl
|
||||
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // isARCPseudoStrong
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
// RUN: %clang_cc1 -emit-llvm -o - %s | FileCheck %s
|
||||
|
||||
struct X {
|
||||
X();
|
||||
X(X&&);
|
||||
};
|
||||
|
||||
// CHECK-LABEL: define void @_Z7test_00b
|
||||
X test_00(bool b) {
|
||||
if (b) {
|
||||
// CHECK-NOT: call void @_ZN1XC1EOS_
|
||||
// CHECK: call void @_ZN1XC1Ev
|
||||
// CHECK-NEXT: br label %return
|
||||
X x;
|
||||
return x;
|
||||
} else {
|
||||
// CHECK-NOT: call void @_ZN1XC1EOS_
|
||||
// CHECK: call void @_ZN1XC1Ev
|
||||
// CHECK-NEXT: br label %return
|
||||
X x;
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
// CHECK-LABEL: define void @_Z7test_01b
|
||||
X test_01(bool b) {
|
||||
if (b) {
|
||||
// CHECK-NOT: call void @_ZN1XC1EOS_
|
||||
// CHECK: call void @_ZN1XC1Ev
|
||||
// CHECK-NEXT: br label %return
|
||||
X x;
|
||||
return x;
|
||||
}
|
||||
// CHECK-NOT: call void @_ZN1XC1EOS_
|
||||
// CHECK: call void @_ZN1XC1Ev
|
||||
// CHECK-NEXT: br label %return
|
||||
X x;
|
||||
return x;
|
||||
}
|
||||
|
||||
// CHECK-LABEL: define void @_Z7test_02b
|
||||
X test_02(bool b) {
|
||||
// CHECK: call void @_ZN1XC1Ev
|
||||
X x;
|
||||
|
||||
if (b) {
|
||||
// CHECK-NOT: call void @_ZN1XC1EOS_
|
||||
// CHECK: call void @_ZN1XC1Ev
|
||||
// CHECK-NEXT: br label %return
|
||||
X y;
|
||||
return y;
|
||||
}
|
||||
|
||||
// CHECK-NOT: call void @_ZN1XC1Ev
|
||||
// CHECK: call void @_ZN1XC1EOS_
|
||||
// CHECK-NEXT: br label %return
|
||||
return x;
|
||||
}
|
|
@ -130,17 +130,13 @@ X test2(bool B) {
|
|||
}
|
||||
|
||||
// CHECK-LABEL: define void @_Z5test3b
|
||||
X test3(bool B) {
|
||||
X test3(bool B, X x) {
|
||||
// CHECK: tail call {{.*}} @_ZN1XC1Ev
|
||||
// CHECK-NOT: call {{.*}} @_ZN1XC1ERKS_
|
||||
// CHECK: call {{.*}} @_ZN1XC1Ev
|
||||
// CHECK: call {{.*}} @_ZN1XC1ERKS_
|
||||
if (B) {
|
||||
X y;
|
||||
return y;
|
||||
}
|
||||
// FIXME: we should NRVO this variable too.
|
||||
X x;
|
||||
// CHECK: tail call {{.*}} @_ZN1XC1ERKS_
|
||||
return x;
|
||||
}
|
||||
|
||||
|
@ -191,9 +187,13 @@ X test6() {
|
|||
}
|
||||
|
||||
// CHECK-LABEL: define void @_Z5test7b
|
||||
// CHECK-EH-LABEL: define void @_Z5test7b
|
||||
X test7(bool b) {
|
||||
// CHECK: tail call {{.*}} @_ZN1XC1Ev
|
||||
// CHECK-NEXT: ret
|
||||
|
||||
// CHECK-EH: tail call {{.*}} @_ZN1XC1Ev
|
||||
// CHECK-EH-NEXT: ret
|
||||
if (b) {
|
||||
X x;
|
||||
return x;
|
||||
|
@ -202,10 +202,14 @@ X test7(bool b) {
|
|||
}
|
||||
|
||||
// CHECK-LABEL: define void @_Z5test8b
|
||||
// CHECK-EH-LABEL: define void @_Z5test8b
|
||||
X test8(bool b) {
|
||||
// CHECK: tail call {{.*}} @_ZN1XC1Ev
|
||||
// CHECK-NEXT: ret
|
||||
if (b) {
|
||||
|
||||
// CHECK-EH: tail call {{.*}} @_ZN1XC1Ev
|
||||
// CHECK-EH-NEXT: ret
|
||||
if (b) {
|
||||
X x;
|
||||
return x;
|
||||
} else {
|
||||
|
@ -221,4 +225,37 @@ Y<int> test9() {
|
|||
// CHECK-LABEL: define linkonce_odr void @_ZN1YIiE1fEv
|
||||
// CHECK: tail call {{.*}} @_ZN1YIiEC1Ev
|
||||
|
||||
// CHECK-LABEL: define void @_Z6test10b
|
||||
X test10(bool B, X x) {
|
||||
if (B) {
|
||||
// CHECK: tail call {{.*}} @_ZN1XC1ERKS_
|
||||
// CHECK-EH: tail call {{.*}} @_ZN1XC1ERKS_
|
||||
return x;
|
||||
}
|
||||
// CHECK: tail call {{.*}} @_ZN1XC1Ev
|
||||
// CHECK-NOT: call {{.*}} @_ZN1XC1ERKS_
|
||||
|
||||
// CHECK-EH: tail call {{.*}} @_ZN1XC1Ev
|
||||
// CHECK-EH-NOT: call {{.*}} @_ZN1XC1ERKS_
|
||||
X y;
|
||||
return y;
|
||||
}
|
||||
|
||||
// CHECK-LABEL: define {{.*}} void @_Z6test11I1XET_v
|
||||
// CHECK-EH-LABEL: define {{.*}} void @_Z6test11I1XET_v
|
||||
template <typename T>
|
||||
T test11() {
|
||||
// CHECK: tail call {{.*}} @_ZN1XC1Ev
|
||||
// CHECK-NEXT: ret void
|
||||
|
||||
// CHECK-EH: tail call {{.*}} @_ZN1XC1Ev
|
||||
// CHECK-EH-NEXT: ret void
|
||||
T t;
|
||||
return t;
|
||||
}
|
||||
|
||||
void test12() {
|
||||
test11<X>();
|
||||
}
|
||||
|
||||
// CHECK-EH-03: attributes [[NR_NUW]] = { noreturn nounwind }
|
||||
|
|
|
@ -0,0 +1,153 @@
|
|||
// RUN: %clang_cc1 -fcxx-exceptions -fsyntax-only -ast-dump -o - %s | FileCheck %s
|
||||
|
||||
struct X {
|
||||
X();
|
||||
X(const X&);
|
||||
X(X&&);
|
||||
};
|
||||
|
||||
// CHECK-LABEL: FunctionDecl {{.*}} test_00
|
||||
X test_00() {
|
||||
// CHECK: VarDecl {{.*}} x {{.*}} nrvo
|
||||
X x;
|
||||
return x;
|
||||
}
|
||||
|
||||
// CHECK-LABEL: FunctionDecl {{.*}} test_01
|
||||
X test_01(bool b) {
|
||||
// CHECK: VarDecl {{.*}} x {{.*}} nrvo
|
||||
X x;
|
||||
if (b)
|
||||
return x;
|
||||
return x;
|
||||
}
|
||||
|
||||
// CHECK-LABEL: FunctionDecl {{.*}} test_02
|
||||
X test_02(bool b) {
|
||||
// CHECK-NOT: VarDecl {{.*}} x {{.*}} nrvo
|
||||
X x;
|
||||
// CHECK-NOT: VarDecl {{.*}} y {{.*}} nrvo
|
||||
X y;
|
||||
if (b)
|
||||
return y;
|
||||
return x;
|
||||
}
|
||||
|
||||
// CHECK-LABEL: FunctionDecl {{.*}} test_03
|
||||
X test_03(bool b) {
|
||||
if (b) {
|
||||
// CHECK: VarDecl {{.*}} y {{.*}} nrvo
|
||||
X y;
|
||||
return y;
|
||||
}
|
||||
// CHECK: VarDecl {{.*}} x {{.*}} nrvo
|
||||
X x;
|
||||
return x;
|
||||
}
|
||||
|
||||
extern "C" _Noreturn void exit(int) throw();
|
||||
|
||||
// CHECK-LABEL: FunctionDecl {{.*}} test_04
|
||||
X test_04(bool b) {
|
||||
{
|
||||
// CHECK: VarDecl {{.*}} x {{.*}} nrvo
|
||||
X x;
|
||||
if (b)
|
||||
return x;
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void may_throw();
|
||||
// CHECK-LABEL: FunctionDecl {{.*}} test_05
|
||||
X test_05() {
|
||||
try {
|
||||
may_throw();
|
||||
return X();
|
||||
} catch (X x) {
|
||||
// CHECK-NOT: VarDecl {{.*}} x {{.*}} nrvo
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
// CHECK-LABEL: FunctionDecl {{.*}} test_06
|
||||
X test_06() {
|
||||
// CHECK-NOT: VarDecl {{.*}} x {{.*}} nrvo
|
||||
X x __attribute__((aligned(8)));
|
||||
return x;
|
||||
}
|
||||
|
||||
// CHECK-LABEL: FunctionDecl {{.*}} test_07
|
||||
X test_07(bool b) {
|
||||
if (b) {
|
||||
// CHECK: VarDecl {{.*}} x {{.*}} nrvo
|
||||
X x;
|
||||
return x;
|
||||
}
|
||||
return X();
|
||||
}
|
||||
|
||||
// CHECK-LABEL: FunctionDecl {{.*}} test_08
|
||||
X test_08(bool b) {
|
||||
if (b) {
|
||||
// CHECK: VarDecl {{.*}} x {{.*}} nrvo
|
||||
X x;
|
||||
return x;
|
||||
} else {
|
||||
// CHECK: VarDecl {{.*}} y {{.*}} nrvo
|
||||
X y;
|
||||
return y;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct Y {
|
||||
Y();
|
||||
// CHECK-LABEL: CXXMethodDecl {{.*}} test_09 'Y<T> ()'
|
||||
// CHECK: VarDecl {{.*}} y 'Y<T>' nrvo
|
||||
|
||||
// CHECK-LABEL: CXXMethodDecl {{.*}} test_09 'Y<int> ()'
|
||||
// CHECK: VarDecl {{.*}} y 'Y<int>' nrvo
|
||||
static Y test_09() {
|
||||
Y y;
|
||||
return y;
|
||||
}
|
||||
};
|
||||
|
||||
struct Z {
|
||||
Z(const X&);
|
||||
};
|
||||
|
||||
// CHECK-LABEL: FunctionDecl {{.*}} test_10 'A ()'
|
||||
// CHECK: VarDecl {{.*}} b 'B' nrvo
|
||||
|
||||
// CHECK-LABEL: FunctionDecl {{.*}} test_10 'X ()'
|
||||
// CHECK: VarDecl {{.*}} b {{.*}} nrvo
|
||||
|
||||
// CHECK-LABEL: FunctionDecl {{.*}} test_10 'Z ()'
|
||||
// CHECK-NOT: VarDecl {{.*}} b {{.*}} nrvo
|
||||
template <typename A, typename B>
|
||||
A test_10() {
|
||||
B b;
|
||||
return b;
|
||||
}
|
||||
|
||||
// CHECK-LABEL: FunctionDecl {{.*}} test_11 'A (bool)'
|
||||
// CHECK-NOT: VarDecl {{.*}} a {{.*}} nrvo
|
||||
|
||||
// CHECK-LABEL: FunctionDecl {{.*}} test_11 'X (bool)'
|
||||
// CHECK-NOT: VarDecl {{.*}} a {{.*}} nrvo
|
||||
template <typename A>
|
||||
A test_11(bool b) {
|
||||
A a;
|
||||
if (b)
|
||||
return A();
|
||||
return a;
|
||||
}
|
||||
|
||||
void instantiate() {
|
||||
Y<int>::test_09();
|
||||
test_10<X, X>();
|
||||
test_10<Z, X>();
|
||||
test_11<X>(true);
|
||||
}
|
Loading…
Reference in New Issue