Skip to content

Commit 6fac21e

Browse files
authored
[clang][bytecode] Avoid copies with elidable CXXConstructExprs (#166931)
To fix the newly added cwg6.cpp.
1 parent 82b9216 commit 6fac21e

File tree

6 files changed

+77
-32
lines changed

6 files changed

+77
-32
lines changed

clang/lib/AST/ByteCode/Compiler.cpp

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3235,7 +3235,8 @@ bool Compiler<Emitter>::VisitCXXConstructExpr(const CXXConstructExpr *E) {
32353235
return this->visitInitializer(E->getArg(0));
32363236

32373237
// Zero initialization.
3238-
if (E->requiresZeroInitialization()) {
3238+
bool ZeroInit = E->requiresZeroInitialization();
3239+
if (ZeroInit) {
32393240
const Record *R = getRecord(E->getType());
32403241

32413242
if (!this->visitZeroRecordInitializer(R, E))
@@ -3246,6 +3247,19 @@ bool Compiler<Emitter>::VisitCXXConstructExpr(const CXXConstructExpr *E) {
32463247
return true;
32473248
}
32483249

3250+
// Avoid materializing a temporary for an elidable copy/move constructor.
3251+
if (!ZeroInit && E->isElidable()) {
3252+
const Expr *SrcObj = E->getArg(0);
3253+
assert(SrcObj->isTemporaryObject(Ctx.getASTContext(), Ctor->getParent()));
3254+
assert(Ctx.getASTContext().hasSameUnqualifiedType(E->getType(),
3255+
SrcObj->getType()));
3256+
if (const auto *ME = dyn_cast<MaterializeTemporaryExpr>(SrcObj)) {
3257+
if (!this->emitCheckFunctionDecl(Ctor, E))
3258+
return false;
3259+
return this->visitInitializer(ME->getSubExpr());
3260+
}
3261+
}
3262+
32493263
const Function *Func = getFunction(Ctor);
32503264

32513265
if (!Func)

clang/lib/AST/ByteCode/Interp.cpp

Lines changed: 49 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -919,45 +919,19 @@ bool CheckInit(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
919919
return true;
920920
}
921921

922-
static bool CheckCallable(InterpState &S, CodePtr OpPC, const Function *F) {
923-
924-
if (F->isVirtual() && !S.getLangOpts().CPlusPlus20) {
925-
const SourceLocation &Loc = S.Current->getLocation(OpPC);
926-
S.CCEDiag(Loc, diag::note_constexpr_virtual_call);
927-
return false;
928-
}
929-
930-
if (S.checkingPotentialConstantExpression() && S.Current->getDepth() != 0)
931-
return false;
932-
933-
if (F->isValid() && F->hasBody() && F->isConstexpr())
934-
return true;
935-
936-
const FunctionDecl *DiagDecl = F->getDecl();
937-
const FunctionDecl *Definition = nullptr;
938-
DiagDecl->getBody(Definition);
939-
940-
if (!Definition && S.checkingPotentialConstantExpression() &&
941-
DiagDecl->isConstexpr()) {
942-
return false;
943-
}
944-
945-
// Implicitly constexpr.
946-
if (F->isLambdaStaticInvoker())
947-
return true;
948-
922+
static bool diagnoseCallableDecl(InterpState &S, CodePtr OpPC,
923+
const FunctionDecl *DiagDecl) {
949924
// Bail out if the function declaration itself is invalid. We will
950925
// have produced a relevant diagnostic while parsing it, so just
951926
// note the problematic sub-expression.
952927
if (DiagDecl->isInvalidDecl())
953928
return Invalid(S, OpPC);
954929

955930
// Diagnose failed assertions specially.
956-
if (S.Current->getLocation(OpPC).isMacroID() &&
957-
F->getDecl()->getIdentifier()) {
931+
if (S.Current->getLocation(OpPC).isMacroID() && DiagDecl->getIdentifier()) {
958932
// FIXME: Instead of checking for an implementation-defined function,
959933
// check and evaluate the assert() macro.
960-
StringRef Name = F->getDecl()->getName();
934+
StringRef Name = DiagDecl->getName();
961935
bool AssertFailed =
962936
Name == "__assert_rtn" || Name == "__assert_fail" || Name == "_wassert";
963937
if (AssertFailed) {
@@ -1004,7 +978,7 @@ static bool CheckCallable(InterpState &S, CodePtr OpPC, const Function *F) {
1004978
// for a constant expression. It might be defined at the point we're
1005979
// actually calling it.
1006980
bool IsExtern = DiagDecl->getStorageClass() == SC_Extern;
1007-
bool IsDefined = F->isDefined();
981+
bool IsDefined = DiagDecl->isDefined();
1008982
if (!IsDefined && !IsExtern && DiagDecl->isConstexpr() &&
1009983
S.checkingPotentialConstantExpression())
1010984
return false;
@@ -1027,6 +1001,35 @@ static bool CheckCallable(InterpState &S, CodePtr OpPC, const Function *F) {
10271001
return false;
10281002
}
10291003

1004+
static bool CheckCallable(InterpState &S, CodePtr OpPC, const Function *F) {
1005+
if (F->isVirtual() && !S.getLangOpts().CPlusPlus20) {
1006+
const SourceLocation &Loc = S.Current->getLocation(OpPC);
1007+
S.CCEDiag(Loc, diag::note_constexpr_virtual_call);
1008+
return false;
1009+
}
1010+
1011+
if (S.checkingPotentialConstantExpression() && S.Current->getDepth() != 0)
1012+
return false;
1013+
1014+
if (F->isValid() && F->hasBody() && F->isConstexpr())
1015+
return true;
1016+
1017+
const FunctionDecl *DiagDecl = F->getDecl();
1018+
const FunctionDecl *Definition = nullptr;
1019+
DiagDecl->getBody(Definition);
1020+
1021+
if (!Definition && S.checkingPotentialConstantExpression() &&
1022+
DiagDecl->isConstexpr()) {
1023+
return false;
1024+
}
1025+
1026+
// Implicitly constexpr.
1027+
if (F->isLambdaStaticInvoker())
1028+
return true;
1029+
1030+
return diagnoseCallableDecl(S, OpPC, DiagDecl);
1031+
}
1032+
10301033
static bool CheckCallDepth(InterpState &S, CodePtr OpPC) {
10311034
if ((S.Current->getDepth() + 1) > S.getLangOpts().ConstexprCallDepth) {
10321035
S.FFDiag(S.Current->getSource(OpPC),
@@ -1500,6 +1503,21 @@ bool CheckDestructor(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
15001503
return CheckActive(S, OpPC, Ptr, AK_Destroy);
15011504
}
15021505

1506+
/// Opcode. Check if the function decl can be called at compile time.
1507+
bool CheckFunctionDecl(InterpState &S, CodePtr OpPC, const FunctionDecl *FD) {
1508+
if (S.checkingPotentialConstantExpression() && S.Current->getDepth() != 0)
1509+
return false;
1510+
1511+
const FunctionDecl *Definition = nullptr;
1512+
const Stmt *Body = FD->getBody(Definition);
1513+
1514+
if (Definition && Body &&
1515+
(Definition->isConstexpr() || Definition->hasAttr<MSConstexprAttr>()))
1516+
return true;
1517+
1518+
return diagnoseCallableDecl(S, OpPC, FD);
1519+
}
1520+
15031521
static void compileFunction(InterpState &S, const Function *Func) {
15041522
const FunctionDecl *Definition = Func->getDecl()->getDefinition();
15051523
if (!Definition)

clang/lib/AST/ByteCode/Interp.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ bool CheckBitCast(InterpState &S, CodePtr OpPC, bool HasIndeterminateBits,
117117
bool TargetIsUCharOrByte);
118118
bool CheckBCPResult(InterpState &S, const Pointer &Ptr);
119119
bool CheckDestructor(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
120+
bool CheckFunctionDecl(InterpState &S, CodePtr OpPC, const FunctionDecl *FD);
120121

121122
bool handleFixedPointOverflow(InterpState &S, CodePtr OpPC,
122123
const FixedPoint &FP);

clang/lib/AST/ByteCode/Opcodes.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ def ArgBool : ArgType { let Name = "bool"; }
5353
def ArgFixedPoint : ArgType { let Name = "FixedPoint"; let AsRef = true; }
5454

5555
def ArgFunction : ArgType { let Name = "const Function *"; }
56+
def ArgFunctionDecl : ArgType { let Name = "const FunctionDecl *"; }
5657
def ArgRecordDecl : ArgType { let Name = "const RecordDecl *"; }
5758
def ArgRecordField : ArgType { let Name = "const Record::Field *"; }
5859
def ArgFltSemantics : ArgType { let Name = "const llvm::fltSemantics *"; }
@@ -422,6 +423,8 @@ def CheckLiteralType : Opcode {
422423

423424
def CheckArraySize : Opcode { let Args = [ArgUint64]; }
424425

426+
def CheckFunctionDecl : Opcode { let Args = [ArgFunctionDecl]; }
427+
425428
// [] -> [Value]
426429
def GetGlobal : AccessOpcode;
427430
def GetGlobalUnchecked : AccessOpcode;

clang/test/AST/ByteCode/cxx11.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,3 +379,11 @@ namespace DiscardedAddrLabel {
379379
}
380380
}
381381

382+
struct Counter {
383+
int copies;
384+
constexpr Counter(int copies) : copies(copies) {}
385+
constexpr Counter(const Counter& other) : copies(other.copies + 1) {}
386+
};
387+
// Passing an lvalue by value makes a non-elidable copy.
388+
constexpr int PassByValue(Counter c) { return c.copies; }
389+
static_assert(PassByValue(Counter(0)) == 0, "expect no copies");

clang/test/CXX/dcl.decl/dcl.fct.def/dcl.fct.def.default/p2.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify -fcxx-exceptions %s
2+
// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify -fcxx-exceptions %s -fexperimental-new-constant-interpreter
23

34
// An explicitly-defaulted function may be declared constexpr only if it would
45
// have been implicitly declared as constexpr.

0 commit comments

Comments
 (0)