37 changes: 31 additions & 6 deletions clang/include/clang/AST/DeclAccessPair.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

#include "clang/Basic/Specifiers.h"
#include "llvm/Support/DataTypes.h"
#include "llvm/Support/Endian.h"

namespace clang {

Expand All @@ -27,9 +28,17 @@ class NamedDecl;
/// A POD class for pairing a NamedDecl* with an access specifier.
/// Can be put into unions.
class DeclAccessPair {
uintptr_t Ptr; // we'd use llvm::PointerUnion, but it isn't trivial
/// Use the lower 2 bit to store AccessSpecifier. Use the higher
/// 61 bit to store the pointer to a NamedDecl or the DeclID to
/// a NamedDecl. If the 3rd bit is set, storing the DeclID, otherwise
/// storing the pointer.
llvm::support::detail::packed_endian_specific_integral<
uint64_t, llvm::endianness::native, alignof(void *)>
Ptr;

enum { Mask = 0x3 };
enum { ASMask = 0x3, Mask = 0x7 };

bool isDeclID() const { return (Ptr >> 2) & 0x1; }

public:
static DeclAccessPair make(NamedDecl *D, AccessSpecifier AS) {
Expand All @@ -38,12 +47,22 @@ class DeclAccessPair {
return p;
}

static DeclAccessPair makeLazy(uint64_t ID, AccessSpecifier AS) {
DeclAccessPair p;
p.Ptr = (ID << 3) | (0x1 << 2) | uint64_t(AS);
return p;
}

uint64_t getDeclID() const {
assert(isDeclID());
return (~Mask & Ptr) >> 3;
}

NamedDecl *getDecl() const {
assert(!isDeclID());
return reinterpret_cast<NamedDecl*>(~Mask & Ptr);
}
AccessSpecifier getAccess() const {
return AccessSpecifier(Mask & Ptr);
}
AccessSpecifier getAccess() const { return AccessSpecifier(ASMask & Ptr); }

void setDecl(NamedDecl *D) {
set(D, getAccess());
Expand All @@ -52,12 +71,18 @@ class DeclAccessPair {
set(getDecl(), AS);
}
void set(NamedDecl *D, AccessSpecifier AS) {
Ptr = uintptr_t(AS) | reinterpret_cast<uintptr_t>(D);
Ptr = uint64_t(AS) | reinterpret_cast<uint64_t>(D);
}

operator NamedDecl*() const { return getDecl(); }
NamedDecl *operator->() const { return getDecl(); }
};

// Make sure DeclAccessPair is pointer-aligned types.
static_assert(alignof(DeclAccessPair) == alignof(void *));
// Make sure DeclAccessPair is still POD.
static_assert(std::is_standard_layout_v<DeclAccessPair> &&
std::is_trivial_v<DeclAccessPair>);
}

#endif
17 changes: 3 additions & 14 deletions clang/include/clang/AST/DeclBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -708,10 +708,7 @@ class alignas(8) Decl {

/// Set the owning module ID. This may only be called for
/// deserialized Decls.
void setOwningModuleID(unsigned ID) {
assert(isFromASTFile() && "Only works on a deserialized declaration");
*((unsigned*)this - 2) = ID;
}
void setOwningModuleID(unsigned ID);

public:
/// Determine the availability of the given declaration.
Expand Down Expand Up @@ -784,19 +781,11 @@ class alignas(8) Decl {

/// Retrieve the global declaration ID associated with this
/// declaration, which specifies where this Decl was loaded from.
GlobalDeclID getGlobalID() const {
if (isFromASTFile())
return (*((const GlobalDeclID *)this - 1));
return GlobalDeclID();
}
GlobalDeclID getGlobalID() const;

/// Retrieve the global ID of the module that owns this particular
/// declaration.
unsigned getOwningModuleID() const {
if (isFromASTFile())
return *((const unsigned*)this - 2);
return 0;
}
unsigned getOwningModuleID() const;

private:
Module *getOwningModuleSlow() const;
Expand Down
18 changes: 17 additions & 1 deletion clang/include/clang/AST/DeclID.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
#include "llvm/ADT/DenseMapInfo.h"
#include "llvm/ADT/iterator.h"

#include <climits>

namespace clang {

/// Predefined declaration IDs.
Expand Down Expand Up @@ -107,12 +109,16 @@ class DeclIDBase {
///
/// DeclID should only be used directly in serialization. All other users
/// should use LocalDeclID or GlobalDeclID.
using DeclID = uint32_t;
using DeclID = uint64_t;

protected:
DeclIDBase() : ID(PREDEF_DECL_NULL_ID) {}
explicit DeclIDBase(DeclID ID) : ID(ID) {}

explicit DeclIDBase(unsigned LocalID, unsigned ModuleFileIndex) {
ID = (DeclID)LocalID | ((DeclID)ModuleFileIndex << 32);
}

public:
DeclID get() const { return ID; }

Expand All @@ -124,6 +130,10 @@ class DeclIDBase {

bool isInvalid() const { return ID == PREDEF_DECL_NULL_ID; }

unsigned getModuleFileIndex() const { return ID >> 32; }

unsigned getLocalDeclIndex() const;

friend bool operator==(const DeclIDBase &LHS, const DeclIDBase &RHS) {
return LHS.ID == RHS.ID;
}
Expand Down Expand Up @@ -156,6 +166,9 @@ class LocalDeclID : public DeclIDBase {
LocalDeclID(PredefinedDeclIDs ID) : Base(ID) {}
explicit LocalDeclID(DeclID ID) : Base(ID) {}

explicit LocalDeclID(unsigned LocalID, unsigned ModuleFileIndex)
: Base(LocalID, ModuleFileIndex) {}

LocalDeclID &operator++() {
++ID;
return *this;
Expand All @@ -175,6 +188,9 @@ class GlobalDeclID : public DeclIDBase {
GlobalDeclID() : Base() {}
explicit GlobalDeclID(DeclID ID) : Base(ID) {}

explicit GlobalDeclID(unsigned LocalID, unsigned ModuleFileIndex)
: Base(LocalID, ModuleFileIndex) {}

// For DeclIDIterator<GlobalDeclID> to be able to convert a GlobalDeclID
// to a LocalDeclID.
explicit operator LocalDeclID() const { return LocalDeclID(this->ID); }
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/AST/UnresolvedSet.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ class UnresolvedSetIterator : public llvm::iterator_adaptor_base<
// temporaries with defaulted ctors are not zero initialized.
UnresolvedSetIterator() : iterator_adaptor_base(nullptr) {}

uint64_t getDeclID() const { return I->getDeclID(); }
NamedDecl *getDecl() const { return I->getDecl(); }
void setDecl(NamedDecl *ND) const { return I->setDecl(ND); }
AccessSpecifier getAccess() const { return I->getAccess(); }
Expand Down
2 changes: 1 addition & 1 deletion clang/include/clang/Basic/CodeGenOptions.def
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ CODEGENOPT(SeparateNamedSections, 1, 0) ///< Set for -fseparate-named-sections.
CODEGENOPT(EnableAIXExtendedAltivecABI, 1, 0) ///< Set for -mabi=vec-extabi. Enables the extended Altivec ABI on AIX.
CODEGENOPT(XCOFFReadOnlyPointers, 1, 0) ///< Set for -mxcoff-roptr.
CODEGENOPT(AllTocData, 1, 0) ///< AIX -mtocdata
ENUM_CODEGENOPT(FramePointer, FramePointerKind, 2, FramePointerKind::None) /// frame-pointer: all,non-leaf,none
ENUM_CODEGENOPT(FramePointer, FramePointerKind, 2, FramePointerKind::None) /// frame-pointer: all,non-leaf,reserved,none

CODEGENOPT(ClearASTBeforeBackend , 1, 0) ///< Free the AST before running backend code generation. Only works with -disable-free.
CODEGENOPT(DisableFree , 1, 0) ///< Don't free memory.
Expand Down
9 changes: 6 additions & 3 deletions clang/include/clang/Basic/CodeGenOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,15 +127,18 @@ class CodeGenOptions : public CodeGenOptionsBase {
std::string BinutilsVersion;

enum class FramePointerKind {
None, // Omit all frame pointers.
NonLeaf, // Keep non-leaf frame pointers.
All, // Keep all frame pointers.
None, // Omit all frame pointers.
Reserved, // Maintain valid frame pointer chain.
NonLeaf, // Keep non-leaf frame pointers.
All, // Keep all frame pointers.
};

static StringRef getFramePointerKindName(FramePointerKind Kind) {
switch (Kind) {
case FramePointerKind::None:
return "none";
case FramePointerKind::Reserved:
return "reserved";
case FramePointerKind::NonLeaf:
return "non-leaf";
case FramePointerKind::All:
Expand Down
8 changes: 5 additions & 3 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -607,6 +607,8 @@ def note_using_decl_class_member_workaround : Note<
"a const variable|a constexpr variable}0 instead">;
def err_using_decl_can_not_refer_to_namespace : Error<
"using declaration cannot refer to a namespace">;
def note_namespace_using_decl : Note<
"did you mean 'using namespace'?">;
def warn_cxx17_compat_using_decl_scoped_enumerator: Warning<
"using declaration naming a scoped enumerator is incompatible with "
"C++ standards before C++20">, InGroup<CXXPre20Compat>, DefaultIgnore;
Expand Down Expand Up @@ -6088,9 +6090,9 @@ def err_redefinition_different_concept : Error<
"redefinition of concept %0 with different template parameters or requirements">;
def err_tag_reference_non_tag : Error<
"%select{non-struct type|non-class type|non-union type|non-enum "
"type|typedef|type alias|template|type alias template|template "
"template argument}1 %0 cannot be referenced with a "
"%select{struct|interface|union|class|enum}2 specifier">;
"type|typedef|type alias|template|alias template|template "
"template argument}1 %0 cannot be referenced with the '"
"%select{struct|interface|union|class|enum}2' specifier">;
def err_tag_reference_conflict : Error<
"implicit declaration introduced by elaborated type conflicts with a "
"%select{non-struct type|non-class type|non-union type|non-enum "
Expand Down
4 changes: 2 additions & 2 deletions clang/include/clang/Driver/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -7724,8 +7724,8 @@ def pic_is_pie : Flag<["-"], "pic-is-pie">,
MarshallingInfoFlag<LangOpts<"PIE">>;

def mframe_pointer_EQ : Joined<["-"], "mframe-pointer=">,
HelpText<"Specify which frame pointers to retain.">, Values<"all,non-leaf,none">,
NormalizedValuesScope<"CodeGenOptions::FramePointerKind">, NormalizedValues<["All", "NonLeaf", "None"]>,
HelpText<"Specify which frame pointers to retain.">, Values<"all,non-leaf,reserved,none">,
NormalizedValuesScope<"CodeGenOptions::FramePointerKind">, NormalizedValues<["All", "NonLeaf", "Reserved", "None"]>,
MarshallingInfoEnum<CodeGenOpts<"FramePointer">, "None">;


Expand Down
3 changes: 2 additions & 1 deletion clang/include/clang/Driver/ToolChain.h
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,8 @@ class ToolChain {

/// Executes the given \p Executable and returns the stdout.
llvm::Expected<std::unique_ptr<llvm::MemoryBuffer>>
executeToolChainProgram(StringRef Executable) const;
executeToolChainProgram(StringRef Executable,
unsigned SecondsToWait = 0) const;

void setTripleEnvironment(llvm::Triple::EnvironmentType Env);

Expand Down
29 changes: 23 additions & 6 deletions clang/include/clang/Serialization/ASTBitCodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,12 @@ class DeclOffset {
}
};

// The unaligned decl ID used in the Blobs of bistreams.
using unaligned_decl_id_t =
llvm::support::detail::packed_endian_specific_integral<
serialization::DeclID, llvm::endianness::native,
llvm::support::unaligned>;

/// The number of predefined preprocessed entity IDs.
const unsigned int NUM_PREDEF_PP_ENTITY_IDS = 1;

Expand Down Expand Up @@ -1980,33 +1986,44 @@ enum CleanupObjectKind { COK_Block, COK_CompoundLiteral };

/// Describes the categories of an Objective-C class.
struct ObjCCategoriesInfo {
// The ID of the definition
LocalDeclID DefinitionID;
// The ID of the definition. Use unaligned_decl_id_t to keep
// ObjCCategoriesInfo 32-bit aligned.
unaligned_decl_id_t DefinitionID;

// Offset into the array of category lists.
unsigned Offset;

ObjCCategoriesInfo() = default;
ObjCCategoriesInfo(LocalDeclID ID, unsigned Offset)
: DefinitionID(ID.get()), Offset(Offset) {}

LocalDeclID getDefinitionID() const { return LocalDeclID(DefinitionID); }

friend bool operator<(const ObjCCategoriesInfo &X,
const ObjCCategoriesInfo &Y) {
return X.DefinitionID < Y.DefinitionID;
return X.getDefinitionID() < Y.getDefinitionID();
}

friend bool operator>(const ObjCCategoriesInfo &X,
const ObjCCategoriesInfo &Y) {
return X.DefinitionID > Y.DefinitionID;
return X.getDefinitionID() > Y.getDefinitionID();
}

friend bool operator<=(const ObjCCategoriesInfo &X,
const ObjCCategoriesInfo &Y) {
return X.DefinitionID <= Y.DefinitionID;
return X.getDefinitionID() <= Y.getDefinitionID();
}

friend bool operator>=(const ObjCCategoriesInfo &X,
const ObjCCategoriesInfo &Y) {
return X.DefinitionID >= Y.DefinitionID;
return X.getDefinitionID() >= Y.getDefinitionID();
}
};

static_assert(alignof(ObjCCategoriesInfo) <= 4);
static_assert(std::is_standard_layout_v<ObjCCategoriesInfo> &&
std::is_trivial_v<ObjCCategoriesInfo>);

/// A key used when looking up entities by \ref DeclarationName.
///
/// Different \ref DeclarationNames are mapped to different keys, but the
Expand Down
36 changes: 16 additions & 20 deletions clang/include/clang/Serialization/ASTReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -504,12 +504,6 @@ class ASTReader
/// = I + 1 has already been loaded.
llvm::PagedVector<Decl *> DeclsLoaded;

using GlobalDeclMapType = ContinuousRangeMap<GlobalDeclID, ModuleFile *, 4>;

/// Mapping from global declaration IDs to the module in which the
/// declaration resides.
GlobalDeclMapType GlobalDeclMap;

using FileOffset = std::pair<ModuleFile *, uint64_t>;
using FileOffsetsTy = SmallVector<FileOffset, 2>;
using DeclUpdateOffsetsMap = llvm::DenseMap<GlobalDeclID, FileOffsetsTy>;
Expand Down Expand Up @@ -592,10 +586,11 @@ class ASTReader

struct FileDeclsInfo {
ModuleFile *Mod = nullptr;
ArrayRef<LocalDeclID> Decls;
ArrayRef<serialization::unaligned_decl_id_t> Decls;

FileDeclsInfo() = default;
FileDeclsInfo(ModuleFile *Mod, ArrayRef<LocalDeclID> Decls)
FileDeclsInfo(ModuleFile *Mod,
ArrayRef<serialization::unaligned_decl_id_t> Decls)
: Mod(Mod), Decls(Decls) {}
};

Expand All @@ -604,11 +599,7 @@ class ASTReader

/// An array of lexical contents of a declaration context, as a sequence of
/// Decl::Kind, DeclID pairs.
using unaligned_decl_id_t =
llvm::support::detail::packed_endian_specific_integral<
serialization::DeclID, llvm::endianness::native,
llvm::support::unaligned>;
using LexicalContents = ArrayRef<unaligned_decl_id_t>;
using LexicalContents = ArrayRef<serialization::unaligned_decl_id_t>;

/// Map from a DeclContext to its lexical contents.
llvm::DenseMap<const DeclContext*, std::pair<ModuleFile*, LexicalContents>>
Expand Down Expand Up @@ -1489,22 +1480,23 @@ class ASTReader
unsigned ClientLoadCapabilities);

public:
class ModuleDeclIterator : public llvm::iterator_adaptor_base<
ModuleDeclIterator, const LocalDeclID *,
std::random_access_iterator_tag, const Decl *,
ptrdiff_t, const Decl *, const Decl *> {
class ModuleDeclIterator
: public llvm::iterator_adaptor_base<
ModuleDeclIterator, const serialization::unaligned_decl_id_t *,
std::random_access_iterator_tag, const Decl *, ptrdiff_t,
const Decl *, const Decl *> {
ASTReader *Reader = nullptr;
ModuleFile *Mod = nullptr;

public:
ModuleDeclIterator() : iterator_adaptor_base(nullptr) {}

ModuleDeclIterator(ASTReader *Reader, ModuleFile *Mod,
const LocalDeclID *Pos)
const serialization::unaligned_decl_id_t *Pos)
: iterator_adaptor_base(Pos), Reader(Reader), Mod(Mod) {}

value_type operator*() const {
return Reader->GetDecl(Reader->getGlobalDeclID(*Mod, *I));
return Reader->GetDecl(Reader->getGlobalDeclID(*Mod, (LocalDeclID)*I));
}

value_type operator->() const { return **this; }
Expand Down Expand Up @@ -1544,6 +1536,9 @@ class ASTReader
StringRef Arg2 = StringRef(), StringRef Arg3 = StringRef()) const;
void Error(llvm::Error &&Err) const;

/// Translate a \param GlobalDeclID to the index of DeclsLoaded array.
unsigned translateGlobalDeclIDToIndex(GlobalDeclID ID) const;

public:
/// Load the AST file and validate its contents against the given
/// Preprocessor.
Expand Down Expand Up @@ -1915,7 +1910,8 @@ class ASTReader

/// Retrieve the module file that owns the given declaration, or NULL
/// if the declaration is not from a module file.
ModuleFile *getOwningModuleFile(const Decl *D);
ModuleFile *getOwningModuleFile(const Decl *D) const;
ModuleFile *getOwningModuleFile(GlobalDeclID ID) const;

/// Returns the source location for the decl \p ID.
SourceLocation getSourceLocationForDeclID(GlobalDeclID ID);
Expand Down
18 changes: 3 additions & 15 deletions clang/include/clang/Serialization/ModuleFile.h
Original file line number Diff line number Diff line change
Expand Up @@ -454,23 +454,11 @@ class ModuleFile {
/// by the declaration ID (-1).
const DeclOffset *DeclOffsets = nullptr;

/// Base declaration ID for declarations local to this module.
serialization::DeclID BaseDeclID = 0;

/// Remapping table for declaration IDs in this module.
ContinuousRangeMap<serialization::DeclID, int, 2> DeclRemap;

/// Mapping from the module files that this module file depends on
/// to the base declaration ID for that module as it is understood within this
/// module.
///
/// This is effectively a reverse global-to-local mapping for declaration
/// IDs, so that we can interpret a true global ID (for this translation unit)
/// as a local ID (for this module file).
llvm::DenseMap<ModuleFile *, serialization::DeclID> GlobalToLocalDeclIDs;
/// Base declaration index in ASTReader for declarations local to this module.
unsigned BaseDeclIndex = 0;

/// Array of file-level DeclIDs sorted by file.
const LocalDeclID *FileSortedDecls = nullptr;
const serialization::unaligned_decl_id_t *FileSortedDecls = nullptr;
unsigned NumFileSortedDecls = 0;

/// Array of category list location information within this
Expand Down
2 changes: 1 addition & 1 deletion clang/include/clang/Serialization/ModuleManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ namespace serialization {
/// Manages the set of modules loaded by an AST reader.
class ModuleManager {
/// The chain of AST files, in the order in which we started to load
/// them (this order isn't really useful for anything).
/// them.
SmallVector<std::unique_ptr<ModuleFile>, 2> Chain;

/// The chain of non-module PCH files. The first entry is the one named
Expand Down
4 changes: 2 additions & 2 deletions clang/include/clang/Tooling/Syntax/Tokens.h
Original file line number Diff line number Diff line change
Expand Up @@ -292,9 +292,9 @@ class TokenBuffer {
/// "DECL", "(", "a", ")", ";"}
llvm::ArrayRef<syntax::Token> spelledTokens(FileID FID) const;

/// Returns the spelled Token starting at Loc, if there are no such tokens
/// Returns the spelled Token containing the Loc, if there are no such tokens
/// returns nullptr.
const syntax::Token *spelledTokenAt(SourceLocation Loc) const;
const syntax::Token *spelledTokenContaining(SourceLocation Loc) const;

/// Get all tokens that expand a macro in \p FID. For the following input
/// #define FOO B
Expand Down
40 changes: 33 additions & 7 deletions clang/lib/AST/DeclBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,18 +74,17 @@ void *Decl::operator new(std::size_t Size, const ASTContext &Context,
GlobalDeclID ID, std::size_t Extra) {
// Allocate an extra 8 bytes worth of storage, which ensures that the
// resulting pointer will still be 8-byte aligned.
static_assert(sizeof(unsigned) * 2 >= alignof(Decl),
"Decl won't be misaligned");
static_assert(sizeof(uint64_t) >= alignof(Decl), "Decl won't be misaligned");
void *Start = Context.Allocate(Size + Extra + 8);
void *Result = (char*)Start + 8;

unsigned *PrefixPtr = (unsigned *)Result - 2;
uint64_t *PrefixPtr = (uint64_t *)Result - 1;

// Zero out the first 4 bytes; this is used to store the owning module ID.
PrefixPtr[0] = 0;
*PrefixPtr = ID.get();

// Store the global declaration ID in the second 4 bytes.
PrefixPtr[1] = ID.get();
// We leave the upper 16 bits to store the module IDs. 48 bits should be
// sufficient to store a declaration ID.
assert(*PrefixPtr < llvm::maskTrailingOnes<uint64_t>(48));

return Result;
}
Expand All @@ -111,6 +110,29 @@ void *Decl::operator new(std::size_t Size, const ASTContext &Ctx,
return ::operator new(Size + Extra, Ctx);
}

GlobalDeclID Decl::getGlobalID() const {
if (!isFromASTFile())
return GlobalDeclID();
// See the comments in `Decl::operator new` for details.
uint64_t ID = *((const uint64_t *)this - 1);
return GlobalDeclID(ID & llvm::maskTrailingOnes<uint64_t>(48));
}

unsigned Decl::getOwningModuleID() const {
if (!isFromASTFile())
return 0;

uint64_t ID = *((const uint64_t *)this - 1);
return ID >> 48;
}

void Decl::setOwningModuleID(unsigned ID) {
assert(isFromASTFile() && "Only works on a deserialized declaration");
uint64_t *IDAddress = (uint64_t *)this - 1;
*IDAddress &= llvm::maskTrailingOnes<uint64_t>(48);
*IDAddress |= (uint64_t)ID << 48;
}

Module *Decl::getOwningModuleSlow() const {
assert(isFromASTFile() && "Not from AST file?");
return getASTContext().getExternalSource()->getModule(getOwningModuleID());
Expand Down Expand Up @@ -2163,3 +2185,7 @@ DependentDiagnostic *DependentDiagnostic::Create(ASTContext &C,

return DD;
}

unsigned DeclIDBase::getLocalDeclIndex() const {
return ID & llvm::maskTrailingOnes<DeclID>(32);
}
4 changes: 2 additions & 2 deletions clang/lib/AST/DeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ void LazyASTUnresolvedSet::getFromExternalSource(ASTContext &C) const {
assert(Source && "getFromExternalSource with no external source");

for (ASTUnresolvedSet::iterator I = Impl.begin(); I != Impl.end(); ++I)
I.setDecl(cast<NamedDecl>(Source->GetExternalDecl(
GlobalDeclID(reinterpret_cast<uintptr_t>(I.getDecl()) >> 2))));
I.setDecl(
cast<NamedDecl>(Source->GetExternalDecl(GlobalDeclID(I.getDeclID()))));
Impl.Decls.setLazy(false);
}

Expand Down
40 changes: 13 additions & 27 deletions clang/lib/AST/Interp/ByteCodeExprGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1104,14 +1104,9 @@ bool ByteCodeExprGen<Emitter>::visitInitList(ArrayRef<const Expr *> Inits,
if (!this->visit(Init))
return false;

if (FieldToInit->isBitField()) {
if (!this->emitInitBitField(T, FieldToInit, E))
return false;
} else {
if (!this->emitInitField(T, FieldToInit->Offset, E))
return false;
}
return this->emitPopPtr(E);
if (FieldToInit->isBitField())
return this->emitInitBitField(T, FieldToInit, E);
return this->emitInitField(T, FieldToInit->Offset, E);
};

auto initCompositeField = [=](const Record::Field *FieldToInit,
Expand Down Expand Up @@ -1147,9 +1142,6 @@ bool ByteCodeExprGen<Emitter>::visitInitList(ArrayRef<const Expr *> Inits,
else
FToInit = cast<CXXParenListInitExpr>(E)->getInitializedFieldInUnion();

if (!this->emitDupPtr(E))
return false;

const Record::Field *FieldToInit = R->getField(FToInit);
if (std::optional<PrimType> T = classify(Init)) {
if (!initPrimitiveField(FieldToInit, Init, *T))
Expand All @@ -1169,8 +1161,6 @@ bool ByteCodeExprGen<Emitter>::visitInitList(ArrayRef<const Expr *> Inits,
while (InitIndex < R->getNumFields() &&
R->getField(InitIndex)->Decl->isUnnamedBitField())
++InitIndex;
if (!this->emitDupPtr(E))
return false;

if (std::optional<PrimType> T = classify(Init)) {
const Record::Field *FieldToInit = R->getField(InitIndex);
Expand All @@ -1180,7 +1170,7 @@ bool ByteCodeExprGen<Emitter>::visitInitList(ArrayRef<const Expr *> Inits,
} else {
// Initializer for a direct base class.
if (const Record::Base *B = R->getBase(Init->getType())) {
if (!this->emitGetPtrBasePop(B->Offset, Init))
if (!this->emitGetPtrBase(B->Offset, Init))
return false;

if (!this->visitInitializer(Init))
Expand Down Expand Up @@ -1513,7 +1503,7 @@ bool ByteCodeExprGen<Emitter>::VisitMemberExpr(const MemberExpr *E) {
// Leave a pointer to the field on the stack.
if (F->Decl->getType()->isReferenceType())
return this->emitGetFieldPop(PT_Ptr, F->Offset, E) && maybeLoadValue();
return this->emitGetPtrField(F->Offset, E) && maybeLoadValue();
return this->emitGetPtrFieldPop(F->Offset, E) && maybeLoadValue();
}

return false;
Expand Down Expand Up @@ -2147,9 +2137,6 @@ bool ByteCodeExprGen<Emitter>::VisitLambdaExpr(const LambdaExpr *E) {
if (!this->emitInitField(*T, F.Offset, E))
return false;
} else {
if (!this->emitDupPtr(E))
return false;

if (!this->emitGetPtrField(F.Offset, E))
return false;

Expand Down Expand Up @@ -2846,9 +2833,6 @@ bool ByteCodeExprGen<Emitter>::visitZeroRecordInitializer(const Record *R,
continue;
}

// TODO: Add GetPtrFieldPop and get rid of this dup.
if (!this->emitDupPtr(E))
return false;
if (!this->emitGetPtrField(Field.Offset, E))
return false;

Expand Down Expand Up @@ -3258,8 +3242,6 @@ bool ByteCodeExprGen<Emitter>::visitAPValueInitializer(const APValue &Val,
PrimType ElemT = classifyPrim(ArrType->getElementType());
assert(ArrType);

if (!this->emitDupPtr(E))
return false;
if (!this->emitGetPtrField(RF->Offset, E))
return false;

Expand All @@ -3273,8 +3255,6 @@ bool ByteCodeExprGen<Emitter>::visitAPValueInitializer(const APValue &Val,
if (!this->emitPopPtr(E))
return false;
} else if (F.isStruct() || F.isUnion()) {
if (!this->emitDupPtr(E))
return false;
if (!this->emitGetPtrField(RF->Offset, E))
return false;
if (!this->visitAPValueInitializer(F, E))
Expand Down Expand Up @@ -3914,6 +3894,14 @@ bool ByteCodeExprGen<Emitter>::visitDeclRef(const ValueDecl *D, const Expr *E) {
if (IsPtr)
return this->emitGetThisFieldPtr(Offset, E);
return this->emitGetPtrThisField(Offset, E);
} else if (const auto *DRE = dyn_cast<DeclRefExpr>(E);
DRE && DRE->refersToEnclosingVariableOrCapture()) {
if (const auto *VD = dyn_cast<VarDecl>(D); VD && VD->isInitCapture()) {
if (!this->visitVarDecl(cast<VarDecl>(D)))
return false;
// Retry.
return this->visitDeclRef(D, E);
}
}

// Try to lazily visit (or emit dummy pointers for) declarations
Expand Down Expand Up @@ -4201,8 +4189,6 @@ bool ByteCodeExprGen<Emitter>::emitRecordDestruction(const Record *R) {
for (const Record::Field &Field : llvm::reverse(R->fields())) {
const Descriptor *D = Field.Desc;
if (!D->isPrimitive() && !D->isPrimitiveArray()) {
if (!this->emitDupPtr(SourceInfo{}))
return false;
if (!this->emitGetPtrField(Field.Offset, SourceInfo{}))
return false;
if (!this->emitDestruction(D))
Expand Down
5 changes: 5 additions & 0 deletions clang/lib/AST/Interp/EvalEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ EvaluationResult EvalEmitter::interpretExpr(const Expr *E,
bool ConvertResultToRValue) {
S.setEvalLocation(E->getExprLoc());
this->ConvertResultToRValue = ConvertResultToRValue;
this->CheckFullyInitialized = isa<ConstantExpr>(E);
EvalResult.setSource(E);

if (!this->visitExpr(E)) {
Expand Down Expand Up @@ -175,6 +176,10 @@ bool EvalEmitter::emitRetVoid(const SourceInfo &Info) {

bool EvalEmitter::emitRetValue(const SourceInfo &Info) {
const auto &Ptr = S.Stk.pop<Pointer>();

if (CheckFullyInitialized && !EvalResult.checkFullyInitialized(S, Ptr))
return false;

if (std::optional<APValue> APV = Ptr.toRValue(S.getCtx())) {
EvalResult.setValue(*APV);
return true;
Expand Down
14 changes: 6 additions & 8 deletions clang/lib/AST/Interp/EvaluationResult.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,16 +141,14 @@ bool EvaluationResult::checkFullyInitialized(InterpState &S,
const Pointer &Ptr) const {
assert(Source);
assert(empty());

// Our Source must be a VarDecl.
const Decl *SourceDecl = Source.dyn_cast<const Decl *>();
assert(SourceDecl);
const auto *VD = cast<VarDecl>(SourceDecl);
assert(VD->getType()->isRecordType() || VD->getType()->isArrayType());
SourceLocation InitLoc = VD->getAnyInitializer()->getExprLoc();

assert(!Ptr.isZero());

SourceLocation InitLoc;
if (const auto *D = Source.dyn_cast<const Decl *>())
InitLoc = cast<VarDecl>(D)->getAnyInitializer()->getExprLoc();
else if (const auto *E = Source.dyn_cast<const Expr *>())
InitLoc = E->getExprLoc();

if (const Record *R = Ptr.getRecord())
return CheckFieldsInitialized(S, InitLoc, Ptr, R);
const auto *CAT =
Expand Down
25 changes: 24 additions & 1 deletion clang/lib/AST/Interp/Interp.h
Original file line number Diff line number Diff line change
Expand Up @@ -1283,9 +1283,32 @@ inline bool GetPtrGlobal(InterpState &S, CodePtr OpPC, uint32_t I) {
return true;
}

/// 1) Pops a Pointer from the stack
/// 1) Peeks a Pointer
/// 2) Pushes Pointer.atField(Off) on the stack
inline bool GetPtrField(InterpState &S, CodePtr OpPC, uint32_t Off) {
const Pointer &Ptr = S.Stk.peek<Pointer>();

if (S.getLangOpts().CPlusPlus && S.inConstantContext() &&
!CheckNull(S, OpPC, Ptr, CSK_Field))
return false;

if (!CheckExtern(S, OpPC, Ptr))
return false;
if (!CheckRange(S, OpPC, Ptr, CSK_Field))
return false;
if (!CheckArray(S, OpPC, Ptr))
return false;
if (!CheckSubobject(S, OpPC, Ptr, CSK_Field))
return false;

if (Ptr.isBlockPointer() && Off > Ptr.block()->getSize())
return false;

S.Stk.push<Pointer>(Ptr.atField(Off));
return true;
}

inline bool GetPtrFieldPop(InterpState &S, CodePtr OpPC, uint32_t Off) {
const Pointer &Ptr = S.Stk.pop<Pointer>();

if (S.getLangOpts().CPlusPlus && S.inConstantContext() &&
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/AST/Interp/MemberPointer.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ class MemberPointer final {
bool hasBase() const { return !Base.isZero(); }

void print(llvm::raw_ostream &OS) const {
OS << "MemberPtr(" << Base << " " << (void *)Dcl << " + " << PtrOffset
OS << "MemberPtr(" << Base << " " << (const void *)Dcl << " + " << PtrOffset
<< ")";
}

Expand Down
48 changes: 13 additions & 35 deletions clang/lib/AST/Interp/Opcodes.td
Original file line number Diff line number Diff line change
Expand Up @@ -281,53 +281,31 @@ def Null : Opcode {
//===----------------------------------------------------------------------===//
// Pointer generation
//===----------------------------------------------------------------------===//
class OffsetOpcode : Opcode {
let Args = [ArgUint32];
}

// [] -> [Pointer]
def GetPtrLocal : Opcode {
// Offset of local.
let Args = [ArgUint32];
def GetPtrLocal : OffsetOpcode {
bit HasCustomEval = 1;
}
// [] -> [Pointer]
def GetPtrParam : Opcode {
// Offset of parameter.
let Args = [ArgUint32];
}
def GetPtrParam : OffsetOpcode;
// [] -> [Pointer]
def GetPtrGlobal : Opcode {
// Index of global.
let Args = [ArgUint32];
}
def GetPtrGlobal : OffsetOpcode;
// [Pointer] -> [Pointer]
def GetPtrField : Opcode {
// Offset of field.
let Args = [ArgUint32];
}
def GetPtrField : OffsetOpcode;
def GetPtrFieldPop : OffsetOpcode;
// [Pointer] -> [Pointer]
def GetPtrActiveField : Opcode {
// Offset of field.
let Args = [ArgUint32];
}
def GetPtrActiveField : OffsetOpcode;
// [] -> [Pointer]
def GetPtrActiveThisField : Opcode {
// Offset of field.
let Args = [ArgUint32];
}
def GetPtrActiveThisField : OffsetOpcode;
// [] -> [Pointer]
def GetPtrThisField : Opcode {
// Offset of field.
let Args = [ArgUint32];
}
def GetPtrThisField : OffsetOpcode;
// [Pointer] -> [Pointer]
def GetPtrBase : Opcode {
// Offset of field, which is a base.
let Args = [ArgUint32];
}
def GetPtrBase : OffsetOpcode;
// [Pointer] -> [Pointer]
def GetPtrBasePop : Opcode {
// Offset of field, which is a base.
let Args = [ArgUint32];
}
def GetPtrBasePop : OffsetOpcode;
def GetMemberPtrBasePop : Opcode {
// Offset of field, which is a base.
let Args = [ArgSint32];
Expand Down
46 changes: 24 additions & 22 deletions clang/lib/AST/Interp/Pointer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,53 +64,55 @@ Pointer::~Pointer() {
}

void Pointer::operator=(const Pointer &P) {
if (!this->isIntegralPointer() || !P.isBlockPointer())
assert(P.StorageKind == StorageKind || (this->isZero() && P.isZero()));

// If the current storage type is Block, we need to remove
// this pointer from the block.
bool WasBlockPointer = isBlockPointer();
StorageKind = P.StorageKind;
if (StorageKind == Storage::Block) {
Block *Old = PointeeStorage.BS.Pointee;
if (WasBlockPointer && PointeeStorage.BS.Pointee)
if (WasBlockPointer && Old) {
PointeeStorage.BS.Pointee->removePointer(this);
Old->cleanup();
}
}

Offset = P.Offset;
StorageKind = P.StorageKind;
Offset = P.Offset;

if (P.isBlockPointer()) {
PointeeStorage.BS = P.PointeeStorage.BS;
PointeeStorage.BS.Pointee = P.PointeeStorage.BS.Pointee;

if (PointeeStorage.BS.Pointee)
PointeeStorage.BS.Pointee->addPointer(this);

if (WasBlockPointer && Old)
Old->cleanup();

} else if (StorageKind == Storage::Int) {
} else if (P.isIntegralPointer()) {
PointeeStorage.Int = P.PointeeStorage.Int;
} else {
assert(false && "Unhandled storage kind");
}
}

void Pointer::operator=(Pointer &&P) {
if (!this->isIntegralPointer() || !P.isBlockPointer())
assert(P.StorageKind == StorageKind || (this->isZero() && P.isZero()));

// If the current storage type is Block, we need to remove
// this pointer from the block.
bool WasBlockPointer = isBlockPointer();
StorageKind = P.StorageKind;
if (StorageKind == Storage::Block) {
Block *Old = PointeeStorage.BS.Pointee;
if (WasBlockPointer && PointeeStorage.BS.Pointee)
if (WasBlockPointer && Old) {
PointeeStorage.BS.Pointee->removePointer(this);
Old->cleanup();
}
}

Offset = P.Offset;
StorageKind = P.StorageKind;
Offset = P.Offset;

if (P.isBlockPointer()) {
PointeeStorage.BS = P.PointeeStorage.BS;
PointeeStorage.BS.Pointee = P.PointeeStorage.BS.Pointee;

if (PointeeStorage.BS.Pointee)
PointeeStorage.BS.Pointee->addPointer(this);

if (WasBlockPointer && Old)
Old->cleanup();

} else if (StorageKind == Storage::Int) {
} else if (P.isIntegralPointer()) {
PointeeStorage.Int = P.PointeeStorage.Int;
} else {
assert(false && "Unhandled storage kind");
Expand Down
5 changes: 4 additions & 1 deletion clang/lib/Basic/Targets.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -673,8 +673,11 @@ std::unique_ptr<TargetInfo> AllocateTarget(const llvm::Triple &Triple,
}
case llvm::Triple::spirv64: {
if (os != llvm::Triple::UnknownOS ||
Triple.getEnvironment() != llvm::Triple::UnknownEnvironment)
Triple.getEnvironment() != llvm::Triple::UnknownEnvironment) {
if (os == llvm::Triple::OSType::AMDHSA)
return std::make_unique<SPIRV64AMDGCNTargetInfo>(Triple, Opts);
return nullptr;
}
return std::make_unique<SPIRV64TargetInfo>(Triple, Opts);
}
case llvm::Triple::wasm32:
Expand Down
75 changes: 75 additions & 0 deletions clang/lib/Basic/Targets/SPIR.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
//===----------------------------------------------------------------------===//

#include "SPIR.h"
#include "AMDGPU.h"
#include "Targets.h"
#include "llvm/TargetParser/TargetParser.h"

using namespace clang;
using namespace clang::targets;
Expand Down Expand Up @@ -54,3 +56,76 @@ void SPIRV64TargetInfo::getTargetDefines(const LangOptions &Opts,
BaseSPIRVTargetInfo::getTargetDefines(Opts, Builder);
DefineStd(Builder, "SPIRV64", Opts);
}

static const AMDGPUTargetInfo AMDGPUTI(llvm::Triple("amdgcn-amd-amdhsa"), {});

ArrayRef<const char *> SPIRV64AMDGCNTargetInfo::getGCCRegNames() const {
return AMDGPUTI.getGCCRegNames();
}

bool SPIRV64AMDGCNTargetInfo::initFeatureMap(
llvm::StringMap<bool> &Features, DiagnosticsEngine &Diags, StringRef,
const std::vector<std::string> &FeatureVec) const {
llvm::AMDGPU::fillAMDGPUFeatureMap({}, getTriple(), Features);

return TargetInfo::initFeatureMap(Features, Diags, {}, FeatureVec);
}

bool SPIRV64AMDGCNTargetInfo::validateAsmConstraint(
const char *&Name, TargetInfo::ConstraintInfo &Info) const {
return AMDGPUTI.validateAsmConstraint(Name, Info);
}

std::string
SPIRV64AMDGCNTargetInfo::convertConstraint(const char *&Constraint) const {
return AMDGPUTI.convertConstraint(Constraint);
}

ArrayRef<Builtin::Info> SPIRV64AMDGCNTargetInfo::getTargetBuiltins() const {
return AMDGPUTI.getTargetBuiltins();
}

void SPIRV64AMDGCNTargetInfo::getTargetDefines(const LangOptions &Opts,
MacroBuilder &Builder) const {
BaseSPIRVTargetInfo::getTargetDefines(Opts, Builder);
DefineStd(Builder, "SPIRV64", Opts);

Builder.defineMacro("__AMD__");
Builder.defineMacro("__AMDGPU__");
Builder.defineMacro("__AMDGCN__");
}

void SPIRV64AMDGCNTargetInfo::setAuxTarget(const TargetInfo *Aux) {
assert(Aux && "Cannot invoke setAuxTarget without a valid auxiliary target!");

// This is a 1:1 copy of AMDGPUTargetInfo::setAuxTarget()
assert(HalfFormat == Aux->HalfFormat);
assert(FloatFormat == Aux->FloatFormat);
assert(DoubleFormat == Aux->DoubleFormat);

// On x86_64 long double is 80-bit extended precision format, which is
// not supported by AMDGPU. 128-bit floating point format is also not
// supported by AMDGPU. Therefore keep its own format for these two types.
auto SaveLongDoubleFormat = LongDoubleFormat;
auto SaveFloat128Format = Float128Format;
auto SaveLongDoubleWidth = LongDoubleWidth;
auto SaveLongDoubleAlign = LongDoubleAlign;
copyAuxTarget(Aux);
LongDoubleFormat = SaveLongDoubleFormat;
Float128Format = SaveFloat128Format;
LongDoubleWidth = SaveLongDoubleWidth;
LongDoubleAlign = SaveLongDoubleAlign;
// For certain builtin types support on the host target, claim they are
// supported to pass the compilation of the host code during the device-side
// compilation.
// FIXME: As the side effect, we also accept `__float128` uses in the device
// code. To reject these builtin types supported in the host target but not in
// the device target, one approach would support `device_builtin` attribute
// so that we could tell the device builtin types from the host ones. This
// also solves the different representations of the same builtin type, such
// as `size_t` in the MSVC environment.
if (Aux->hasFloat128Type()) {
HasFloat128 = true;
Float128Format = DoubleFormat;
}
}
51 changes: 51 additions & 0 deletions clang/lib/Basic/Targets/SPIR.h
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,57 @@ class LLVM_LIBRARY_VISIBILITY SPIRV64TargetInfo : public BaseSPIRVTargetInfo {
MacroBuilder &Builder) const override;
};

class LLVM_LIBRARY_VISIBILITY SPIRV64AMDGCNTargetInfo final
: public BaseSPIRVTargetInfo {
public:
SPIRV64AMDGCNTargetInfo(const llvm::Triple &Triple, const TargetOptions &Opts)
: BaseSPIRVTargetInfo(Triple, Opts) {
assert(Triple.getArch() == llvm::Triple::spirv64 &&
"Invalid architecture for 64-bit AMDGCN SPIR-V.");
assert(Triple.getVendor() == llvm::Triple::VendorType::AMD &&
"64-bit AMDGCN SPIR-V target must use AMD vendor");
assert(getTriple().getOS() == llvm::Triple::OSType::AMDHSA &&
"64-bit AMDGCN SPIR-V target must use AMDHSA OS");
assert(getTriple().getEnvironment() == llvm::Triple::UnknownEnvironment &&
"64-bit SPIR-V target must use unknown environment type");
PointerWidth = PointerAlign = 64;
SizeType = TargetInfo::UnsignedLong;
PtrDiffType = IntPtrType = TargetInfo::SignedLong;

resetDataLayout("e-i64:64-v16:16-v24:32-v32:32-v48:64-"
"v96:128-v192:256-v256:256-v512:512-v1024:1024-G1-P4-A0");

BFloat16Width = BFloat16Align = 16;
BFloat16Format = &llvm::APFloat::BFloat();

HasLegalHalfType = true;
HasFloat16 = true;
HalfArgsAndReturns = true;
}

bool hasBFloat16Type() const override { return true; }

ArrayRef<const char *> getGCCRegNames() const override;

bool initFeatureMap(llvm::StringMap<bool> &Features, DiagnosticsEngine &Diags,
StringRef,
const std::vector<std::string> &) const override;

bool validateAsmConstraint(const char *&Name,
TargetInfo::ConstraintInfo &Info) const override;

std::string convertConstraint(const char *&Constraint) const override;

ArrayRef<Builtin::Info> getTargetBuiltins() const override;

void getTargetDefines(const LangOptions &Opts,
MacroBuilder &Builder) const override;

void setAuxTarget(const TargetInfo *Aux) override;

bool hasInt128Type() const override { return TargetInfo::hasInt128Type(); }
};

} // namespace targets
} // namespace clang
#endif // LLVM_CLANG_LIB_BASIC_TARGETS_SPIR_H
7 changes: 7 additions & 0 deletions clang/lib/CodeGen/CGBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6012,6 +6012,9 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
llvm::Triple::getArchTypePrefix(getTarget().getTriple().getArch());
if (!Prefix.empty()) {
IntrinsicID = Intrinsic::getIntrinsicForClangBuiltin(Prefix.data(), Name);
if (IntrinsicID == Intrinsic::not_intrinsic && Prefix == "spv" &&
getTarget().getTriple().getOS() == llvm::Triple::OSType::AMDHSA)
IntrinsicID = Intrinsic::getIntrinsicForClangBuiltin("amdgcn", Name);
// NOTE we don't need to perform a compatibility flag check here since the
// intrinsics are declared in Builtins*.def via LANGBUILTIN which filter the
// MS builtins via ALL_MS_LANGUAGES and are filtered earlier.
Expand Down Expand Up @@ -6182,6 +6185,10 @@ static Value *EmitTargetArchBuiltinExpr(CodeGenFunction *CGF,
case llvm::Triple::riscv32:
case llvm::Triple::riscv64:
return CGF->EmitRISCVBuiltinExpr(BuiltinID, E, ReturnValue);
case llvm::Triple::spirv64:
if (CGF->getTarget().getTriple().getOS() != llvm::Triple::OSType::AMDHSA)
return nullptr;
return CGF->EmitAMDGPUBuiltinExpr(BuiltinID, E);
default:
return nullptr;
}
Expand Down
1 change: 1 addition & 0 deletions clang/lib/CodeGen/CGCall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1917,6 +1917,7 @@ static void getTrivialDefaultFunctionAttributes(
case CodeGenOptions::FramePointerKind::None:
// This is the default behavior.
break;
case CodeGenOptions::FramePointerKind::Reserved:
case CodeGenOptions::FramePointerKind::NonLeaf:
case CodeGenOptions::FramePointerKind::All:
FuncAttrs.addAttribute("frame-pointer",
Expand Down
11 changes: 10 additions & 1 deletion clang/lib/CodeGen/CGDebugInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,16 @@ using namespace clang::CodeGen;

static uint32_t getTypeAlignIfRequired(const Type *Ty, const ASTContext &Ctx) {
auto TI = Ctx.getTypeInfo(Ty);
return TI.isAlignRequired() ? TI.Align : 0;
if (TI.isAlignRequired())
return TI.Align;

// MaxFieldAlignmentAttr is the attribute added to types
// declared after #pragma pack(n).
if (auto *Decl = Ty->getAsRecordDecl())
if (Decl->hasAttr<MaxFieldAlignmentAttr>())
return TI.Align;

return 0;
}

static uint32_t getTypeAlignIfRequired(QualType Ty, const ASTContext &Ctx) {
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/CodeGen/CodeGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1328,6 +1328,9 @@ void CodeGenModule::Release() {
case CodeGenOptions::FramePointerKind::None:
// 0 ("none") is the default.
break;
case CodeGenOptions::FramePointerKind::Reserved:
getModule().setFramePointer(llvm::FramePointerKind::Reserved);
break;
case CodeGenOptions::FramePointerKind::NonLeaf:
getModule().setFramePointer(llvm::FramePointerKind::NonLeaf);
break;
Expand Down
8 changes: 4 additions & 4 deletions clang/lib/Driver/ToolChain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,8 @@ ToolChain::ToolChain(const Driver &D, const llvm::Triple &T,
}

llvm::Expected<std::unique_ptr<llvm::MemoryBuffer>>
ToolChain::executeToolChainProgram(StringRef Executable) const {
ToolChain::executeToolChainProgram(StringRef Executable,
unsigned SecondsToWait) const {
llvm::SmallString<64> OutputFile;
llvm::sys::fs::createTemporaryFile("toolchain-program", "txt", OutputFile);
llvm::FileRemover OutputRemover(OutputFile.c_str());
Expand All @@ -115,9 +116,8 @@ ToolChain::executeToolChainProgram(StringRef Executable) const {
};

std::string ErrorMessage;
if (llvm::sys::ExecuteAndWait(Executable, {}, {}, Redirects,
/* SecondsToWait */ 0,
/*MemoryLimit*/ 0, &ErrorMessage))
if (llvm::sys::ExecuteAndWait(Executable, {}, {}, Redirects, SecondsToWait,
/*MemoryLimit=*/0, &ErrorMessage))
return llvm::createStringError(std::error_code(),
Executable + ": " + ErrorMessage);

Expand Down
8 changes: 6 additions & 2 deletions clang/lib/Driver/ToolChains/AMDGPU.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -645,7 +645,11 @@ void amdgpu::getAMDGPUTargetFeatures(const Driver &D,
std::vector<StringRef> &Features) {
// Add target ID features to -target-feature options. No diagnostics should
// be emitted here since invalid target ID is diagnosed at other places.
StringRef TargetID = Args.getLastArgValue(options::OPT_mcpu_EQ);
StringRef TargetID;
if (Args.hasArg(options::OPT_mcpu_EQ))
TargetID = Args.getLastArgValue(options::OPT_mcpu_EQ);
else if (Args.hasArg(options::OPT_march_EQ))
TargetID = Args.getLastArgValue(options::OPT_march_EQ);
if (!TargetID.empty()) {
llvm::StringMap<bool> FeatureMap;
auto OptionalGpuArch = parseTargetID(Triple, TargetID, &FeatureMap);
Expand Down Expand Up @@ -877,7 +881,7 @@ AMDGPUToolChain::getSystemGPUArchs(const ArgList &Args) const {
else
Program = GetProgramPath("amdgpu-arch");

auto StdoutOrErr = executeToolChainProgram(Program);
auto StdoutOrErr = executeToolChainProgram(Program, /*SecondsToWait=*/10);
if (!StdoutOrErr)
return StdoutOrErr.takeError();

Expand Down
5 changes: 0 additions & 5 deletions clang/lib/Driver/ToolChains/AMDGPUOpenMP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,9 @@ void AMDGPUOpenMPToolChain::addClangTargetOptions(
Action::OffloadKind DeviceOffloadingKind) const {
HostTC.addClangTargetOptions(DriverArgs, CC1Args, DeviceOffloadingKind);

StringRef GPUArch = DriverArgs.getLastArgValue(options::OPT_march_EQ);
assert(!GPUArch.empty() && "Must have an explicit GPU arch.");

assert(DeviceOffloadingKind == Action::OFK_OpenMP &&
"Only OpenMP offloading kinds are supported.");

CC1Args.push_back("-target-cpu");
CC1Args.push_back(DriverArgs.MakeArgStringRef(GPUArch));
CC1Args.push_back("-fcuda-is-device");

if (DriverArgs.hasArg(options::OPT_nogpulib))
Expand Down
2 changes: 0 additions & 2 deletions clang/lib/Driver/ToolChains/Arch/ARM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -799,8 +799,6 @@ llvm::ARM::FPUKind arm::getARMTargetFeatures(const Driver &D,
StringRef FrameChainOption = A->getValue();
if (FrameChainOption.starts_with("aapcs"))
Features.push_back("+aapcs-frame-chain");
if (FrameChainOption == "aapcs+leaf")
Features.push_back("+aapcs-frame-chain-leaf");
}

// CMSE: Check for target 8M (for -mcmse to be applicable) is performed later.
Expand Down
14 changes: 14 additions & 0 deletions clang/lib/Driver/ToolChains/BareMetal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,7 @@ void baremetal::Linker::ConstructJob(Compilation &C, const JobAction &JA,
ArgStringList CmdArgs;

auto &TC = static_cast<const toolchains::BareMetal &>(getToolChain());
const Driver &D = getToolChain().getDriver();
const llvm::Triple::ArchType Arch = TC.getArch();
const llvm::Triple &Triple = getToolChain().getEffectiveTriple();

Expand Down Expand Up @@ -466,6 +467,19 @@ void baremetal::Linker::ConstructJob(Compilation &C, const JobAction &JA,
TC.AddLinkRuntimeLib(Args, CmdArgs);
}

if (D.isUsingLTO()) {
assert(!Inputs.empty() && "Must have at least one input.");
// Find the first filename InputInfo object.
auto Input = llvm::find_if(
Inputs, [](const InputInfo &II) -> bool { return II.isFilename(); });
if (Input == Inputs.end())
// For a very rare case, all of the inputs to the linker are
// InputArg. If that happens, just use the first InputInfo.
Input = Inputs.begin();

addLTOOptions(TC, Args, CmdArgs, Output, *Input,
D.getLTOMode() == LTOK_Thin);
}
if (TC.getTriple().isRISCV())
CmdArgs.push_back("-X");

Expand Down
3 changes: 3 additions & 0 deletions clang/lib/Driver/ToolChains/Clang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5678,6 +5678,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
case CodeGenOptions::FramePointerKind::None:
FPKeepKindStr = "-mframe-pointer=none";
break;
case CodeGenOptions::FramePointerKind::Reserved:
FPKeepKindStr = "-mframe-pointer=reserved";
break;
case CodeGenOptions::FramePointerKind::NonLeaf:
FPKeepKindStr = "-mframe-pointer=non-leaf";
break;
Expand Down
115 changes: 88 additions & 27 deletions clang/lib/Driver/ToolChains/CommonArgs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,14 @@ static bool useFramePointerForTargetByDefault(const llvm::opt::ArgList &Args,
return true;
}

static bool useLeafFramePointerForTargetByDefault(const llvm::Triple &Triple) {
if (Triple.isAArch64() || Triple.isPS() || Triple.isVE() ||
(Triple.isAndroid() && Triple.isRISCV64()))
return false;

return true;
}

static bool mustUseNonLeafFramePointerForTarget(const llvm::Triple &Triple) {
switch (Triple.getArch()) {
default:
Expand All @@ -176,38 +184,91 @@ static bool mustUseNonLeafFramePointerForTarget(const llvm::Triple &Triple) {
}
}

// True if a target-specific option requires the frame chain to be preserved,
// even if new frame records are not created.
static bool mustMaintainValidFrameChain(const llvm::opt::ArgList &Args,
const llvm::Triple &Triple) {
if (Triple.isARM() || Triple.isThumb()) {
// For 32-bit Arm, the -mframe-chain=aapcs and -mframe-chain=aapcs+leaf
// options require the frame pointer register to be reserved (or point to a
// new AAPCS-compilant frame record), even with -fno-omit-frame-pointer.
if (Arg *A = Args.getLastArg(options::OPT_mframe_chain)) {
StringRef V = A->getValue();
return V != "none";
}
return false;
}
return false;
}

// True if a target-specific option causes -fno-omit-frame-pointer to also
// cause frame records to be created in leaf functions.
static bool framePointerImpliesLeafFramePointer(const llvm::opt::ArgList &Args,
const llvm::Triple &Triple) {
if (Triple.isARM() || Triple.isThumb()) {
// For 32-bit Arm, the -mframe-chain=aapcs+leaf option causes the
// -fno-omit-frame-pointer optiion to imply -mno-omit-leaf-frame-pointer,
// but does not by itself imply either option.
if (Arg *A = Args.getLastArg(options::OPT_mframe_chain)) {
StringRef V = A->getValue();
return V == "aapcs+leaf";
}
return false;
}
return false;
}

clang::CodeGenOptions::FramePointerKind
getFramePointerKind(const llvm::opt::ArgList &Args,
const llvm::Triple &Triple) {
// We have 4 states:
// There are three things to consider here:
// * Should a frame record be created for non-leaf functions?
// * Should a frame record be created for leaf functions?
// * Is the frame pointer register reserved, i.e. must it always point to
// either a new, valid frame record or be un-modified?
//
// 00) leaf retained, non-leaf retained
// 01) leaf retained, non-leaf omitted (this is invalid)
// 10) leaf omitted, non-leaf retained
// (what -momit-leaf-frame-pointer was designed for)
// 11) leaf omitted, non-leaf omitted
// Not all combinations of these are valid:
// * It's not useful to have leaf frame records without non-leaf ones.
// * It's not useful to have frame records without reserving the frame
// pointer.
//
// "omit" options taking precedence over "no-omit" options is the only way
// to make 3 valid states representable
llvm::opt::Arg *A =
Args.getLastArg(clang::driver::options::OPT_fomit_frame_pointer,
clang::driver::options::OPT_fno_omit_frame_pointer);

bool OmitFP = A && A->getOption().matches(
clang::driver::options::OPT_fomit_frame_pointer);
bool NoOmitFP = A && A->getOption().matches(
clang::driver::options::OPT_fno_omit_frame_pointer);
bool OmitLeafFP =
Args.hasFlag(clang::driver::options::OPT_momit_leaf_frame_pointer,
clang::driver::options::OPT_mno_omit_leaf_frame_pointer,
Triple.isAArch64() || Triple.isPS() || Triple.isVE() ||
(Triple.isAndroid() && Triple.isRISCV64()));
if (NoOmitFP || mustUseNonLeafFramePointerForTarget(Triple) ||
(!OmitFP && useFramePointerForTargetByDefault(Args, Triple))) {
if (OmitLeafFP)
return clang::CodeGenOptions::FramePointerKind::NonLeaf;
return clang::CodeGenOptions::FramePointerKind::All;
}
// | Non-leaf | Leaf | Reserved |
// | N | N | N | FramePointerKind::None
// | N | N | Y | FramePointerKind::Reserved
// | N | Y | N | Invalid
// | N | Y | Y | Invalid
// | Y | N | N | Invalid
// | Y | N | Y | FramePointerKind::NonLeaf
// | Y | Y | N | Invalid
// | Y | Y | Y | FramePointerKind::All
//
// The FramePointerKind::Reserved case is currently only reachable for Arm,
// which has the -mframe-chain= option which can (in combination with
// -fno-omit-frame-pointer) specify that the frame chain must be valid,
// without requiring new frame records to be created.

bool DefaultFP = useFramePointerForTargetByDefault(Args, Triple);
bool EnableFP =
mustUseNonLeafFramePointerForTarget(Triple) ||
Args.hasFlag(clang::driver::options::OPT_fno_omit_frame_pointer,
clang::driver::options::OPT_fomit_frame_pointer, DefaultFP);

bool DefaultLeafFP =
useLeafFramePointerForTargetByDefault(Triple) ||
(EnableFP && framePointerImpliesLeafFramePointer(Args, Triple));
bool EnableLeafFP = Args.hasFlag(
clang::driver::options::OPT_mno_omit_leaf_frame_pointer,
clang::driver::options::OPT_momit_leaf_frame_pointer, DefaultLeafFP);

bool FPRegReserved = EnableFP || mustMaintainValidFrameChain(Args, Triple);

if (EnableFP) {
if (EnableLeafFP)
return clang::CodeGenOptions::FramePointerKind::All;
return clang::CodeGenOptions::FramePointerKind::NonLeaf;
}
if (FPRegReserved)
return clang::CodeGenOptions::FramePointerKind::Reserved;
return clang::CodeGenOptions::FramePointerKind::None;
}

Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Driver/ToolChains/Cuda.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -826,7 +826,7 @@ NVPTXToolChain::getSystemGPUArchs(const ArgList &Args) const {
else
Program = GetProgramPath("nvptx-arch");

auto StdoutOrErr = executeToolChainProgram(Program);
auto StdoutOrErr = executeToolChainProgram(Program, /*SecondsToWait=*/10);
if (!StdoutOrErr)
return StdoutOrErr.takeError();

Expand Down
3 changes: 3 additions & 0 deletions clang/lib/Driver/ToolChains/Flang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -802,6 +802,9 @@ void Flang::ConstructJob(Compilation &C, const JobAction &JA,
case CodeGenOptions::FramePointerKind::None:
FPKeepKindStr = "-mframe-pointer=none";
break;
case CodeGenOptions::FramePointerKind::Reserved:
FPKeepKindStr = "-mframe-pointer=reserved";
break;
case CodeGenOptions::FramePointerKind::NonLeaf:
FPKeepKindStr = "-mframe-pointer=non-leaf";
break;
Expand Down
5 changes: 4 additions & 1 deletion clang/lib/Sema/SemaDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13079,7 +13079,10 @@ NamedDecl *Sema::BuildUsingDeclaration(
// A using-declaration shall not name a namespace.
if (R.getAsSingle<NamespaceDecl>()) {
Diag(IdentLoc, diag::err_using_decl_can_not_refer_to_namespace)
<< SS.getRange();
<< SS.getRange();
// Suggest using 'using namespace ...' instead.
Diag(SS.getBeginLoc(), diag::note_namespace_using_decl)
<< FixItHint::CreateInsertion(SS.getBeginLoc(), "namespace ");
return BuildInvalid();
}

Expand Down
13 changes: 6 additions & 7 deletions clang/lib/Sema/SemaTemplateDeduction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -712,20 +712,19 @@ DeduceTemplateSpecArguments(Sema &S, TemplateParameterList *TemplateParams,
if (const auto *TD = TNA.getAsTemplateDecl(); TD && TD->isTypeAlias())
return TemplateDeductionResult::Success;

// Perform template argument deduction for the template name.
if (auto Result =
DeduceTemplateArguments(S, TemplateParams, TNP, TNA, Info,
SA->template_arguments(), Deduced);
Result != TemplateDeductionResult::Success)
return Result;

// FIXME: To preserve sugar, the TST needs to carry sugared resolved
// arguments.
ArrayRef<TemplateArgument> AResolved =
SA->getCanonicalTypeInternal()
->castAs<TemplateSpecializationType>()
->template_arguments();

// Perform template argument deduction for the template name.
if (auto Result = DeduceTemplateArguments(S, TemplateParams, TNP, TNA, Info,
AResolved, Deduced);
Result != TemplateDeductionResult::Success)
return Result;

// Perform template argument deduction on each template
// argument. Ignore any missing/extra arguments, since they could be
// filled in by default arguments.
Expand Down
163 changes: 87 additions & 76 deletions clang/lib/Serialization/ASTReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1658,7 +1658,7 @@ bool ASTReader::ReadSLocEntry(int ID) {

unsigned NumFileDecls = Record[7];
if (NumFileDecls && ContextObj) {
const LocalDeclID *FirstDecl = F->FileSortedDecls + Record[6];
const unaligned_decl_id_t *FirstDecl = F->FileSortedDecls + Record[6];
assert(F->FileSortedDecls && "FILE_SORTED_DECLS not encountered yet ?");
FileDeclIDs[FID] =
FileDeclsInfo(F, llvm::ArrayRef(FirstDecl, NumFileDecls));
Expand Down Expand Up @@ -3377,26 +3377,11 @@ llvm::Error ASTReader::ReadASTBlock(ModuleFile &F,
"duplicate DECL_OFFSET record in AST file");
F.DeclOffsets = (const DeclOffset *)Blob.data();
F.LocalNumDecls = Record[0];
unsigned LocalBaseDeclID = Record[1];
F.BaseDeclID = getTotalNumDecls();

if (F.LocalNumDecls > 0) {
// Introduce the global -> local mapping for declarations within this
// module.
GlobalDeclMap.insert(std::make_pair(
GlobalDeclID(getTotalNumDecls() + NUM_PREDEF_DECL_IDS), &F));

// Introduce the local -> global mapping for declarations within this
// module.
F.DeclRemap.insertOrReplace(
std::make_pair(LocalBaseDeclID, F.BaseDeclID - LocalBaseDeclID));

// Introduce the global -> local mapping for declarations within this
// module.
F.GlobalToLocalDeclIDs[&F] = LocalBaseDeclID;
F.BaseDeclIndex = getTotalNumDecls();

if (F.LocalNumDecls > 0)
DeclsLoaded.resize(DeclsLoaded.size() + F.LocalNumDecls);
}

break;
}

Expand Down Expand Up @@ -3631,7 +3616,7 @@ llvm::Error ASTReader::ReadASTBlock(ModuleFile &F,
break;

case FILE_SORTED_DECLS:
F.FileSortedDecls = (const LocalDeclID *)Blob.data();
F.FileSortedDecls = (const unaligned_decl_id_t *)Blob.data();
F.NumFileSortedDecls = Record[0];
break;

Expand Down Expand Up @@ -4058,7 +4043,6 @@ void ASTReader::ReadModuleOffsetMap(ModuleFile &F) const {
RemapBuilder PreprocessedEntityRemap(F.PreprocessedEntityRemap);
RemapBuilder SubmoduleRemap(F.SubmoduleRemap);
RemapBuilder SelectorRemap(F.SelectorRemap);
RemapBuilder DeclRemap(F.DeclRemap);
RemapBuilder TypeRemap(F.TypeRemap);

auto &ImportedModuleVector = F.TransitiveImports;
Expand Down Expand Up @@ -4097,8 +4081,6 @@ void ASTReader::ReadModuleOffsetMap(ModuleFile &F) const {
endian::readNext<uint32_t, llvm::endianness::little>(Data);
uint32_t SelectorIDOffset =
endian::readNext<uint32_t, llvm::endianness::little>(Data);
uint32_t DeclIDOffset =
endian::readNext<uint32_t, llvm::endianness::little>(Data);
uint32_t TypeIndexOffset =
endian::readNext<uint32_t, llvm::endianness::little>(Data);

Expand All @@ -4116,11 +4098,7 @@ void ASTReader::ReadModuleOffsetMap(ModuleFile &F) const {
PreprocessedEntityRemap);
mapOffset(SubmoduleIDOffset, OM->BaseSubmoduleID, SubmoduleRemap);
mapOffset(SelectorIDOffset, OM->BaseSelectorID, SelectorRemap);
mapOffset(DeclIDOffset, OM->BaseDeclID, DeclRemap);
mapOffset(TypeIndexOffset, OM->BaseTypeIndex, TypeRemap);

// Global -> local mappings.
F.GlobalToLocalDeclIDs[OM] = DeclIDOffset;
}
}

Expand Down Expand Up @@ -4645,7 +4623,7 @@ ASTReader::ASTReadResult ASTReader::ReadAST(StringRef FileName, ModuleKind Type,
// that we load any additional categories.
if (ContextObj) {
for (unsigned I = 0, N = ObjCClassesLoaded.size(); I != N; ++I) {
loadObjCCategories(GlobalDeclID(ObjCClassesLoaded[I]->getGlobalID()),
loadObjCCategories(ObjCClassesLoaded[I]->getGlobalID(),
ObjCClassesLoaded[I], PreviousGeneration);
}
}
Expand Down Expand Up @@ -7644,50 +7622,59 @@ CXXBaseSpecifier *ASTReader::GetExternalCXXBaseSpecifiers(uint64_t Offset) {

GlobalDeclID ASTReader::getGlobalDeclID(ModuleFile &F,
LocalDeclID LocalID) const {
DeclID ID = LocalID.get();
if (ID < NUM_PREDEF_DECL_IDS)
return GlobalDeclID(ID);
if (LocalID.get() < NUM_PREDEF_DECL_IDS)
return GlobalDeclID(LocalID.get());

unsigned OwningModuleFileIndex = LocalID.getModuleFileIndex();
DeclID ID = LocalID.getLocalDeclIndex();

if (!F.ModuleOffsetMap.empty())
ReadModuleOffsetMap(F);

ContinuousRangeMap<DeclID, int, 2>::iterator I =
F.DeclRemap.find(ID - NUM_PREDEF_DECL_IDS);
assert(I != F.DeclRemap.end() && "Invalid index into decl index remap");
ModuleFile *OwningModuleFile =
OwningModuleFileIndex == 0
? &F
: F.TransitiveImports[OwningModuleFileIndex - 1];

if (OwningModuleFileIndex == 0)
ID -= NUM_PREDEF_DECL_IDS;

return GlobalDeclID(ID + I->second);
uint64_t NewModuleFileIndex = OwningModuleFile->Index + 1;
return GlobalDeclID(ID, NewModuleFileIndex);
}

bool ASTReader::isDeclIDFromModule(GlobalDeclID ID, ModuleFile &M) const {
// Predefined decls aren't from any module.
if (ID.get() < NUM_PREDEF_DECL_IDS)
return false;

return ID.get() - NUM_PREDEF_DECL_IDS >= M.BaseDeclID &&
ID.get() - NUM_PREDEF_DECL_IDS < M.BaseDeclID + M.LocalNumDecls;
unsigned ModuleFileIndex = ID.getModuleFileIndex();
return M.Index == ModuleFileIndex - 1;
}

ModuleFile *ASTReader::getOwningModuleFile(GlobalDeclID ID) const {
// Predefined decls aren't from any module.
if (ID.get() < NUM_PREDEF_DECL_IDS)
return nullptr;

uint64_t ModuleFileIndex = ID.getModuleFileIndex();
assert(ModuleFileIndex && "Untranslated Local Decl?");

return &getModuleManager()[ModuleFileIndex - 1];
}

ModuleFile *ASTReader::getOwningModuleFile(const Decl *D) {
ModuleFile *ASTReader::getOwningModuleFile(const Decl *D) const {
if (!D->isFromASTFile())
return nullptr;
GlobalDeclMapType::const_iterator I =
GlobalDeclMap.find(GlobalDeclID(D->getGlobalID()));
assert(I != GlobalDeclMap.end() && "Corrupted global declaration map");
return I->second;

return getOwningModuleFile(D->getGlobalID());
}

SourceLocation ASTReader::getSourceLocationForDeclID(GlobalDeclID ID) {
if (ID.get() < NUM_PREDEF_DECL_IDS)
return SourceLocation();

unsigned Index = ID.get() - NUM_PREDEF_DECL_IDS;

if (Index > DeclsLoaded.size()) {
Error("declaration ID out-of-range for AST file");
return SourceLocation();
}

if (Decl *D = DeclsLoaded[Index])
if (Decl *D = GetExistingDecl(ID))
return D->getLocation();

SourceLocation Loc;
Expand Down Expand Up @@ -7754,8 +7741,19 @@ static Decl *getPredefinedDecl(ASTContext &Context, PredefinedDeclIDs ID) {
llvm_unreachable("PredefinedDeclIDs unknown enum value");
}

unsigned ASTReader::translateGlobalDeclIDToIndex(GlobalDeclID GlobalID) const {
ModuleFile *OwningModuleFile = getOwningModuleFile(GlobalID);
if (!OwningModuleFile) {
assert(GlobalID.get() < NUM_PREDEF_DECL_IDS && "Untransalted Global ID?");
return GlobalID.get();
}

return OwningModuleFile->BaseDeclIndex + GlobalID.getLocalDeclIndex();
}

Decl *ASTReader::GetExistingDecl(GlobalDeclID ID) {
assert(ContextObj && "reading decl with no AST context");

if (ID.get() < NUM_PREDEF_DECL_IDS) {
Decl *D = getPredefinedDecl(*ContextObj, (PredefinedDeclIDs)ID);
if (D) {
Expand All @@ -7768,7 +7766,7 @@ Decl *ASTReader::GetExistingDecl(GlobalDeclID ID) {
return D;
}

unsigned Index = ID.get() - NUM_PREDEF_DECL_IDS;
unsigned Index = translateGlobalDeclIDToIndex(ID);

if (Index >= DeclsLoaded.size()) {
assert(0 && "declaration ID out-of-range for AST file");
Expand All @@ -7783,7 +7781,7 @@ Decl *ASTReader::GetDecl(GlobalDeclID ID) {
if (ID.get() < NUM_PREDEF_DECL_IDS)
return GetExistingDecl(ID);

unsigned Index = ID.get() - NUM_PREDEF_DECL_IDS;
unsigned Index = translateGlobalDeclIDToIndex(ID);

if (Index >= DeclsLoaded.size()) {
assert(0 && "declaration ID out-of-range for AST file");
Expand All @@ -7802,20 +7800,31 @@ Decl *ASTReader::GetDecl(GlobalDeclID ID) {

LocalDeclID ASTReader::mapGlobalIDToModuleFileGlobalID(ModuleFile &M,
GlobalDeclID GlobalID) {
DeclID ID = GlobalID.get();
if (ID < NUM_PREDEF_DECL_IDS)
if (GlobalID.get() < NUM_PREDEF_DECL_IDS)
return LocalDeclID(GlobalID.get());

if (!M.ModuleOffsetMap.empty())
ReadModuleOffsetMap(M);

ModuleFile *Owner = getOwningModuleFile(GlobalID);
DeclID ID = GlobalID.getLocalDeclIndex();

if (Owner == &M) {
ID += NUM_PREDEF_DECL_IDS;
return LocalDeclID(ID);
}

GlobalDeclMapType::const_iterator I = GlobalDeclMap.find(GlobalID);
assert(I != GlobalDeclMap.end() && "Corrupted global declaration map");
ModuleFile *Owner = I->second;
uint64_t OrignalModuleFileIndex = 0;
for (unsigned I = 0; I < M.TransitiveImports.size(); I++)
if (M.TransitiveImports[I] == Owner) {
OrignalModuleFileIndex = I + 1;
break;
}

llvm::DenseMap<ModuleFile *, DeclID>::iterator Pos =
M.GlobalToLocalDeclIDs.find(Owner);
if (Pos == M.GlobalToLocalDeclIDs.end())
if (!OrignalModuleFileIndex)
return LocalDeclID();

return LocalDeclID(ID - Owner->BaseDeclID + Pos->second);
return LocalDeclID(ID, OrignalModuleFileIndex);
}

GlobalDeclID ASTReader::ReadDeclID(ModuleFile &F, const RecordData &Record,
Expand Down Expand Up @@ -7894,32 +7903,34 @@ void ASTReader::FindExternalLexicalDecls(

namespace {

class DeclIDComp {
class UnalignedDeclIDComp {
ASTReader &Reader;
ModuleFile &Mod;

public:
DeclIDComp(ASTReader &Reader, ModuleFile &M) : Reader(Reader), Mod(M) {}
UnalignedDeclIDComp(ASTReader &Reader, ModuleFile &M)
: Reader(Reader), Mod(M) {}

bool operator()(LocalDeclID L, LocalDeclID R) const {
bool operator()(unaligned_decl_id_t L, unaligned_decl_id_t R) const {
SourceLocation LHS = getLocation(L);
SourceLocation RHS = getLocation(R);
return Reader.getSourceManager().isBeforeInTranslationUnit(LHS, RHS);
}

bool operator()(SourceLocation LHS, LocalDeclID R) const {
bool operator()(SourceLocation LHS, unaligned_decl_id_t R) const {
SourceLocation RHS = getLocation(R);
return Reader.getSourceManager().isBeforeInTranslationUnit(LHS, RHS);
}

bool operator()(LocalDeclID L, SourceLocation RHS) const {
bool operator()(unaligned_decl_id_t L, SourceLocation RHS) const {
SourceLocation LHS = getLocation(L);
return Reader.getSourceManager().isBeforeInTranslationUnit(LHS, RHS);
}

SourceLocation getLocation(LocalDeclID ID) const {
SourceLocation getLocation(unaligned_decl_id_t ID) const {
return Reader.getSourceManager().getFileLoc(
Reader.getSourceLocationForDeclID(Reader.getGlobalDeclID(Mod, ID)));
Reader.getSourceLocationForDeclID(
Reader.getGlobalDeclID(Mod, (LocalDeclID)ID)));
}
};

Expand All @@ -7942,8 +7953,8 @@ void ASTReader::FindFileRegionDecls(FileID File,
BeginLoc = SM.getLocForStartOfFile(File).getLocWithOffset(Offset);
SourceLocation EndLoc = BeginLoc.getLocWithOffset(Length);

DeclIDComp DIDComp(*this, *DInfo.Mod);
ArrayRef<LocalDeclID>::iterator BeginIt =
UnalignedDeclIDComp DIDComp(*this, *DInfo.Mod);
ArrayRef<unaligned_decl_id_t>::iterator BeginIt =
llvm::lower_bound(DInfo.Decls, BeginLoc, DIDComp);
if (BeginIt != DInfo.Decls.begin())
--BeginIt;
Expand All @@ -7952,17 +7963,18 @@ void ASTReader::FindFileRegionDecls(FileID File,
// to backtrack until we find it otherwise we will fail to report that the
// region overlaps with an objc container.
while (BeginIt != DInfo.Decls.begin() &&
GetDecl(getGlobalDeclID(*DInfo.Mod, *BeginIt))
GetDecl(getGlobalDeclID(*DInfo.Mod, (LocalDeclID)(*BeginIt)))
->isTopLevelDeclInObjCContainer())
--BeginIt;

ArrayRef<LocalDeclID>::iterator EndIt =
ArrayRef<unaligned_decl_id_t>::iterator EndIt =
llvm::upper_bound(DInfo.Decls, EndLoc, DIDComp);
if (EndIt != DInfo.Decls.end())
++EndIt;

for (ArrayRef<LocalDeclID>::iterator DIt = BeginIt; DIt != EndIt; ++DIt)
Decls.push_back(GetDecl(getGlobalDeclID(*DInfo.Mod, *DIt)));
for (ArrayRef<unaligned_decl_id_t>::iterator DIt = BeginIt; DIt != EndIt;
++DIt)
Decls.push_back(GetDecl(getGlobalDeclID(*DInfo.Mod, (LocalDeclID)(*DIt))));
}

bool
Expand Down Expand Up @@ -8169,7 +8181,6 @@ LLVM_DUMP_METHOD void ASTReader::dump() {
dumpModuleIDMap("Global bit offset map", GlobalBitOffsetsMap);
dumpModuleIDMap("Global source location entry map", GlobalSLocEntryMap);
dumpModuleIDMap("Global type map", GlobalTypeMap);
dumpModuleIDMap("Global declaration map", GlobalDeclMap);
dumpModuleIDMap("Global identifier map", GlobalIdentifierMap);
dumpModuleIDMap("Global macro map", GlobalMacroMap);
dumpModuleIDMap("Global submodule map", GlobalSubmoduleMap);
Expand Down Expand Up @@ -9185,7 +9196,7 @@ void ASTRecordReader::readUnresolvedSet(LazyASTUnresolvedSet &Set) {
while (NumDecls--) {
GlobalDeclID ID = readDeclID();
AccessSpecifier AS = (AccessSpecifier) readInt();
Set.addLazyDecl(getContext(), ID.get(), AS);
Set.addLazyDecl(getContext(), ID, AS);
}
}

Expand Down
16 changes: 7 additions & 9 deletions clang/lib/Serialization/ASTReaderDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2924,7 +2924,7 @@ void ASTDeclReader::mergeTemplatePattern(RedeclarableTemplateDecl *D,
auto *ExistingPattern = Existing->getTemplatedDecl();
RedeclarableResult Result(
/*MergeWith*/ ExistingPattern,
GlobalDeclID(DPattern->getCanonicalDecl()->getGlobalID()), IsKeyDecl);
DPattern->getCanonicalDecl()->getGlobalID(), IsKeyDecl);

if (auto *DClass = dyn_cast<CXXRecordDecl>(DPattern)) {
// Merge with any existing definition.
Expand Down Expand Up @@ -3245,11 +3245,10 @@ bool ASTReader::isConsumerInterestedIn(Decl *D) {
/// Get the correct cursor and offset for loading a declaration.
ASTReader::RecordLocation ASTReader::DeclCursorForID(GlobalDeclID ID,
SourceLocation &Loc) {
GlobalDeclMapType::iterator I = GlobalDeclMap.find(ID);
assert(I != GlobalDeclMap.end() && "Corrupted global declaration map");
ModuleFile *M = I->second;
const DeclOffset &DOffs =
M->DeclOffsets[ID.get() - M->BaseDeclID - NUM_PREDEF_DECL_IDS];
ModuleFile *M = getOwningModuleFile(ID);
assert(M);
unsigned LocalDeclIndex = ID.getLocalDeclIndex();
const DeclOffset &DOffs = M->DeclOffsets[LocalDeclIndex];
Loc = ReadSourceLocation(*M, DOffs.getRawLoc());
return RecordLocation(M, DOffs.getBitOffset(M->DeclsBlockStartOffset));
}
Expand Down Expand Up @@ -3792,7 +3791,6 @@ void ASTReader::markIncompleteDeclChain(Decl *D) {

/// Read the declaration at the given offset from the AST file.
Decl *ASTReader::ReadDeclRecord(GlobalDeclID ID) {
unsigned Index = ID.get() - NUM_PREDEF_DECL_IDS;
SourceLocation DeclLoc;
RecordLocation Loc = DeclCursorForID(ID, DeclLoc);
llvm::BitstreamCursor &DeclsCursor = Loc.F->DeclsCursor;
Expand Down Expand Up @@ -4123,7 +4121,7 @@ Decl *ASTReader::ReadDeclRecord(GlobalDeclID ID) {
}

assert(D && "Unknown declaration reading AST file");
LoadedDecl(Index, D);
LoadedDecl(translateGlobalDeclIDToIndex(ID), D);
// Set the DeclContext before doing any deserialization, to make sure internal
// calls to Decl::getASTContext() by Decl's methods will find the
// TranslationUnitDecl without crashing.
Expand Down Expand Up @@ -4449,7 +4447,7 @@ namespace {
M.ObjCCategoriesMap + M.LocalNumObjCCategoriesInMap,
Compare);
if (Result == M.ObjCCategoriesMap + M.LocalNumObjCCategoriesInMap ||
Result->DefinitionID != LocalID) {
Result->getDefinitionID() != LocalID) {
// We didn't find anything. If the class definition is in this module
// file, then the module files it depends on cannot have any categories,
// so suppress further lookup.
Expand Down
7 changes: 1 addition & 6 deletions clang/lib/Serialization/ASTWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3357,12 +3357,10 @@ void ASTWriter::WriteTypeDeclOffsets() {
Abbrev = std::make_shared<BitCodeAbbrev>();
Abbrev->Add(BitCodeAbbrevOp(DECL_OFFSET));
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // # of declarations
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // base decl ID
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // declarations block
unsigned DeclOffsetAbbrev = Stream.EmitAbbrev(std::move(Abbrev));
{
RecordData::value_type Record[] = {DECL_OFFSET, DeclOffsets.size(),
FirstDeclID.get() - NUM_PREDEF_DECL_IDS};
RecordData::value_type Record[] = {DECL_OFFSET, DeclOffsets.size()};
Stream.EmitRecordWithBlob(DeclOffsetAbbrev, Record, bytes(DeclOffsets));
}
}
Expand Down Expand Up @@ -5423,7 +5421,6 @@ ASTFileSignature ASTWriter::WriteASTCore(Sema &SemaRef, StringRef isysroot,
M.NumPreprocessedEntities);
writeBaseIDOrNone(M.BaseSubmoduleID, M.LocalNumSubmodules);
writeBaseIDOrNone(M.BaseSelectorID, M.LocalNumSelectors);
writeBaseIDOrNone(M.BaseDeclID, M.LocalNumDecls);
writeBaseIDOrNone(M.BaseTypeIndex, M.LocalNumTypes);
}
}
Expand Down Expand Up @@ -6618,13 +6615,11 @@ void ASTWriter::ReaderInitialized(ASTReader *Reader) {

// Note, this will get called multiple times, once one the reader starts up
// and again each time it's done reading a PCH or module.
FirstDeclID = LocalDeclID(NUM_PREDEF_DECL_IDS + Chain->getTotalNumDecls());
FirstTypeID = NUM_PREDEF_TYPE_IDS + Chain->getTotalNumTypes();
FirstIdentID = NUM_PREDEF_IDENT_IDS + Chain->getTotalNumIdentifiers();
FirstMacroID = NUM_PREDEF_MACRO_IDS + Chain->getTotalNumMacros();
FirstSubmoduleID = NUM_PREDEF_SUBMODULE_IDS + Chain->getTotalNumSubmodules();
FirstSelectorID = NUM_PREDEF_SELECTOR_IDS + Chain->getTotalNumSelectors();
NextDeclID = FirstDeclID;
NextTypeID = FirstTypeID;
NextIdentID = FirstIdentID;
NextMacroID = FirstMacroID;
Expand Down
3 changes: 1 addition & 2 deletions clang/lib/Serialization/ModuleFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,6 @@ LLVM_DUMP_METHOD void ModuleFile::dump() {
<< " Number of types: " << LocalNumTypes << '\n';
dumpLocalRemap("Type index local -> global map", TypeRemap);

llvm::errs() << " Base decl ID: " << BaseDeclID << '\n'
llvm::errs() << " Base decl index: " << BaseDeclIndex << '\n'
<< " Number of decls: " << LocalNumDecls << '\n';
dumpLocalRemap("Decl ID local -> global map", DeclRemap);
}
1 change: 1 addition & 0 deletions clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ add_clang_library(clangStaticAnalyzerCheckers
NoReturnFunctionChecker.cpp
NonNullParamChecker.cpp
NonnullGlobalConstantsChecker.cpp
NoOwnershipChangeVisitor.cpp
NullabilityChecker.cpp
NumberObjectConversionChecker.cpp
ObjCAtSyncChecker.cpp
Expand Down
147 changes: 18 additions & 129 deletions clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@

#include "AllocationState.h"
#include "InterCheckerAPI.h"
#include "NoOwnershipChangeVisitor.h"
#include "clang/AST/Attr.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclTemplate.h"
Expand Down Expand Up @@ -79,13 +80,11 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SetOperations.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
#include <climits>
#include <functional>
#include <optional>
#include <utility>
Expand Down Expand Up @@ -414,7 +413,7 @@ class MallocChecker
bool isFreeingCall(const CallEvent &Call) const;
static bool isFreeingOwnershipAttrCall(const FunctionDecl *Func);

friend class NoOwnershipChangeVisitor;
friend class NoMemOwnershipChangeVisitor;

CallDescriptionMap<CheckFn> AllocatingMemFnMap{
{{CDM::CLibrary, {"alloca"}, 1}, &MallocChecker::checkAlloca},
Expand Down Expand Up @@ -765,61 +764,8 @@ class MallocChecker
//===----------------------------------------------------------------------===//

namespace {
class NoOwnershipChangeVisitor final : public NoStateChangeFuncVisitor {
// The symbol whose (lack of) ownership change we are interested in.
SymbolRef Sym;
const MallocChecker &Checker;
using OwnerSet = llvm::SmallPtrSet<const MemRegion *, 8>;

// Collect which entities point to the allocated memory, and could be
// responsible for deallocating it.
class OwnershipBindingsHandler : public StoreManager::BindingsHandler {
SymbolRef Sym;
OwnerSet &Owners;

public:
OwnershipBindingsHandler(SymbolRef Sym, OwnerSet &Owners)
: Sym(Sym), Owners(Owners) {}

bool HandleBinding(StoreManager &SMgr, Store Store, const MemRegion *Region,
SVal Val) override {
if (Val.getAsSymbol() == Sym)
Owners.insert(Region);
return true;
}

LLVM_DUMP_METHOD void dump() const { dumpToStream(llvm::errs()); }
LLVM_DUMP_METHOD void dumpToStream(llvm::raw_ostream &out) const {
out << "Owners: {\n";
for (const MemRegion *Owner : Owners) {
out << " ";
Owner->dumpToStream(out);
out << ",\n";
}
out << "}\n";
}
};

class NoMemOwnershipChangeVisitor final : public NoOwnershipChangeVisitor {
protected:
OwnerSet getOwnersAtNode(const ExplodedNode *N) {
OwnerSet Ret;

ProgramStateRef State = N->getState();
OwnershipBindingsHandler Handler{Sym, Ret};
State->getStateManager().getStoreManager().iterBindings(State->getStore(),
Handler);
return Ret;
}

LLVM_DUMP_METHOD static std::string
getFunctionName(const ExplodedNode *CallEnterN) {
if (const CallExpr *CE = llvm::dyn_cast_or_null<CallExpr>(
CallEnterN->getLocationAs<CallEnter>()->getCallExpr()))
if (const FunctionDecl *FD = CE->getDirectCallee())
return FD->getQualifiedNameAsString();
return "";
}

/// Syntactically checks whether the callee is a deallocating function. Since
/// we have no path-sensitive information on this call (we would need a
/// CallEvent instead of a CallExpr for that), its possible that a
Expand All @@ -828,8 +774,9 @@ class NoOwnershipChangeVisitor final : public NoStateChangeFuncVisitor {
/// See namespace `memory_passed_to_fn_call_free_through_fn_ptr` in
/// clang/test/Analysis/NewDeleteLeaks.cpp.
bool isFreeingCallAsWritten(const CallExpr &Call) const {
if (Checker.FreeingMemFnMap.lookupAsWritten(Call) ||
Checker.ReallocatingMemFnMap.lookupAsWritten(Call))
const auto *MallocChk = static_cast<const MallocChecker *>(&Checker);
if (MallocChk->FreeingMemFnMap.lookupAsWritten(Call) ||
MallocChk->ReallocatingMemFnMap.lookupAsWritten(Call))
return true;

if (const auto *Func =
Expand All @@ -839,23 +786,21 @@ class NoOwnershipChangeVisitor final : public NoStateChangeFuncVisitor {
return false;
}

bool hasResourceStateChanged(ProgramStateRef CallEnterState,
ProgramStateRef CallExitEndState) final {
return CallEnterState->get<RegionState>(Sym) !=
CallExitEndState->get<RegionState>(Sym);
}

/// Heuristically guess whether the callee intended to free memory. This is
/// done syntactically, because we are trying to argue about alternative
/// paths of execution, and as a consequence we don't have path-sensitive
/// information.
bool doesFnIntendToHandleOwnership(const Decl *Callee, ASTContext &ACtx) {
bool doesFnIntendToHandleOwnership(const Decl *Callee,
ASTContext &ACtx) final {
using namespace clang::ast_matchers;
const FunctionDecl *FD = dyn_cast<FunctionDecl>(Callee);

// Given that the stack frame was entered, the body should always be
// theoretically obtainable. In case of body farms, the synthesized body
// is not attached to declaration, thus triggering the '!FD->hasBody()'
// branch. That said, would a synthesized body ever intend to handle
// ownership? As of today they don't. And if they did, how would we
// put notes inside it, given that it doesn't match any source locations?
if (!FD || !FD->hasBody())
return false;

auto Matches = match(findAll(stmt(anyOf(cxxDeleteExpr().bind("delete"),
callExpr().bind("call")))),
*FD->getBody(), ACtx);
Expand All @@ -873,30 +818,7 @@ class NoOwnershipChangeVisitor final : public NoStateChangeFuncVisitor {
return false;
}

bool wasModifiedInFunction(const ExplodedNode *CallEnterN,
const ExplodedNode *CallExitEndN) override {
if (!doesFnIntendToHandleOwnership(
CallExitEndN->getFirstPred()->getLocationContext()->getDecl(),
CallExitEndN->getState()->getAnalysisManager().getASTContext()))
return true;

if (CallEnterN->getState()->get<RegionState>(Sym) !=
CallExitEndN->getState()->get<RegionState>(Sym))
return true;

OwnerSet CurrOwners = getOwnersAtNode(CallEnterN);
OwnerSet ExitOwners = getOwnersAtNode(CallExitEndN);

// Owners in the current set may be purged from the analyzer later on.
// If a variable is dead (is not referenced directly or indirectly after
// some point), it will be removed from the Store before the end of its
// actual lifetime.
// This means that if the ownership status didn't change, CurrOwners
// must be a superset of, but not necessarily equal to ExitOwners.
return !llvm::set_is_subset(ExitOwners, CurrOwners);
}

static PathDiagnosticPieceRef emitNote(const ExplodedNode *N) {
PathDiagnosticPieceRef emitNote(const ExplodedNode *N) final {
PathDiagnosticLocation L = PathDiagnosticLocation::create(
N->getLocation(),
N->getState()->getStateManager().getContext().getSourceManager());
Expand All @@ -905,42 +827,9 @@ class NoOwnershipChangeVisitor final : public NoStateChangeFuncVisitor {
"later deallocation");
}

PathDiagnosticPieceRef
maybeEmitNoteForObjCSelf(PathSensitiveBugReport &R,
const ObjCMethodCall &Call,
const ExplodedNode *N) override {
// TODO: Implement.
return nullptr;
}

PathDiagnosticPieceRef
maybeEmitNoteForCXXThis(PathSensitiveBugReport &R,
const CXXConstructorCall &Call,
const ExplodedNode *N) override {
// TODO: Implement.
return nullptr;
}

PathDiagnosticPieceRef
maybeEmitNoteForParameters(PathSensitiveBugReport &R, const CallEvent &Call,
const ExplodedNode *N) override {
// TODO: Factor the logic of "what constitutes as an entity being passed
// into a function call" out by reusing the code in
// NoStoreFuncVisitor::maybeEmitNoteForParameters, maybe by incorporating
// the printing technology in UninitializedObject's FieldChainInfo.
ArrayRef<ParmVarDecl *> Parameters = Call.parameters();
for (unsigned I = 0; I < Call.getNumArgs() && I < Parameters.size(); ++I) {
SVal V = Call.getArgSVal(I);
if (V.getAsSymbol() == Sym)
return emitNote(N);
}
return nullptr;
}

public:
NoOwnershipChangeVisitor(SymbolRef Sym, const MallocChecker *Checker)
: NoStateChangeFuncVisitor(bugreporter::TrackingKind::Thorough), Sym(Sym),
Checker(*Checker) {}
NoMemOwnershipChangeVisitor(SymbolRef Sym, const MallocChecker *Checker)
: NoOwnershipChangeVisitor(Sym, Checker) {}

void Profile(llvm::FoldingSetNodeID &ID) const override {
static int Tag = 0;
Expand Down Expand Up @@ -2949,7 +2838,7 @@ void MallocChecker::HandleLeak(SymbolRef Sym, ExplodedNode *N,
R->markInteresting(Sym);
R->addVisitor<MallocBugVisitor>(Sym, true);
if (ShouldRegisterNoOwnershipChangeVisitor)
R->addVisitor<NoOwnershipChangeVisitor>(Sym, this);
R->addVisitor<NoMemOwnershipChangeVisitor>(Sym, this);
C.emitReport(std::move(R));
}

Expand Down
116 changes: 116 additions & 0 deletions clang/lib/StaticAnalyzer/Checkers/NoOwnershipChangeVisitor.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
//===--------------------------------------------------------------*- 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
//
//===----------------------------------------------------------------------===//

#include "NoOwnershipChangeVisitor.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h"
#include "llvm/ADT/SetOperations.h"

using namespace clang;
using namespace ento;
using OwnerSet = NoOwnershipChangeVisitor::OwnerSet;

// Collect which entities point to the allocated memory, and could be
// responsible for deallocating it.
class OwnershipBindingsHandler : public StoreManager::BindingsHandler {
SymbolRef Sym;
OwnerSet &Owners;

public:
OwnershipBindingsHandler(SymbolRef Sym, OwnerSet &Owners)
: Sym(Sym), Owners(Owners) {}

bool HandleBinding(StoreManager &SMgr, Store Store, const MemRegion *Region,
SVal Val) override {
if (Val.getAsSymbol() == Sym)
Owners.insert(Region);
return true;
}

LLVM_DUMP_METHOD void dump() const { dumpToStream(llvm::errs()); }
LLVM_DUMP_METHOD void dumpToStream(llvm::raw_ostream &out) const {
out << "Owners: {\n";
for (const MemRegion *Owner : Owners) {
out << " ";
Owner->dumpToStream(out);
out << ",\n";
}
out << "}\n";
}
};

OwnerSet NoOwnershipChangeVisitor::getOwnersAtNode(const ExplodedNode *N) {
OwnerSet Ret;

ProgramStateRef State = N->getState();
OwnershipBindingsHandler Handler{Sym, Ret};
State->getStateManager().getStoreManager().iterBindings(State->getStore(),
Handler);
return Ret;
}

LLVM_DUMP_METHOD std::string
NoOwnershipChangeVisitor::getFunctionName(const ExplodedNode *CallEnterN) {
if (const CallExpr *CE = llvm::dyn_cast_or_null<CallExpr>(
CallEnterN->getLocationAs<CallEnter>()->getCallExpr()))
if (const FunctionDecl *FD = CE->getDirectCallee())
return FD->getQualifiedNameAsString();
return "";
}

bool NoOwnershipChangeVisitor::wasModifiedInFunction(
const ExplodedNode *CallEnterN, const ExplodedNode *CallExitEndN) {
const Decl *Callee =
CallExitEndN->getFirstPred()->getLocationContext()->getDecl();
const FunctionDecl *FD = dyn_cast<FunctionDecl>(Callee);

// Given that the stack frame was entered, the body should always be
// theoretically obtainable. In case of body farms, the synthesized body
// is not attached to declaration, thus triggering the '!FD->hasBody()'
// branch. That said, would a synthesized body ever intend to handle
// ownership? As of today they don't. And if they did, how would we
// put notes inside it, given that it doesn't match any source locations?
if (!FD || !FD->hasBody())
return false;
if (!doesFnIntendToHandleOwnership(
Callee,
CallExitEndN->getState()->getAnalysisManager().getASTContext()))
return true;

if (hasResourceStateChanged(CallEnterN->getState(), CallExitEndN->getState()))
return true;

OwnerSet CurrOwners = getOwnersAtNode(CallEnterN);
OwnerSet ExitOwners = getOwnersAtNode(CallExitEndN);

// Owners in the current set may be purged from the analyzer later on.
// If a variable is dead (is not referenced directly or indirectly after
// some point), it will be removed from the Store before the end of its
// actual lifetime.
// This means that if the ownership status didn't change, CurrOwners
// must be a superset of, but not necessarily equal to ExitOwners.
return !llvm::set_is_subset(ExitOwners, CurrOwners);
}

PathDiagnosticPieceRef NoOwnershipChangeVisitor::maybeEmitNoteForParameters(
PathSensitiveBugReport &R, const CallEvent &Call, const ExplodedNode *N) {
// TODO: Factor the logic of "what constitutes as an entity being passed
// into a function call" out by reusing the code in
// NoStoreFuncVisitor::maybeEmitNoteForParameters, maybe by incorporating
// the printing technology in UninitializedObject's FieldChainInfo.
ArrayRef<ParmVarDecl *> Parameters = Call.parameters();
for (unsigned I = 0; I < Call.getNumArgs() && I < Parameters.size(); ++I) {
SVal V = Call.getArgSVal(I);
if (V.getAsSymbol() == Sym)
return emitNote(N);
}
return nullptr;
}
77 changes: 77 additions & 0 deletions clang/lib/StaticAnalyzer/Checkers/NoOwnershipChangeVisitor.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
//===--------------------------------------------------------------*- 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
//
//===----------------------------------------------------------------------===//

#include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h"

namespace clang {
namespace ento {

class NoOwnershipChangeVisitor : public NoStateChangeFuncVisitor {
protected:
// The symbol whose (lack of) ownership change we are interested in.
SymbolRef Sym;
const CheckerBase &Checker;

LLVM_DUMP_METHOD static std::string
getFunctionName(const ExplodedNode *CallEnterN);

/// Heuristically guess whether the callee intended to free the resource. This
/// is done syntactically, because we are trying to argue about alternative
/// paths of execution, and as a consequence we don't have path-sensitive
/// information.
virtual bool doesFnIntendToHandleOwnership(const Decl *Callee,
ASTContext &ACtx) = 0;

virtual bool hasResourceStateChanged(ProgramStateRef CallEnterState,
ProgramStateRef CallExitEndState) = 0;

bool wasModifiedInFunction(const ExplodedNode *CallEnterN,
const ExplodedNode *CallExitEndN) final;

virtual PathDiagnosticPieceRef emitNote(const ExplodedNode *N) = 0;

PathDiagnosticPieceRef maybeEmitNoteForObjCSelf(PathSensitiveBugReport &R,
const ObjCMethodCall &Call,
const ExplodedNode *N) final {
// TODO: Implement.
return nullptr;
}

PathDiagnosticPieceRef maybeEmitNoteForCXXThis(PathSensitiveBugReport &R,
const CXXConstructorCall &Call,
const ExplodedNode *N) final {
// TODO: Implement.
return nullptr;
}

// Set this to final, effectively dispatch to emitNote.
PathDiagnosticPieceRef
maybeEmitNoteForParameters(PathSensitiveBugReport &R, const CallEvent &Call,
const ExplodedNode *N) final;

public:
using OwnerSet = llvm::SmallPtrSet<const MemRegion *, 8>;

private:
OwnerSet getOwnersAtNode(const ExplodedNode *N);

public:
NoOwnershipChangeVisitor(SymbolRef Sym, const CheckerBase *Checker)
: NoStateChangeFuncVisitor(bugreporter::TrackingKind::Thorough), Sym(Sym),
Checker(*Checker) {}

void Profile(llvm::FoldingSetNodeID &ID) const override {
static int Tag = 0;
ID.AddPointer(&Tag);
ID.AddPointer(Sym);
}
};
} // namespace ento
} // namespace clang
7 changes: 4 additions & 3 deletions clang/lib/Tooling/Syntax/Tokens.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -383,12 +383,13 @@ llvm::ArrayRef<syntax::Token> TokenBuffer::spelledTokens(FileID FID) const {
return It->second.SpelledTokens;
}

const syntax::Token *TokenBuffer::spelledTokenAt(SourceLocation Loc) const {
const syntax::Token *
TokenBuffer::spelledTokenContaining(SourceLocation Loc) const {
assert(Loc.isFileID());
const auto *Tok = llvm::partition_point(
spelledTokens(SourceMgr->getFileID(Loc)),
[&](const syntax::Token &Tok) { return Tok.location() < Loc; });
if (!Tok || Tok->location() != Loc)
[&](const syntax::Token &Tok) { return Tok.endLocation() <= Loc; });
if (!Tok || Loc < Tok->location())
return nullptr;
return Tok;
}
Expand Down
32 changes: 32 additions & 0 deletions clang/test/AST/Interp/cxx20.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -782,3 +782,35 @@ namespace APValues {
constexpr const A &v = get<A{}>;
constexpr const A &w = get<A{1, &g, &A::n, "hello"}>;
}

namespace self_referencing {
struct S {
S* ptr = nullptr;
constexpr S(int i) : ptr(this) {
if (this == ptr && i)
ptr = nullptr;
}
constexpr ~S() {}
};

void test() {
S s(1);
}
}

namespace GH64949 {
struct f {
int g; // both-note {{subobject declared here}}
constexpr ~f() {}
};

class h {
public:
consteval h(char *) {}
f i;
};

void test() { h{nullptr}; } // both-error {{call to consteval function 'GH64949::h::h' is not a constant expression}} \
// both-note {{subobject 'g' is not initialized}} \
// both-warning {{expression result unused}}
}
16 changes: 16 additions & 0 deletions clang/test/AST/Interp/lambda.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -264,3 +264,19 @@ namespace CaptureDefaults {
};
static_assert(f2() == 3, "");
}

constexpr auto t4 = ([x=42]() consteval { return x; }());
static_assert(t4 == 42, "");

namespace InvalidCapture {

int &f(int *p);
char &f(...);
void g() {
int n = -1; // both-note {{declared here}}
[=] {
int arr[n]; // both-warning {{variable length arrays in C++ are a Clang extension}} \
both-note {{read of non-const variable 'n' is not allowed in a constant expression}}
} ();
}
}
4 changes: 2 additions & 2 deletions clang/test/CXX/basic/basic.lookup/basic.lookup.elab/p2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace test0 {
typedef int A; // expected-note {{declared here}}

int test() {
struct A a; // expected-error {{typedef 'A' cannot be referenced with a struct specifier}}
struct A a; // expected-error {{typedef 'A' cannot be referenced with the 'struct' specifier}}
return a.foo;
}
}
Expand All @@ -18,7 +18,7 @@ namespace test0 {
template <class> class A; // expected-note {{declared here}}

int test() {
struct A a; // expected-error {{template 'A' cannot be referenced with a struct specifier}}
struct A a; // expected-error {{template 'A' cannot be referenced with the 'struct' specifier}}
return a.foo;
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
// RUN: %clang_cc1 -fsyntax-only -verify %s
// RUN: not %clang_cc1 -fsyntax-only -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s

namespace A {
namespace B { }
}

// CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:7-[[@LINE+1]]:7}:"namespace "
using A::B; // expected-error{{using declaration cannot refer to a namespace}}
// expected-note@-1 {{did you mean 'using namespace'?}}
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,18 @@

struct A { typedef int type; };
template<typename T> using X = A; // expected-note {{declared here}}
struct X<int>* p2; // expected-error {{type alias template 'X' cannot be referenced with a struct specifier}}
struct X<int>* p2; // expected-error {{alias template 'X' cannot be referenced with the 'struct' specifier}}


template<typename T> using Id = T; // expected-note {{declared here}}
template<template<typename> class F>
struct Y {
struct F<int> i; // expected-error {{type alias template 'Id' cannot be referenced with a struct specifier}}
struct F<int> i; // expected-error {{alias template 'Id' cannot be referenced with the 'struct' specifier}}
typename F<A>::type j; // ok

// FIXME: don't produce the diagnostic both for the definition and the instantiation.
template<typename T> using U = F<char>; // expected-note 2{{declared here}}
struct Y<F>::template U<char> k; // expected-error 2{{type alias template 'U' cannot be referenced with a struct specifier}}
struct Y<F>::template U<char> k; // expected-error 2{{alias template 'U' cannot be referenced with the 'struct' specifier}}
typename Y<F>::template U<char> l; // ok
};
template struct Y<Id>; // expected-note {{requested here}}
6 changes: 3 additions & 3 deletions clang/test/CXX/drs/cwg2xx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -759,7 +759,7 @@ namespace cwg254 { // cwg254: 2.9
typedef typename T::type type; // ok even if this is a typedef-name, because
// it's not an elaborated-type-specifier
typedef struct T::type foo;
// expected-error@-1 {{typedef 'type' cannot be referenced with a struct specifier}}
// expected-error@-1 {{typedef 'type' cannot be referenced with the 'struct' specifier}}
// expected-note@#cwg254-instantiation {{in instantiation of template class 'cwg254::A<cwg254::C>' requested here}}
// expected-note@#cwg254-C {{declared here}}
};
Expand Down Expand Up @@ -1264,10 +1264,10 @@ namespace cwg298 { // cwg298: 3.1

struct A a;
struct B b;
// expected-error@-1 {{typedef 'B' cannot be referenced with a struct specifier}}
// expected-error@-1 {{typedef 'B' cannot be referenced with the 'struct' specifier}}
// expected-note@#cwg298-B {{declared here}}
struct C c;
// expected-error@-1 {{typedef 'C' cannot be referenced with a struct specifier}}
// expected-error@-1 {{typedef 'C' cannot be referenced with the 'struct' specifier}}
// expected-note@#cwg298-C {{declared here}}

B::B() {}
Expand Down
4 changes: 3 additions & 1 deletion clang/test/CXX/drs/cwg4xx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ namespace cwg407 { // cwg407: 3.8
{
typedef struct S S; // #cwg407-typedef-S
struct S *p;
// expected-error@-1 {{typedef 'S' cannot be referenced with a struct specifier}}
// expected-error@-1 {{typedef 'S' cannot be referenced with the 'struct' specifier}}
// expected-note@#cwg407-typedef-S {{declared here}}
}
}
Expand Down Expand Up @@ -941,8 +941,10 @@ namespace cwg460 { // cwg460: yes
// expected-error@-1 {{using declaration requires a qualified name}}
using cwg460::X;
// expected-error@-1 {{using declaration cannot refer to a namespace}}
// expected-note@-2 {{did you mean 'using namespace'?}}
using X::Q;
// expected-error@-1 {{using declaration cannot refer to a namespace}}
// expected-note@-2 {{did you mean 'using namespace'?}}
}
}

Expand Down
2 changes: 1 addition & 1 deletion clang/test/CXX/temp/temp.decls/temp.friend/p1.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ namespace test7 {

// This shouldn't crash.
template <class T> class D {
friend class A; // expected-error {{template 'A' cannot be referenced with a class specifier}}
friend class A; // expected-error {{template 'A' cannot be referenced with the 'class' specifier}}
};
template class D<int>;
}
Expand Down
2 changes: 1 addition & 1 deletion clang/test/CXX/temp/temp.spec/no-body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ namespace good { // Only good in C++98/03

namespace unsupported {
#ifndef FIXING
template struct y; // expected-error {{template 'y' cannot be referenced with a struct specifier}}
template struct y; // expected-error {{template 'y' cannot be referenced with the 'struct' specifier}}
#endif
}

Expand Down
4 changes: 2 additions & 2 deletions clang/test/CodeGen/debug-info-packed-struct.c
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ struct layout2 {
#pragma pack()
// CHECK: l2_ofs0
// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "l2_ofs1",
// CHECK-SAME: {{.*}}size: 64, offset: 8)
// CHECK-SAME: {{.*}}size: 64, align: 8, offset: 8)
// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "l2_ofs9",
// CHECK-SAME: {{.*}}size: 1, offset: 72, flags: DIFlagBitField, extraData: i64 72)

Expand All @@ -81,7 +81,7 @@ struct layout3 {
#pragma pack()
// CHECK: l3_ofs0
// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "l3_ofs4",
// CHECK-SAME: {{.*}}size: 64, offset: 32)
// CHECK-SAME: {{.*}}size: 64, align: 32, offset: 32)
// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "l3_ofs12",
// CHECK-SAME: {{.*}}size: 1, offset: 96, flags: DIFlagBitField, extraData: i64 96)

Expand Down
4 changes: 4 additions & 0 deletions clang/test/CodeGen/target-data.c
Original file line number Diff line number Diff line change
Expand Up @@ -268,3 +268,7 @@
// RUN: %clang_cc1 -triple ve -o - -emit-llvm %s | \
// RUN: FileCheck %s -check-prefix=VE
// VE: target datalayout = "e-m:e-i64:64-n32:64-S128-v64:64:64-v128:64:64-v256:64:64-v512:64:64-v1024:64:64-v2048:64:64-v4096:64:64-v8192:64:64-v16384:64:64"

// RUN: %clang_cc1 -triple spirv64-amd -o - -emit-llvm %s | \
// RUN: FileCheck %s -check-prefix=SPIR64
// AMDGPUSPIRV64: target datalayout = "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-G1-P4-A0"
Loading