[AST] Only store the needed data in SwitchStmt

Don't store the data for the init statement and condition variable
if not needed. This cuts the size of SwitchStmt by up to 2 pointers.
The order of the children is intentionally kept the same.

Also use the newly available space in the bit-fields of Stmt
to store the bit representing whether all enums have been covered
instead of using a PointerIntPair.

Differential Revision: https://reviews.llvm.org/D53714

Reviewed By: rjmccall

llvm-svn: 345510
This commit is contained in:
Bruno Ricci 2018-10-29 16:12:37 +00:00
parent e92567601b
commit e2806f857b
9 changed files with 232 additions and 78 deletions

View File

@ -177,6 +177,17 @@ protected:
unsigned : NumStmtBits;
/// True if the SwitchStmt has storage for an init statement.
unsigned HasInit : 1;
/// True if the SwitchStmt has storage for a condition variable.
unsigned HasVar : 1;
/// If the SwitchStmt is a switch on an enum value, records whether all
/// the enum values were covered by CaseStmts. The coverage information
/// value is meant to be a hint for possible clients.
unsigned AllEnumCasesCovered : 1;
/// The location of the "switch".
SourceLocation SwitchLoc;
};
@ -1427,21 +1438,102 @@ public:
};
/// SwitchStmt - This represents a 'switch' stmt.
class SwitchStmt : public Stmt {
enum { INIT, VAR, COND, BODY, END_EXPR };
Stmt* SubExprs[END_EXPR];
class SwitchStmt final : public Stmt,
private llvm::TrailingObjects<SwitchStmt, Stmt *> {
friend TrailingObjects;
// This points to a linked list of case and default statements and, if the
// SwitchStmt is a switch on an enum value, records whether all the enum
// values were covered by CaseStmts. The coverage information value is meant
// to be a hint for possible clients.
llvm::PointerIntPair<SwitchCase *, 1, bool> FirstCase;
/// Points to a linked list of case and default statements.
SwitchCase *FirstCase;
public:
SwitchStmt(const ASTContext &C, Stmt *Init, VarDecl *Var, Expr *cond);
// SwitchStmt is followed by several trailing objects,
// some of which optional. Note that it would be more convenient to
// put the optional trailing objects at the end but this would change
// the order in children().
// The trailing objects are in order:
//
// * A "Stmt *" for the init statement.
// Present if and only if hasInitStorage().
//
// * A "Stmt *" for the condition variable.
// Present if and only if hasVarStorage(). This is in fact a "DeclStmt *".
//
// * A "Stmt *" for the condition.
// Always present. This is in fact an "Expr *".
//
// * A "Stmt *" for the body.
// Always present.
enum { InitOffset = 0, BodyOffsetFromCond = 1 };
enum { NumMandatoryStmtPtr = 2 };
unsigned numTrailingObjects(OverloadToken<Stmt *>) const {
return NumMandatoryStmtPtr + hasInitStorage() + hasVarStorage();
}
unsigned initOffset() const { return InitOffset; }
unsigned varOffset() const { return InitOffset + hasInitStorage(); }
unsigned condOffset() const {
return InitOffset + hasInitStorage() + hasVarStorage();
}
unsigned bodyOffset() const { return condOffset() + BodyOffsetFromCond; }
/// Build a switch statement.
SwitchStmt(const ASTContext &Ctx, Stmt *Init, VarDecl *Var, Expr *Cond);
/// Build a empty switch statement.
explicit SwitchStmt(EmptyShell Empty) : Stmt(SwitchStmtClass, Empty) {}
explicit SwitchStmt(EmptyShell Empty, bool HasInit, bool HasVar);
public:
/// Create a switch statement.
static SwitchStmt *Create(const ASTContext &Ctx, Stmt *Init, VarDecl *Var,
Expr *Cond);
/// Create an empty switch statement optionally with storage for
/// an init expression and a condition variable.
static SwitchStmt *CreateEmpty(const ASTContext &Ctx, bool HasInit,
bool HasVar);
/// True if this SwitchStmt has storage for an init statement.
bool hasInitStorage() const { return SwitchStmtBits.HasInit; }
/// True if this SwitchStmt has storage for a condition variable.
bool hasVarStorage() const { return SwitchStmtBits.HasVar; }
Expr *getCond() {
return reinterpret_cast<Expr *>(getTrailingObjects<Stmt *>()[condOffset()]);
}
const Expr *getCond() const {
return reinterpret_cast<Expr *>(getTrailingObjects<Stmt *>()[condOffset()]);
}
void setCond(Expr *Cond) {
getTrailingObjects<Stmt *>()[condOffset()] = reinterpret_cast<Stmt *>(Cond);
}
Stmt *getBody() { return getTrailingObjects<Stmt *>()[bodyOffset()]; }
const Stmt *getBody() const {
return getTrailingObjects<Stmt *>()[bodyOffset()];
}
void setBody(Stmt *Body) {
getTrailingObjects<Stmt *>()[bodyOffset()] = Body;
}
Stmt *getInit() {
return hasInitStorage() ? getTrailingObjects<Stmt *>()[initOffset()]
: nullptr;
}
const Stmt *getInit() const {
return hasInitStorage() ? getTrailingObjects<Stmt *>()[initOffset()]
: nullptr;
}
void setInit(Stmt *Init) {
assert(hasInitStorage() &&
"This switch statement has no storage for an init statement!");
getTrailingObjects<Stmt *>()[initOffset()] = Init;
}
/// Retrieve the variable declared in this "switch" statement, if any.
///
@ -1452,64 +1544,69 @@ public:
/// // ...
/// }
/// \endcode
VarDecl *getConditionVariable() const;
void setConditionVariable(const ASTContext &C, VarDecl *V);
VarDecl *getConditionVariable();
const VarDecl *getConditionVariable() const {
return const_cast<SwitchStmt *>(this)->getConditionVariable();
}
/// Set the condition variable in this switch statement.
/// The switch statement must have storage for it.
void setConditionVariable(const ASTContext &Ctx, VarDecl *VD);
/// If this SwitchStmt has a condition variable, return the faux DeclStmt
/// associated with the creation of that condition variable.
const DeclStmt *getConditionVariableDeclStmt() const {
return reinterpret_cast<DeclStmt*>(SubExprs[VAR]);
DeclStmt *getConditionVariableDeclStmt() {
return hasVarStorage() ? static_cast<DeclStmt *>(
getTrailingObjects<Stmt *>()[varOffset()])
: nullptr;
}
Stmt *getInit() { return SubExprs[INIT]; }
const Stmt *getInit() const { return SubExprs[INIT]; }
void setInit(Stmt *S) { SubExprs[INIT] = S; }
const Expr *getCond() const { return reinterpret_cast<Expr*>(SubExprs[COND]);}
const Stmt *getBody() const { return SubExprs[BODY]; }
const SwitchCase *getSwitchCaseList() const { return FirstCase.getPointer(); }
const DeclStmt *getConditionVariableDeclStmt() const {
return hasVarStorage() ? static_cast<DeclStmt *>(
getTrailingObjects<Stmt *>()[varOffset()])
: nullptr;
}
Expr *getCond() { return reinterpret_cast<Expr*>(SubExprs[COND]);}
void setCond(Expr *E) { SubExprs[COND] = reinterpret_cast<Stmt *>(E); }
Stmt *getBody() { return SubExprs[BODY]; }
void setBody(Stmt *S) { SubExprs[BODY] = S; }
SwitchCase *getSwitchCaseList() { return FirstCase.getPointer(); }
/// Set the case list for this switch statement.
void setSwitchCaseList(SwitchCase *SC) { FirstCase.setPointer(SC); }
SwitchCase *getSwitchCaseList() { return FirstCase; }
const SwitchCase *getSwitchCaseList() const { return FirstCase; }
void setSwitchCaseList(SwitchCase *SC) { FirstCase = SC; }
SourceLocation getSwitchLoc() const { return SwitchStmtBits.SwitchLoc; }
void setSwitchLoc(SourceLocation L) { SwitchStmtBits.SwitchLoc = L; }
void setBody(Stmt *S, SourceLocation SL) {
SubExprs[BODY] = S;
SwitchStmtBits.SwitchLoc = SL;
setBody(S);
setSwitchLoc(SL);
}
void addSwitchCase(SwitchCase *SC) {
assert(!SC->getNextSwitchCase()
&& "case/default already added to a switch");
SC->setNextSwitchCase(FirstCase.getPointer());
FirstCase.setPointer(SC);
assert(!SC->getNextSwitchCase() &&
"case/default already added to a switch");
SC->setNextSwitchCase(FirstCase);
FirstCase = SC;
}
/// Set a flag in the SwitchStmt indicating that if the 'switch (X)' is a
/// switch over an enum value then all cases have been explicitly covered.
void setAllEnumCasesCovered() { FirstCase.setInt(true); }
void setAllEnumCasesCovered() { SwitchStmtBits.AllEnumCasesCovered = true; }
/// Returns true if the SwitchStmt is a switch of an enum value and all cases
/// have been explicitly covered.
bool isAllEnumCasesCovered() const { return FirstCase.getInt(); }
bool isAllEnumCasesCovered() const {
return SwitchStmtBits.AllEnumCasesCovered;
}
SourceLocation getBeginLoc() const { return getSwitchLoc(); }
SourceLocation getEndLoc() const {
return SubExprs[BODY] ? SubExprs[BODY]->getEndLoc()
: SubExprs[COND]->getEndLoc();
SourceLocation getEndLoc() const LLVM_READONLY {
return getBody() ? getBody()->getEndLoc()
: reinterpret_cast<const Stmt *>(getCond())->getEndLoc();
}
// Iterators
child_range children() {
return child_range(&SubExprs[0], &SubExprs[0]+END_EXPR);
return child_range(getTrailingObjects<Stmt *>(),
getTrailingObjects<Stmt *>() +
numTrailingObjects(OverloadToken<Stmt *>()));
}
static bool classof(const Stmt *T) {

View File

@ -512,6 +512,7 @@ namespace {
void VisitDeclStmt(const DeclStmt *Node);
void VisitAttributedStmt(const AttributedStmt *Node);
void VisitIfStmt(const IfStmt *Node);
void VisitSwitchStmt(const SwitchStmt *Node);
void VisitLabelStmt(const LabelStmt *Node);
void VisitGotoStmt(const GotoStmt *Node);
void VisitCXXCatchStmt(const CXXCatchStmt *Node);
@ -2032,6 +2033,14 @@ void ASTDumper::VisitIfStmt(const IfStmt *Node) {
OS << " has_else";
}
void ASTDumper::VisitSwitchStmt(const SwitchStmt *Node) {
VisitStmt(Node);
if (Node->hasInitStorage())
OS << " has_init";
if (Node->hasVarStorage())
OS << " has_var";
}
void ASTDumper::VisitLabelStmt(const LabelStmt *Node) {
VisitStmt(Node);
OS << " '" << Node->getName() << "'";

View File

@ -5790,8 +5790,8 @@ ExpectedStmt ASTNodeImporter::VisitSwitchStmt(SwitchStmt *S) {
SourceLocation ToSwitchLoc;
std::tie(ToInit, ToConditionVariable, ToCond, ToBody, ToSwitchLoc) = *Imp;
auto *ToStmt = new (Importer.getToContext()) SwitchStmt(
Importer.getToContext(), ToInit, ToConditionVariable, ToCond);
auto *ToStmt = SwitchStmt::Create(Importer.getToContext(), ToInit,
ToConditionVariable, ToCond);
ToStmt->setBody(ToBody);
ToStmt->setSwitchLoc(ToSwitchLoc);

View File

@ -913,33 +913,69 @@ void ForStmt::setConditionVariable(const ASTContext &C, VarDecl *V) {
VarRange.getEnd());
}
SwitchStmt::SwitchStmt(const ASTContext &C, Stmt *init, VarDecl *Var,
Expr *cond)
: Stmt(SwitchStmtClass), FirstCase(nullptr, false) {
setConditionVariable(C, Var);
SubExprs[INIT] = init;
SubExprs[COND] = cond;
SubExprs[BODY] = nullptr;
SwitchStmtBits.SwitchLoc = SourceLocation{};
SwitchStmt::SwitchStmt(const ASTContext &Ctx, Stmt *Init, VarDecl *Var,
Expr *Cond)
: Stmt(SwitchStmtClass), FirstCase(nullptr) {
bool HasInit = Init != nullptr;
bool HasVar = Var != nullptr;
SwitchStmtBits.HasInit = HasInit;
SwitchStmtBits.HasVar = HasVar;
SwitchStmtBits.AllEnumCasesCovered = false;
setCond(Cond);
setBody(nullptr);
if (HasInit)
setInit(Init);
if (HasVar)
setConditionVariable(Ctx, Var);
setSwitchLoc(SourceLocation{});
}
VarDecl *SwitchStmt::getConditionVariable() const {
if (!SubExprs[VAR])
return nullptr;
SwitchStmt::SwitchStmt(EmptyShell Empty, bool HasInit, bool HasVar)
: Stmt(SwitchStmtClass, Empty) {
SwitchStmtBits.HasInit = HasInit;
SwitchStmtBits.HasVar = HasVar;
SwitchStmtBits.AllEnumCasesCovered = false;
}
auto *DS = cast<DeclStmt>(SubExprs[VAR]);
SwitchStmt *SwitchStmt::Create(const ASTContext &Ctx, Stmt *Init, VarDecl *Var,
Expr *Cond) {
bool HasInit = Init != nullptr;
bool HasVar = Var != nullptr;
void *Mem = Ctx.Allocate(
totalSizeToAlloc<Stmt *>(NumMandatoryStmtPtr + HasInit + HasVar),
alignof(SwitchStmt));
return new (Mem) SwitchStmt(Ctx, Init, Var, Cond);
}
SwitchStmt *SwitchStmt::CreateEmpty(const ASTContext &Ctx, bool HasInit,
bool HasVar) {
void *Mem = Ctx.Allocate(
totalSizeToAlloc<Stmt *>(NumMandatoryStmtPtr + HasInit + HasVar),
alignof(SwitchStmt));
return new (Mem) SwitchStmt(EmptyShell(), HasInit, HasVar);
}
VarDecl *SwitchStmt::getConditionVariable() {
auto *DS = getConditionVariableDeclStmt();
if (!DS)
return nullptr;
return cast<VarDecl>(DS->getSingleDecl());
}
void SwitchStmt::setConditionVariable(const ASTContext &C, VarDecl *V) {
void SwitchStmt::setConditionVariable(const ASTContext &Ctx, VarDecl *V) {
assert(hasVarStorage() &&
"This switch statement has no storage for a condition variable!");
if (!V) {
SubExprs[VAR] = nullptr;
getTrailingObjects<Stmt *>()[varOffset()] = nullptr;
return;
}
SourceRange VarRange = V->getSourceRange();
SubExprs[VAR] = new (C) DeclStmt(DeclGroupRef(V), VarRange.getBegin(),
VarRange.getEnd());
getTrailingObjects<Stmt *>()[varOffset()] = new (Ctx)
DeclStmt(DeclGroupRef(V), VarRange.getBegin(), VarRange.getEnd());
}
WhileStmt::WhileStmt(const ASTContext &C, VarDecl *Var, Expr *cond, Stmt *body,

View File

@ -727,8 +727,7 @@ StmtResult Sema::ActOnStartOfSwitchStmt(SourceLocation SwitchLoc,
setFunctionHasBranchIntoScope();
SwitchStmt *SS = new (Context)
SwitchStmt(Context, InitStmt, Cond.get().first, CondExpr);
auto *SS = SwitchStmt::Create(Context, InitStmt, Cond.get().first, CondExpr);
getCurFunction()->SwitchStack.push_back(
FunctionScopeInfo::SwitchInfo(SS, false));
return SS;

View File

@ -240,13 +240,21 @@ void ASTStmtReader::VisitIfStmt(IfStmt *S) {
void ASTStmtReader::VisitSwitchStmt(SwitchStmt *S) {
VisitStmt(S);
S->setInit(Record.readSubStmt());
S->setConditionVariable(Record.getContext(), ReadDeclAs<VarDecl>());
bool HasInit = Record.readInt();
bool HasVar = Record.readInt();
bool AllEnumCasesCovered = Record.readInt();
if (AllEnumCasesCovered)
S->setAllEnumCasesCovered();
S->setCond(Record.readSubExpr());
S->setBody(Record.readSubStmt());
if (HasInit)
S->setInit(Record.readSubStmt());
if (HasVar)
S->setConditionVariable(Record.getContext(), ReadDeclAs<VarDecl>());
S->setSwitchLoc(ReadSourceLocation());
if (Record.readInt())
S->setAllEnumCasesCovered();
SwitchCase *PrevSC = nullptr;
for (auto E = Record.size(); Record.getIdx() != E; ) {
@ -2310,7 +2318,10 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) {
break;
case STMT_SWITCH:
S = new (Context) SwitchStmt(Empty);
S = SwitchStmt::CreateEmpty(
Context,
/* HasInit=*/Record[ASTStmtReader::NumStmtFields + 0],
/* HasVar=*/Record[ASTStmtReader::NumStmtFields + 1]);
break;
case STMT_WHILE:

View File

@ -159,12 +159,22 @@ void ASTStmtWriter::VisitIfStmt(IfStmt *S) {
void ASTStmtWriter::VisitSwitchStmt(SwitchStmt *S) {
VisitStmt(S);
Record.AddStmt(S->getInit());
Record.AddDeclRef(S->getConditionVariable());
bool HasInit = S->getInit() != nullptr;
bool HasVar = S->getConditionVariableDeclStmt() != nullptr;
Record.push_back(HasInit);
Record.push_back(HasVar);
Record.push_back(S->isAllEnumCasesCovered());
Record.AddStmt(S->getCond());
Record.AddStmt(S->getBody());
if (HasInit)
Record.AddStmt(S->getInit());
if (HasVar)
Record.AddDeclRef(S->getConditionVariable());
Record.AddSourceLocation(S->getSwitchLoc());
Record.push_back(S->isAllEnumCasesCovered());
for (SwitchCase *SC = S->getSwitchCaseList(); SC;
SC = SC->getNextSwitchCase())
Record.push_back(Writer.RecordSwitchCaseID(SC));

View File

@ -1,8 +1,6 @@
// RUN: clang-import-test -dump-ast -import %S/Inputs/F.cpp -expression %s | FileCheck %s
// CHECK: SwitchStmt
// CHECK-NEXT: <<NULL>>
// CHECK-NEXT: <<NULL>>
// CHECK-NEXT: IntegerLiteral
// CHECK-NEXT: CompoundStmt
// CHECK-NEXT: CaseStmt
@ -22,7 +20,6 @@
// CHECK-NEXT: DeclStmt
// CHECK-NEXT: VarDecl
// CHECK-SAME: varname
// CHECK-NEXT: <<NULL>>
// CHECK-NEXT: IntegerLiteral
// CHECK-NEXT: CompoundStmt
// CHECK-NEXT: CaseStmt
@ -37,15 +34,11 @@
// CHECK-NEXT: BreakStmt
// CHECK: SwitchStmt
// CHECK-NEXT: <<NULL>>
// CHECK-NEXT: <<NULL>>
// CHECK-NEXT: IntegerLiteral
// CHECK-NEXT: DefaultStmt
// CHECK-NEXT: BreakStmt
// CHECK: SwitchStmt
// CHECK-NEXT: <<NULL>>
// CHECK-NEXT: <<NULL>>
// CHECK-NEXT: IntegerLiteral
// CHECK-NEXT: NullStmt

View File

@ -46,7 +46,6 @@ struct Invalid {
//CHECK: {{^}}[[Blue]]|-[[RESET]][[GREEN]]FunctionDecl[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]line:9:1[[RESET]], [[Yellow]]line:16:1[[RESET]]> [[Yellow]]line:9:6[[RESET]][[CYAN]] TestAttributedStmt[[RESET]] [[Green]]'void ()'[[RESET]]{{$}}
//CHECK: {{^}}[[Blue]]| |-[[RESET]][[MAGENTA:.\[0;1;35m]]CompoundStmt[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]col:27[[RESET]], [[Yellow]]line:16:1[[RESET]]>{{$}}
//CHECK: {{^}}[[Blue]]| | `-[[RESET]][[MAGENTA]]SwitchStmt[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]line:10:3[[RESET]], [[Yellow]]line:15:3[[RESET]]>{{$}}
//CHECK: {{^}}[[Blue]]| | |-[[RESET]][[Blue:.\[0;34m]]<<<NULL>>>[[RESET]]{{$}}
//CHECK: {{^}}[[Blue]]| | |-[[RESET]][[MAGENTA]]IntegerLiteral[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]line:10:11[[RESET]]> [[Green]]'int'[[RESET]][[Cyan:.\[0;36m]][[RESET]][[Cyan]][[RESET]][[CYAN]] 1[[RESET]]{{$}}
//CHECK: {{^}}[[Blue]]| | `-[[RESET]][[MAGENTA]]CompoundStmt[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]col:14[[RESET]], [[Yellow]]line:15:3[[RESET]]>{{$}}
//CHECK: {{^}}[[Blue]]| | |-[[RESET]][[MAGENTA]]CaseStmt[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]line:11:3[[RESET]], [[Yellow]]line:12:27[[RESET]]>{{$}}