Skip to content

Commit 1d59298

Browse files
authored
Revert "Improve stack usage to increase recursive initialization depth (#88546)"
This reverts commit 4082a75.
1 parent 8c9d814 commit 1d59298

File tree

5 files changed

+75
-54
lines changed

5 files changed

+75
-54
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -203,12 +203,6 @@ Non-comprehensive list of changes in this release
203203
- ``__typeof_unqual__`` is available in all C modes as an extension, which behaves
204204
like ``typeof_unqual`` from C23, similar to ``__typeof__`` and ``typeof``.
205205

206-
- Improved stack usage with C++ initialization code. This allows significantly
207-
more levels of recursive initialization before reaching stack exhaustion
208-
limits. This will positively impact recursive template instantiation code,
209-
but should also reduce memory overhead for initializations in general.
210-
Fixes #GH88330
211-
212206
New Compiler Flags
213207
------------------
214208
- ``-fsanitize=implicit-bitfield-conversion`` checks implicit truncation and

clang/include/clang/Sema/Initialization.h

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1134,7 +1134,7 @@ class InitializationSequence {
11341134
OverloadingResult FailedOverloadResult;
11351135

11361136
/// The candidate set created when initialization failed.
1137-
std::unique_ptr<OverloadCandidateSet> FailedCandidateSet;
1137+
OverloadCandidateSet FailedCandidateSet;
11381138

11391139
/// The incomplete type that caused a failure.
11401140
QualType FailedIncompleteType;
@@ -1403,9 +1403,7 @@ class InitializationSequence {
14031403
/// Retrieve a reference to the candidate set when overload
14041404
/// resolution fails.
14051405
OverloadCandidateSet &getFailedCandidateSet() {
1406-
assert(FailedCandidateSet &&
1407-
"this should have been allocated in the constructor!");
1408-
return *FailedCandidateSet;
1406+
return FailedCandidateSet;
14091407
}
14101408

14111409
/// Get the overloading result, for when the initialization

clang/include/clang/Sema/Overload.h

Lines changed: 51 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@
3737
#include <cassert>
3838
#include <cstddef>
3939
#include <cstdint>
40-
#include <memory>
4140
#include <utility>
4241

4342
namespace clang {
@@ -875,8 +874,7 @@ class Sema;
875874
ConversionFixItGenerator Fix;
876875

877876
/// Viable - True to indicate that this overload candidate is viable.
878-
LLVM_PREFERRED_TYPE(bool)
879-
unsigned Viable : 1;
877+
bool Viable : 1;
880878

881879
/// Whether this candidate is the best viable function, or tied for being
882880
/// the best viable function.
@@ -885,14 +883,12 @@ class Sema;
885883
/// was part of the ambiguity kernel: the minimal non-empty set of viable
886884
/// candidates such that all elements of the ambiguity kernel are better
887885
/// than all viable candidates not in the ambiguity kernel.
888-
LLVM_PREFERRED_TYPE(bool)
889-
unsigned Best : 1;
886+
bool Best : 1;
890887

891888
/// IsSurrogate - True to indicate that this candidate is a
892889
/// surrogate for a conversion to a function pointer or reference
893890
/// (C++ [over.call.object]).
894-
LLVM_PREFERRED_TYPE(bool)
895-
unsigned IsSurrogate : 1;
891+
bool IsSurrogate : 1;
896892

897893
/// IgnoreObjectArgument - True to indicate that the first
898894
/// argument's conversion, which for this function represents the
@@ -901,20 +897,18 @@ class Sema;
901897
/// implicit object argument is just a placeholder) or a
902898
/// non-static member function when the call doesn't have an
903899
/// object argument.
904-
LLVM_PREFERRED_TYPE(bool)
905-
unsigned IgnoreObjectArgument : 1;
900+
bool IgnoreObjectArgument : 1;
906901

907902
/// True if the candidate was found using ADL.
908-
LLVM_PREFERRED_TYPE(CallExpr::ADLCallKind)
909-
unsigned IsADLCandidate : 1;
903+
CallExpr::ADLCallKind IsADLCandidate : 1;
910904

911905
/// Whether this is a rewritten candidate, and if so, of what kind?
912906
LLVM_PREFERRED_TYPE(OverloadCandidateRewriteKind)
913907
unsigned RewriteKind : 2;
914908

915909
/// FailureKind - The reason why this candidate is not viable.
916-
LLVM_PREFERRED_TYPE(OverloadFailureKind)
917-
unsigned FailureKind : 5;
910+
/// Actually an OverloadFailureKind.
911+
unsigned char FailureKind;
918912

919913
/// The number of call arguments that were explicitly provided,
920914
/// to be used while performing partial ordering of function templates.
@@ -978,9 +972,7 @@ class Sema;
978972
private:
979973
friend class OverloadCandidateSet;
980974
OverloadCandidate()
981-
: IsSurrogate(false),
982-
IsADLCandidate(static_cast<unsigned>(CallExpr::NotADL)),
983-
RewriteKind(CRK_None) {}
975+
: IsSurrogate(false), IsADLCandidate(CallExpr::NotADL), RewriteKind(CRK_None) {}
984976
};
985977

986978
/// OverloadCandidateSet - A set of overload candidates, used in C++
@@ -1078,16 +1070,51 @@ class Sema;
10781070
};
10791071

10801072
private:
1081-
SmallVector<OverloadCandidate, 4> Candidates;
1082-
llvm::SmallPtrSet<uintptr_t, 4> Functions;
1073+
SmallVector<OverloadCandidate, 16> Candidates;
1074+
llvm::SmallPtrSet<uintptr_t, 16> Functions;
1075+
1076+
// Allocator for ConversionSequenceLists. We store the first few of these
1077+
// inline to avoid allocation for small sets.
1078+
llvm::BumpPtrAllocator SlabAllocator;
10831079

10841080
SourceLocation Loc;
10851081
CandidateSetKind Kind;
10861082
OperatorRewriteInfo RewriteInfo;
10871083

1084+
constexpr static unsigned NumInlineBytes =
1085+
24 * sizeof(ImplicitConversionSequence);
1086+
unsigned NumInlineBytesUsed = 0;
1087+
alignas(void *) char InlineSpace[NumInlineBytes];
1088+
10881089
// Address space of the object being constructed.
10891090
LangAS DestAS = LangAS::Default;
10901091

1092+
/// If we have space, allocates from inline storage. Otherwise, allocates
1093+
/// from the slab allocator.
1094+
/// FIXME: It would probably be nice to have a SmallBumpPtrAllocator
1095+
/// instead.
1096+
/// FIXME: Now that this only allocates ImplicitConversionSequences, do we
1097+
/// want to un-generalize this?
1098+
template <typename T>
1099+
T *slabAllocate(unsigned N) {
1100+
// It's simpler if this doesn't need to consider alignment.
1101+
static_assert(alignof(T) == alignof(void *),
1102+
"Only works for pointer-aligned types.");
1103+
static_assert(std::is_trivial<T>::value ||
1104+
std::is_same<ImplicitConversionSequence, T>::value,
1105+
"Add destruction logic to OverloadCandidateSet::clear().");
1106+
1107+
unsigned NBytes = sizeof(T) * N;
1108+
if (NBytes > NumInlineBytes - NumInlineBytesUsed)
1109+
return SlabAllocator.Allocate<T>(N);
1110+
char *FreeSpaceStart = InlineSpace + NumInlineBytesUsed;
1111+
assert(uintptr_t(FreeSpaceStart) % alignof(void *) == 0 &&
1112+
"Misaligned storage!");
1113+
1114+
NumInlineBytesUsed += NBytes;
1115+
return reinterpret_cast<T *>(FreeSpaceStart);
1116+
}
1117+
10911118
void destroyCandidates();
10921119

10931120
public:
@@ -1136,7 +1163,12 @@ class Sema;
11361163
ConversionSequenceList
11371164
allocateConversionSequences(unsigned NumConversions) {
11381165
ImplicitConversionSequence *Conversions =
1139-
new ImplicitConversionSequence[NumConversions];
1166+
slabAllocate<ImplicitConversionSequence>(NumConversions);
1167+
1168+
// Construct the new objects.
1169+
for (unsigned I = 0; I != NumConversions; ++I)
1170+
new (&Conversions[I]) ImplicitConversionSequence();
1171+
11401172
return ConversionSequenceList(Conversions, NumConversions);
11411173
}
11421174

clang/lib/Sema/SemaInit.cpp

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6114,8 +6114,7 @@ InitializationSequence::InitializationSequence(
61146114
Sema &S, const InitializedEntity &Entity, const InitializationKind &Kind,
61156115
MultiExprArg Args, bool TopLevelOfInitList, bool TreatUnavailableAsInvalid)
61166116
: FailedOverloadResult(OR_Success),
6117-
FailedCandidateSet(new OverloadCandidateSet(
6118-
Kind.getLocation(), OverloadCandidateSet::CSK_Normal)) {
6117+
FailedCandidateSet(Kind.getLocation(), OverloadCandidateSet::CSK_Normal) {
61196118
InitializeFrom(S, Entity, Kind, Args, TopLevelOfInitList,
61206119
TreatUnavailableAsInvalid);
61216120
}
@@ -9736,7 +9735,7 @@ bool InitializationSequence::Diagnose(Sema &S,
97369735
switch (FailedOverloadResult) {
97379736
case OR_Ambiguous:
97389737

9739-
FailedCandidateSet->NoteCandidates(
9738+
FailedCandidateSet.NoteCandidates(
97409739
PartialDiagnosticAt(
97419740
Kind.getLocation(),
97429741
Failure == FK_UserConversionOverloadFailed
@@ -9750,8 +9749,7 @@ bool InitializationSequence::Diagnose(Sema &S,
97509749
break;
97519750

97529751
case OR_No_Viable_Function: {
9753-
auto Cands =
9754-
FailedCandidateSet->CompleteCandidates(S, OCD_AllCandidates, Args);
9752+
auto Cands = FailedCandidateSet.CompleteCandidates(S, OCD_AllCandidates, Args);
97559753
if (!S.RequireCompleteType(Kind.getLocation(),
97569754
DestType.getNonReferenceType(),
97579755
diag::err_typecheck_nonviable_condition_incomplete,
@@ -9761,13 +9759,13 @@ bool InitializationSequence::Diagnose(Sema &S,
97619759
<< OnlyArg->getType() << Args[0]->getSourceRange()
97629760
<< DestType.getNonReferenceType();
97639761

9764-
FailedCandidateSet->NoteCandidates(S, Args, Cands);
9762+
FailedCandidateSet.NoteCandidates(S, Args, Cands);
97659763
break;
97669764
}
97679765
case OR_Deleted: {
97689766
OverloadCandidateSet::iterator Best;
9769-
OverloadingResult Ovl =
9770-
FailedCandidateSet->BestViableFunction(S, Kind.getLocation(), Best);
9767+
OverloadingResult Ovl
9768+
= FailedCandidateSet.BestViableFunction(S, Kind.getLocation(), Best);
97719769

97729770
StringLiteral *Msg = Best->Function->getDeletedMessage();
97739771
S.Diag(Kind.getLocation(), diag::err_typecheck_deleted_function)
@@ -9951,7 +9949,7 @@ bool InitializationSequence::Diagnose(Sema &S,
99519949
// bad.
99529950
switch (FailedOverloadResult) {
99539951
case OR_Ambiguous:
9954-
FailedCandidateSet->NoteCandidates(
9952+
FailedCandidateSet.NoteCandidates(
99559953
PartialDiagnosticAt(Kind.getLocation(),
99569954
S.PDiag(diag::err_ovl_ambiguous_init)
99579955
<< DestType << ArgsRange),
@@ -10005,7 +10003,7 @@ bool InitializationSequence::Diagnose(Sema &S,
1000510003
break;
1000610004
}
1000710005

10008-
FailedCandidateSet->NoteCandidates(
10006+
FailedCandidateSet.NoteCandidates(
1000910007
PartialDiagnosticAt(
1001010008
Kind.getLocation(),
1001110009
S.PDiag(diag::err_ovl_no_viable_function_in_init)
@@ -10015,8 +10013,8 @@ bool InitializationSequence::Diagnose(Sema &S,
1001510013

1001610014
case OR_Deleted: {
1001710015
OverloadCandidateSet::iterator Best;
10018-
OverloadingResult Ovl =
10019-
FailedCandidateSet->BestViableFunction(S, Kind.getLocation(), Best);
10016+
OverloadingResult Ovl
10017+
= FailedCandidateSet.BestViableFunction(S, Kind.getLocation(), Best);
1002010018
if (Ovl != OR_Deleted) {
1002110019
S.Diag(Kind.getLocation(), diag::err_ovl_deleted_init)
1002210020
<< DestType << ArgsRange;
@@ -10095,8 +10093,8 @@ bool InitializationSequence::Diagnose(Sema &S,
1009510093
S.Diag(Kind.getLocation(), diag::err_selected_explicit_constructor)
1009610094
<< Args[0]->getSourceRange();
1009710095
OverloadCandidateSet::iterator Best;
10098-
OverloadingResult Ovl =
10099-
FailedCandidateSet->BestViableFunction(S, Kind.getLocation(), Best);
10096+
OverloadingResult Ovl
10097+
= FailedCandidateSet.BestViableFunction(S, Kind.getLocation(), Best);
1010010098
(void)Ovl;
1010110099
assert(Ovl == OR_Success && "Inconsistent overload resolution");
1010210100
CXXConstructorDecl *CtorDecl = cast<CXXConstructorDecl>(Best->Function);

clang/lib/Sema/SemaOverload.cpp

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1057,14 +1057,17 @@ bool OverloadCandidateSet::OperatorRewriteInfo::shouldAddReversed(
10571057

10581058
void OverloadCandidateSet::destroyCandidates() {
10591059
for (iterator i = begin(), e = end(); i != e; ++i) {
1060-
delete[] i->Conversions.data();
1060+
for (auto &C : i->Conversions)
1061+
C.~ImplicitConversionSequence();
10611062
if (!i->Viable && i->FailureKind == ovl_fail_bad_deduction)
10621063
i->DeductionFailure.Destroy();
10631064
}
10641065
}
10651066

10661067
void OverloadCandidateSet::clear(CandidateSetKind CSK) {
10671068
destroyCandidates();
1069+
SlabAllocator.Reset();
1070+
NumInlineBytesUsed = 0;
10681071
Candidates.clear();
10691072
Functions.clear();
10701073
Kind = CSK;
@@ -6980,7 +6983,7 @@ void Sema::AddOverloadCandidate(
69806983
Candidate.RewriteKind =
69816984
CandidateSet.getRewriteInfo().getRewriteKind(Function, PO);
69826985
Candidate.IsSurrogate = false;
6983-
Candidate.IsADLCandidate = static_cast<unsigned>(IsADLCandidate);
6986+
Candidate.IsADLCandidate = IsADLCandidate;
69846987
Candidate.IgnoreObjectArgument = false;
69856988
Candidate.ExplicitCallArguments = Args.size();
69866989

@@ -7812,7 +7815,7 @@ void Sema::AddTemplateOverloadCandidate(
78127815
Candidate.RewriteKind =
78137816
CandidateSet.getRewriteInfo().getRewriteKind(Candidate.Function, PO);
78147817
Candidate.IsSurrogate = false;
7815-
Candidate.IsADLCandidate = static_cast<unsigned>(IsADLCandidate);
7818+
Candidate.IsADLCandidate = IsADLCandidate;
78167819
// Ignore the object argument if there is one, since we don't have an object
78177820
// type.
78187821
Candidate.IgnoreObjectArgument =
@@ -14122,8 +14125,7 @@ static ExprResult FinishOverloadedCallExpr(Sema &SemaRef, Scope *S, Expr *Fn,
1412214125
return ExprError();
1412314126
return SemaRef.BuildResolvedCallExpr(
1412414127
Res.get(), FDecl, LParenLoc, Args, RParenLoc, ExecConfig,
14125-
/*IsExecConfig=*/false,
14126-
static_cast<CallExpr::ADLCallKind>((*Best)->IsADLCandidate));
14128+
/*IsExecConfig=*/false, (*Best)->IsADLCandidate);
1412714129
}
1412814130

1412914131
case OR_No_Viable_Function: {
@@ -14182,8 +14184,7 @@ static ExprResult FinishOverloadedCallExpr(Sema &SemaRef, Scope *S, Expr *Fn,
1418214184
return ExprError();
1418314185
return SemaRef.BuildResolvedCallExpr(
1418414186
Res.get(), FDecl, LParenLoc, Args, RParenLoc, ExecConfig,
14185-
/*IsExecConfig=*/false,
14186-
static_cast<CallExpr::ADLCallKind>((*Best)->IsADLCandidate));
14187+
/*IsExecConfig=*/false, (*Best)->IsADLCandidate);
1418714188
}
1418814189
}
1418914190

@@ -14490,8 +14491,7 @@ Sema::CreateOverloadedUnaryOp(SourceLocation OpLoc, UnaryOperatorKind Opc,
1449014491
Args[0] = Input;
1449114492
CallExpr *TheCall = CXXOperatorCallExpr::Create(
1449214493
Context, Op, FnExpr.get(), ArgsArray, ResultTy, VK, OpLoc,
14493-
CurFPFeatureOverrides(),
14494-
static_cast<CallExpr::ADLCallKind>(Best->IsADLCandidate));
14494+
CurFPFeatureOverrides(), Best->IsADLCandidate);
1449514495

1449614496
if (CheckCallReturnType(FnDecl->getReturnType(), OpLoc, TheCall, FnDecl))
1449714497
return ExprError();
@@ -14909,8 +14909,7 @@ ExprResult Sema::CreateOverloadedBinOp(SourceLocation OpLoc,
1490914909
// members; CodeGen should take care not to emit the this pointer.
1491014910
TheCall = CXXOperatorCallExpr::Create(
1491114911
Context, ChosenOp, FnExpr.get(), Args, ResultTy, VK, OpLoc,
14912-
CurFPFeatureOverrides(),
14913-
static_cast<CallExpr::ADLCallKind>(Best->IsADLCandidate));
14912+
CurFPFeatureOverrides(), Best->IsADLCandidate);
1491414913

1491514914
if (const auto *Method = dyn_cast<CXXMethodDecl>(FnDecl);
1491614915
Method && Method->isImplicitObjectMemberFunction()) {

0 commit comments

Comments
 (0)