Skip to content

[AST] Use APIntStorage to fix memory leak in EnumConstantDecl. #78311

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jan 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -675,6 +675,8 @@ Bug Fixes in This Version
Fixes (`#67317 <https://github.com/llvm/llvm-project/issues/67317>`_)
- Clang now properly diagnoses use of stand-alone OpenMP directives after a
label (including ``case`` or ``default`` labels).
- Fix compiler memory leak for enums with underlying type larger than 64 bits.
Fixes (`#78311 <https://github.com/llvm/llvm-project/pull/78311>`_)

Before:

Expand Down
71 changes: 71 additions & 0 deletions clang/include/clang/AST/APNumericStorage.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
//===--- APNumericStorage.h - Store APInt/APFloat in ASTContext -*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_AST_APNUMERICSTORAGE_H
#define LLVM_CLANG_AST_APNUMERICSTORAGE_H

#include "llvm/ADT/APFloat.h"
#include "llvm/ADT/APInt.h"

namespace clang {
class ASTContext;

/// Used by IntegerLiteral/FloatingLiteral/EnumConstantDecl to store the
/// numeric without leaking memory.
///
/// For large floats/integers, APFloat/APInt will allocate memory from the heap
/// to represent these numbers. Unfortunately, when we use a BumpPtrAllocator
/// to allocate IntegerLiteral/FloatingLiteral nodes the memory associated with
/// the APFloat/APInt values will never get freed. APNumericStorage uses
/// ASTContext's allocator for memory allocation.
class APNumericStorage {
union {
uint64_t VAL; ///< Used to store the <= 64 bits integer value.
uint64_t *pVal; ///< Used to store the >64 bits integer value.
};
unsigned BitWidth;

bool hasAllocation() const { return llvm::APInt::getNumWords(BitWidth) > 1; }

APNumericStorage(const APNumericStorage &) = delete;
void operator=(const APNumericStorage &) = delete;

protected:
APNumericStorage() : VAL(0), BitWidth(0) {}

llvm::APInt getIntValue() const {
unsigned NumWords = llvm::APInt::getNumWords(BitWidth);
if (NumWords > 1)
return llvm::APInt(BitWidth, NumWords, pVal);
else
return llvm::APInt(BitWidth, VAL);
}
void setIntValue(const ASTContext &C, const llvm::APInt &Val);
};

class APIntStorage : private APNumericStorage {
public:
llvm::APInt getValue() const { return getIntValue(); }
void setValue(const ASTContext &C, const llvm::APInt &Val) {
setIntValue(C, Val);
}
};

class APFloatStorage : private APNumericStorage {
public:
llvm::APFloat getValue(const llvm::fltSemantics &Semantics) const {
return llvm::APFloat(Semantics, getIntValue());
}
void setValue(const ASTContext &C, const llvm::APFloat &Val) {
setIntValue(C, Val.bitcastToAPInt());
}
};

} // end namespace clang

#endif // LLVM_CLANG_AST_APNUMERICSTORAGE_H
21 changes: 14 additions & 7 deletions clang/include/clang/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#ifndef LLVM_CLANG_AST_DECL_H
#define LLVM_CLANG_AST_DECL_H

#include "clang/AST/APNumericStorage.h"
#include "clang/AST/APValue.h"
#include "clang/AST/ASTContextAllocate.h"
#include "clang/AST/DeclAccessPair.h"
Expand Down Expand Up @@ -3251,15 +3252,16 @@ class FieldDecl : public DeclaratorDecl, public Mergeable<FieldDecl> {
/// that is defined. For example, in "enum X {a,b}", each of a/b are
/// EnumConstantDecl's, X is an instance of EnumDecl, and the type of a/b is a
/// TagType for the X EnumDecl.
class EnumConstantDecl : public ValueDecl, public Mergeable<EnumConstantDecl> {
class EnumConstantDecl : public ValueDecl,
public Mergeable<EnumConstantDecl>,
public APIntStorage {
Stmt *Init; // an integer constant expression
llvm::APSInt Val; // The value.
bool IsUnsigned;

protected:
EnumConstantDecl(DeclContext *DC, SourceLocation L,
EnumConstantDecl(const ASTContext &C, DeclContext *DC, SourceLocation L,
IdentifierInfo *Id, QualType T, Expr *E,
const llvm::APSInt &V)
: ValueDecl(EnumConstant, DC, L, Id, T), Init((Stmt*)E), Val(V) {}
const llvm::APSInt &V);

public:
friend class StmtIteratorBase;
Expand All @@ -3272,10 +3274,15 @@ class EnumConstantDecl : public ValueDecl, public Mergeable<EnumConstantDecl> {

const Expr *getInitExpr() const { return (const Expr*) Init; }
Expr *getInitExpr() { return (Expr*) Init; }
const llvm::APSInt &getInitVal() const { return Val; }
llvm::APSInt getInitVal() const {
return llvm::APSInt(getValue(), IsUnsigned);
}

void setInitExpr(Expr *E) { Init = (Stmt*) E; }
void setInitVal(const llvm::APSInt &V) { Val = V; }
void setInitVal(const ASTContext &C, const llvm::APSInt &V) {
setValue(C, V);
IsUnsigned = V.isUnsigned();
}

SourceRange getSourceRange() const override LLVM_READONLY;

Expand Down
52 changes: 1 addition & 51 deletions clang/include/clang/AST/Expr.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#ifndef LLVM_CLANG_AST_EXPR_H
#define LLVM_CLANG_AST_EXPR_H

#include "clang/AST/APNumericStorage.h"
#include "clang/AST/APValue.h"
#include "clang/AST/ASTVector.h"
#include "clang/AST/ComputeDependence.h"
Expand Down Expand Up @@ -1479,57 +1480,6 @@ class DeclRefExpr final
}
};

/// Used by IntegerLiteral/FloatingLiteral to store the numeric without
/// leaking memory.
///
/// For large floats/integers, APFloat/APInt will allocate memory from the heap
/// to represent these numbers. Unfortunately, when we use a BumpPtrAllocator
/// to allocate IntegerLiteral/FloatingLiteral nodes the memory associated with
/// the APFloat/APInt values will never get freed. APNumericStorage uses
/// ASTContext's allocator for memory allocation.
class APNumericStorage {
union {
uint64_t VAL; ///< Used to store the <= 64 bits integer value.
uint64_t *pVal; ///< Used to store the >64 bits integer value.
};
unsigned BitWidth;

bool hasAllocation() const { return llvm::APInt::getNumWords(BitWidth) > 1; }

APNumericStorage(const APNumericStorage &) = delete;
void operator=(const APNumericStorage &) = delete;

protected:
APNumericStorage() : VAL(0), BitWidth(0) { }

llvm::APInt getIntValue() const {
unsigned NumWords = llvm::APInt::getNumWords(BitWidth);
if (NumWords > 1)
return llvm::APInt(BitWidth, NumWords, pVal);
else
return llvm::APInt(BitWidth, VAL);
}
void setIntValue(const ASTContext &C, const llvm::APInt &Val);
};

class APIntStorage : private APNumericStorage {
public:
llvm::APInt getValue() const { return getIntValue(); }
void setValue(const ASTContext &C, const llvm::APInt &Val) {
setIntValue(C, Val);
}
};

class APFloatStorage : private APNumericStorage {
public:
llvm::APFloat getValue(const llvm::fltSemantics &Semantics) const {
return llvm::APFloat(Semantics, getIntValue());
}
void setValue(const ASTContext &C, const llvm::APFloat &Val) {
setIntValue(C, Val.bitcastToAPInt());
}
};

class IntegerLiteral : public Expr, public APIntStorage {
SourceLocation Loc;

Expand Down
11 changes: 9 additions & 2 deletions clang/lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5369,16 +5369,23 @@ void CapturedDecl::setBody(Stmt *B) { BodyAndNothrow.setPointer(B); }
bool CapturedDecl::isNothrow() const { return BodyAndNothrow.getInt(); }
void CapturedDecl::setNothrow(bool Nothrow) { BodyAndNothrow.setInt(Nothrow); }

EnumConstantDecl::EnumConstantDecl(const ASTContext &C, DeclContext *DC,
SourceLocation L, IdentifierInfo *Id,
QualType T, Expr *E, const llvm::APSInt &V)
: ValueDecl(EnumConstant, DC, L, Id, T), Init((Stmt *)E) {
setInitVal(C, V);
}

EnumConstantDecl *EnumConstantDecl::Create(ASTContext &C, EnumDecl *CD,
SourceLocation L,
IdentifierInfo *Id, QualType T,
Expr *E, const llvm::APSInt &V) {
return new (C, CD) EnumConstantDecl(CD, L, Id, T, E, V);
return new (C, CD) EnumConstantDecl(C, CD, L, Id, T, E, V);
}

EnumConstantDecl *
EnumConstantDecl::CreateDeserialized(ASTContext &C, unsigned ID) {
return new (C, ID) EnumConstantDecl(nullptr, SourceLocation(), nullptr,
return new (C, ID) EnumConstantDecl(C, nullptr, SourceLocation(), nullptr,
QualType(), nullptr, llvm::APSInt());
}

Expand Down
2 changes: 1 addition & 1 deletion clang/lib/CodeGen/CGBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13047,7 +13047,7 @@ Value *CodeGenFunction::EmitBPFBuiltinExpr(unsigned BuiltinID,
const auto *DR = cast<DeclRefExpr>(CE->getSubExpr());
const auto *Enumerator = cast<EnumConstantDecl>(DR->getDecl());

auto &InitVal = Enumerator->getInitVal();
auto InitVal = Enumerator->getInitVal();
std::string InitValStr;
if (InitVal.isNegative() || InitVal > uint64_t(INT64_MAX))
InitValStr = std::to_string(InitVal.getSExtValue());
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20214,7 +20214,7 @@ void Sema::ActOnEnumBody(SourceLocation EnumLoc, SourceRange BraceRange,
// Adjust the APSInt value.
InitVal = InitVal.extOrTrunc(NewWidth);
InitVal.setIsSigned(NewSign);
ECD->setInitVal(InitVal);
ECD->setInitVal(Context, InitVal);

// Adjust the Expr initializer and type.
if (ECD->getInitExpr() &&
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Serialization/ASTReaderDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -910,7 +910,7 @@ void ASTDeclReader::VisitEnumConstantDecl(EnumConstantDecl *ECD) {
VisitValueDecl(ECD);
if (Record.readInt())
ECD->setInitExpr(Record.readExpr());
ECD->setInitVal(Record.readAPSInt());
ECD->setInitVal(Reader.getContext(), Record.readAPSInt());
mergeMergeable(ECD);
}

Expand Down