1,547 changes: 713 additions & 834 deletions clang/include/clang/ExtractAPI/API.h

Large diffs are not rendered by default.

103 changes: 103 additions & 0 deletions clang/include/clang/ExtractAPI/APIRecords.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
//===- ExtractAPI/APIRecords.inc --------------------------------*- 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
//
//===----------------------------------------------------------------------===//
///
/// \file
/// This file defines the classes defined from ExtractAPI's APIRecord
///
//===----------------------------------------------------------------------===//

#ifndef ABSTRACT_RECORD
#define ABSTRACT_RECORD(CLASS, BASE) RECORD(CLASS, BASE)
#endif
#ifndef CONCRETE_RECORD
#define CONCRETE_RECORD(CLASS, BASE, KIND) RECORD(CLASS, BASE)
#endif
#ifndef RECORD
#define RECORD(CLASS, BASE)
#endif

CONCRETE_RECORD(NamespaceRecord, APIRecord, RK_Namespace)
CONCRETE_RECORD(GlobalFunctionRecord, APIRecord, RK_GlobalFunction)
CONCRETE_RECORD(GlobalFunctionTemplateRecord, GlobalFunctionRecord,
RK_GlobalFunctionTemplate)
CONCRETE_RECORD(GlobalFunctionTemplateSpecializationRecord,
GlobalFunctionRecord, RK_GlobalFunctionTemplateSpecialization)
CONCRETE_RECORD(GlobalVariableRecord, APIRecord, RK_GlobalVariable)
CONCRETE_RECORD(GlobalVariableTemplateRecord, GlobalVariableRecord,
RK_GlobalVariableTemplate)
CONCRETE_RECORD(GlobalVariableTemplateSpecializationRecord,
GlobalVariableRecord, RK_GlobalVariableTemplateSpecialization)
CONCRETE_RECORD(GlobalVariableTemplatePartialSpecializationRecord,
GlobalVariableRecord,
RK_GlobalVariableTemplatePartialSpecialization)
CONCRETE_RECORD(EnumConstantRecord, APIRecord, RK_EnumConstant)
CONCRETE_RECORD(EnumRecord, APIRecord, RK_Enum)
ABSTRACT_RECORD(RecordFieldRecord, APIRecord)
ABSTRACT_RECORD(RecordRecord, APIRecord)
CONCRETE_RECORD(StructFieldRecord, RecordFieldRecord, RK_StructField)
CONCRETE_RECORD(StructRecord, APIRecord, RK_Struct)
CONCRETE_RECORD(UnionFieldRecord, RecordFieldRecord, RK_UnionField)
CONCRETE_RECORD(UnionRecord, APIRecord, RK_Union)
CONCRETE_RECORD(CXXFieldRecord, APIRecord, RK_CXXField)
CONCRETE_RECORD(CXXFieldTemplateRecord, CXXFieldRecord, RK_CXXFieldTemplate)
ABSTRACT_RECORD(CXXMethodRecord, APIRecord)
CONCRETE_RECORD(CXXConstructorRecord, CXXMethodRecord, RK_CXXConstructorMethod)
CONCRETE_RECORD(CXXDestructorRecord, CXXMethodRecord, RK_CXXDestructorMethod)
CONCRETE_RECORD(CXXStaticMethodRecord, CXXMethodRecord, RK_CXXStaticMethod)
CONCRETE_RECORD(CXXInstanceMethodRecord, CXXMethodRecord, RK_CXXInstanceMethod)
CONCRETE_RECORD(CXXMethodTemplateRecord, CXXMethodRecord, RK_CXXMethodTemplate)
CONCRETE_RECORD(CXXMethodTemplateSpecializationRecord, CXXMethodRecord,
RK_CXXMethodTemplateSpecialization)
ABSTRACT_RECORD(ObjCPropertyRecord, APIRecord)
CONCRETE_RECORD(ObjCInstancePropertyRecord, ObjCPropertyRecord,
RK_ObjCInstanceProperty)
CONCRETE_RECORD(ObjCClassPropertyRecord, ObjCPropertyRecord,
RK_ObjCClassProperty)
CONCRETE_RECORD(ObjCInstanceVariableRecord, APIRecord, RK_ObjCIvar)
ABSTRACT_RECORD(ObjCMethodRecord, APIRecord)
CONCRETE_RECORD(ObjCInstanceMethodRecord, ObjCMethodRecord,
RK_ObjCInstanceMethod)
CONCRETE_RECORD(ObjCClassMethodRecord, ObjCMethodRecord, RK_ObjCClassMethod)
CONCRETE_RECORD(StaticFieldRecord, CXXFieldRecord, RK_StaticField)
ABSTRACT_RECORD(ObjCContainerRecord, APIRecord)
CONCRETE_RECORD(CXXClassRecord, APIRecord, RK_CXXClass)
CONCRETE_RECORD(ClassTemplateRecord, CXXClassRecord, RK_ClassTemplate)
CONCRETE_RECORD(ClassTemplateSpecializationRecord, CXXClassRecord,
RK_ClassTemplateSpecialization)
CONCRETE_RECORD(ClassTemplatePartialSpecializationRecord, CXXClassRecord,
RK_ClassTemplatePartialSpecialization)
CONCRETE_RECORD(ConceptRecord, APIRecord, RK_Concept)
CONCRETE_RECORD(ObjCCategoryRecord, ObjCContainerRecord, RK_ObjCCategory)
CONCRETE_RECORD(ObjCInterfaceRecord, ObjCContainerRecord, RK_ObjCInterface)
CONCRETE_RECORD(ObjCProtocolRecord, ObjCContainerRecord, RK_ObjCProtocol)
CONCRETE_RECORD(MacroDefinitionRecord, APIRecord, RK_MacroDefinition)
CONCRETE_RECORD(TypedefRecord, APIRecord, RK_Typedef)

#undef CONCRETE_RECORD
#undef ABSTRACT_RECORD
#undef RECORD

#ifndef RECORD_CONTEXT
#define RECORD_CONTEXT(CLASS, KIND)
#endif

RECORD_CONTEXT(NamespaceRecord, RK_Namespace)
RECORD_CONTEXT(EnumRecord, RK_Enum)
RECORD_CONTEXT(StructRecord, RK_Struct)
RECORD_CONTEXT(UnionRecord, RK_Union)
RECORD_CONTEXT(ObjCCategoryRecord, RK_ObjCCategory)
RECORD_CONTEXT(ObjCInterfaceRecord, RK_ObjCInterface)
RECORD_CONTEXT(ObjCProtocolRecord, RK_ObjCProtocol)
RECORD_CONTEXT(CXXClassRecord, RK_CXXClass)
RECORD_CONTEXT(ClassTemplateRecord, RK_ClassTemplate)
RECORD_CONTEXT(ClassTemplateSpecializationRecord,
RK_ClassTemplateSpecialization)
RECORD_CONTEXT(ClassTemplatePartialSpecializationRecord,
RK_ClassTemplatePartialSpecialization)

#undef RECORD_CONTEXT
14 changes: 14 additions & 0 deletions clang/include/clang/ExtractAPI/DeclarationFragments.h
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,18 @@ class DeclarationFragments {
/// appending to chain up consecutive appends.
DeclarationFragments &appendSpace();

/// Append a text Fragment of a semicolon character.
///
/// \returns a reference to the DeclarationFragments object itself after
/// appending to chain up consecutive appends.
DeclarationFragments &appendSemicolon();

/// Removes a trailing semicolon character if present.
///
/// \returns a reference to the DeclarationFragments object itself after
/// removing to chain up consecutive operations.
DeclarationFragments &removeTrailingSemicolon();

/// Get the string description of a FragmentKind \p Kind.
static StringRef getFragmentKindString(FragmentKind Kind);

Expand All @@ -192,12 +204,14 @@ class DeclarationFragments {
static DeclarationFragments getStructureTypeFragment(const RecordDecl *Decl);

private:
DeclarationFragments &appendUnduplicatedTextCharacter(char Character);
std::vector<Fragment> Fragments;
};

class AccessControl {
public:
AccessControl(std::string Access) : Access(Access) {}
AccessControl() : Access("public") {}

const std::string &getAccess() const { return Access; }

Expand Down
8 changes: 5 additions & 3 deletions clang/include/clang/ExtractAPI/ExtractAPIActionBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@

#include "clang/ExtractAPI/API.h"
#include "clang/ExtractAPI/APIIgnoresList.h"
#include "clang/Frontend/CompilerInstance.h"
#include "llvm/Support/raw_ostream.h"

namespace clang {

Expand All @@ -29,8 +31,8 @@ class ExtractAPIActionBase {
/// A representation of the APIs this action extracts.
std::unique_ptr<extractapi::APISet> API;

/// A stream to the output file of this action.
std::unique_ptr<raw_pwrite_stream> OS;
/// A stream to the main output file of this action.
std::unique_ptr<llvm::raw_pwrite_stream> OS;

/// The product this action is extracting API information for.
std::string ProductName;
Expand All @@ -46,7 +48,7 @@ class ExtractAPIActionBase {
///
/// Use the serializer to generate output symbol graph files from
/// the information gathered during the execution of Action.
void ImplEndSourceFileAction();
void ImplEndSourceFileAction(CompilerInstance &CI);
};

} // namespace clang
Expand Down
674 changes: 346 additions & 328 deletions clang/include/clang/ExtractAPI/ExtractAPIVisitor.h

Large diffs are not rendered by default.

6 changes: 0 additions & 6 deletions clang/include/clang/ExtractAPI/FrontendActions.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,6 @@ class ExtractAPIAction : public ASTFrontendAction,
void EndSourceFileAction() override;

static StringRef getInputBufferName() { return "<extract-api-includes>"; }

static std::unique_ptr<llvm::raw_pwrite_stream>
CreateOutputFile(CompilerInstance &CI, StringRef InFile);
};

/// Wrap ExtractAPIAction on top of a pre-existing action
Expand Down Expand Up @@ -85,9 +82,6 @@ class WrappingExtractAPIAction : public WrapperFrontendAction,
/// actions. This is the place where all the gathered symbol graph
/// information is emited.
void EndSourceFileAction() override;

static std::unique_ptr<llvm::raw_pwrite_stream>
CreateOutputFile(CompilerInstance &CI, StringRef InFile);
};

} // namespace clang
Expand Down
172 changes: 172 additions & 0 deletions clang/include/clang/ExtractAPI/Serialization/APISetVisitor.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
//===- ExtractAPI/Serialization/APISetVisitor.h ----------------*- 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
//
//===----------------------------------------------------------------------===//
///
/// \file
/// This file defines the ExtractAPI APISetVisitor interface.
///
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_EXTRACTAPI_SERIALIZATION_SERIALIZERBASE_H
#define LLVM_CLANG_EXTRACTAPI_SERIALIZATION_SERIALIZERBASE_H

#include "clang/ExtractAPI/API.h"

namespace clang {
namespace extractapi {

// A helper macro to implement short-circuiting when recursing. It
// invokes CALL_EXPR, which must be a method call, on the derived
// object (s.t. a user of RecursiveASTVisitor can override the method
// in CALL_EXPR).
#define TRY_TO(CALL_EXPR) \
do { \
if (!getDerived()->CALL_EXPR) \
return false; \
} while (false)

/// The base interface of visitors for API information, the interface and usage
/// is almost identical to RecurisveASTVistor. This class performs three
/// distinct tasks:
/// 1. traverse the APISet (i.e. go to every record);
/// 2. at a given record, walk up the class hierarchy starting from the record's
/// dynamic type until APIRecord is reached.
/// 3. given a (record, class) combination where 'class' is some base class of
/// the dynamic type of 'record', call a user-overridable function to actually
/// visit the record.
///
/// These tasks are done by three groups of methods, respectively:
/// 1. traverseRecord(APIRecord *x) does task #1, it is the entry point for
/// traversing the records starting from x. This method simply forwards to
/// traverseFoo(Foo *x) where Foo is the dynamic type of *x, which calls
/// walkUpFromFoo(x) and then recursively visits the child records of x.
/// 2. walkUpFromFoo(Foo *x) does task #2. It doesn't visit children records of
/// x, instead it first calls walkUpFromBar(x) where Bar is the direct parent
/// class of Foo (unless Foo has no parent) and then calls visitFoo(x).
/// 3. visitFoo(Foo *x) does task #3.
///
/// These three method groups are tiered (traverse* > walkUpFrom* >
/// visit*). A method (e.g. traverse*) may call methods from the same
/// tier (e.g. other traverse*) or one tier lower (e.g. walkUpFrom*).
/// It may not call methods from a higher tier.
///
/// Note that since walkUpFromFoo() calls walkUpFromBar() (where Bar
/// is Foo's super class) before calling visitFoo(), the result is
/// that the visit*() methods for a given record are called in the
/// top-down order (e.g. for a record of type ObjCInstancePropertyRecord, the
/// order will be visitRecord(), visitObjCPropertyRecord(), and then
/// visitObjCInstancePropertyRecord()).
///
/// This scheme guarantees that all visit*() calls for the same record
/// are grouped together. In other words, visit*() methods for different
/// records are never interleaved.
///
/// Clients of this visitor should subclass the visitor (providing
/// themselves as the template argument, using the curiously recurring
/// template pattern) and override any of the traverse*, walkUpFrom*,
/// and visit* methods for records where the visitor should customize
/// behavior. Most users only need to override visit*. Advanced
/// users may override traverse* and walkUpFrom* to implement custom
/// traversal strategies. Returning false from one of these overridden
/// functions will abort the entire traversal.
template <typename Derived> class APISetVisitor {
public:
bool traverseAPISet() {
for (const APIRecord *TLR : API.getTopLevelRecords()) {
TRY_TO(traverseAPIRecord(TLR));
}
return true;
}

bool traverseAPIRecord(const APIRecord *Record);
bool walkUpFromAPIRecord(const APIRecord *Record) {
TRY_TO(visitAPIRecord(Record));
return true;
}
bool visitAPIRecord(const APIRecord *Record) { return true; }

#define GENERATE_TRAVERSE_METHOD(CLASS, BASE) \
bool traverse##CLASS(const CLASS *Record) { \
TRY_TO(walkUpFrom##CLASS(Record)); \
TRY_TO(traverseRecordContext(dyn_cast<RecordContext>(Record))); \
return true; \
}

#define GENERATE_WALKUP_AND_VISIT_METHODS(CLASS, BASE) \
bool walkUpFrom##CLASS(const CLASS *Record) { \
TRY_TO(walkUpFrom##BASE(Record)); \
TRY_TO(visit##CLASS(Record)); \
return true; \
} \
bool visit##CLASS(const CLASS *Record) { return true; }

#define CONCRETE_RECORD(CLASS, BASE, KIND) \
GENERATE_TRAVERSE_METHOD(CLASS, BASE) \
GENERATE_WALKUP_AND_VISIT_METHODS(CLASS, BASE)

#define ABSTRACT_RECORD(CLASS, BASE) \
GENERATE_WALKUP_AND_VISIT_METHODS(CLASS, BASE)

#include "../APIRecords.inc"

#undef GENERATE_WALKUP_AND_VISIT_METHODS
#undef GENERATE_TRAVERSE_METHOD

bool traverseRecordContext(const RecordContext *);

protected:
const APISet &API;

public:
APISetVisitor() = delete;
APISetVisitor(const APISetVisitor &) = delete;
APISetVisitor(APISetVisitor &&) = delete;
APISetVisitor &operator=(const APISetVisitor &) = delete;
APISetVisitor &operator=(APISetVisitor &&) = delete;

protected:
APISetVisitor(const APISet &API) : API(API) {}
~APISetVisitor() = default;

Derived *getDerived() { return static_cast<Derived *>(this); };
};

template <typename Derived>
bool APISetVisitor<Derived>::traverseRecordContext(
const RecordContext *Context) {
if (!Context)
return true;

for (auto *Child : Context->records())
TRY_TO(traverseAPIRecord(Child));

return true;
}

template <typename Derived>
bool APISetVisitor<Derived>::traverseAPIRecord(const APIRecord *Record) {
switch (Record->getKind()) {
#define CONCRETE_RECORD(CLASS, BASE, KIND) \
case APIRecord::KIND: { \
TRY_TO(traverse##CLASS(static_cast<const CLASS *>(Record))); \
break; \
}
#include "../APIRecords.inc"
case APIRecord::RK_Unknown: {
TRY_TO(walkUpFromAPIRecord(static_cast<const APIRecord *>(Record)));
break;
}
default:
llvm_unreachable("API Record with uninstantiable kind");
}
return true;
}

} // namespace extractapi
} // namespace clang

#endif // LLVM_CLANG_EXTRACTAPI_SERIALIZATION_SERIALIZERBASE_H
314 changes: 0 additions & 314 deletions clang/include/clang/ExtractAPI/Serialization/SerializerBase.h

This file was deleted.

254 changes: 147 additions & 107 deletions clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,17 @@
#ifndef LLVM_CLANG_EXTRACTAPI_SERIALIZATION_SYMBOLGRAPHSERIALIZER_H
#define LLVM_CLANG_EXTRACTAPI_SERIALIZATION_SYMBOLGRAPHSERIALIZER_H

#include "clang/Basic/Module.h"
#include "clang/ExtractAPI/API.h"
#include "clang/ExtractAPI/APIIgnoresList.h"
#include "clang/ExtractAPI/Serialization/SerializerBase.h"
#include "clang/ExtractAPI/Serialization/APISetVisitor.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/ADT/Twine.h"
#include "llvm/Support/JSON.h"
#include "llvm/Support/VersionTuple.h"
#include "llvm/Support/raw_ostream.h"
Expand All @@ -35,7 +41,30 @@ using namespace llvm::json;
/// Common options to customize the visitor output.
struct SymbolGraphSerializerOption {
/// Do not include unnecessary whitespaces to save space.
bool Compact;
bool Compact = true;
bool EmitSymbolLabelsForTesting = false;
};

/// A representation of the contents of a given module symbol graph
struct ExtendedModule {
ExtendedModule() = default;
ExtendedModule(ExtendedModule &&EM) = default;
ExtendedModule &operator=(ExtendedModule &&EM) = default;
// Copies are expensive so disable them.
ExtendedModule(const ExtendedModule &EM) = delete;
ExtendedModule &operator=(const ExtendedModule &EM) = delete;

/// Add a symbol to the module, do not store the resulting pointer or use it
/// across insertions.
Object *addSymbol(Object &&Symbol);

void addRelationship(Object &&Relationship);

/// A JSON array of formatted symbols from an \c APISet.
Array Symbols;

/// A JSON array of formatted symbol relationships from an \c APISet.
Array Relationships;
};

/// The visitor that organizes API information in the Symbol Graph format.
Expand All @@ -44,28 +73,54 @@ struct SymbolGraphSerializerOption {
/// models an API set as a directed graph, where nodes are symbol declarations,
/// and edges are relationships between the connected symbols.
class SymbolGraphSerializer : public APISetVisitor<SymbolGraphSerializer> {
/// A JSON array of formatted symbols in \c APISet.
Array Symbols;
private:
using Base = APISetVisitor<SymbolGraphSerializer>;
/// The main symbol graph that contains symbols that are either top-level or a
/// are related to symbols defined in this product/module.
ExtendedModule MainModule;

/// A JSON array of formatted symbol relationships in \c APISet.
Array Relationships;
/// Additional symbol graphs that contain symbols that are related to symbols
/// defined in another product/module. The key of this map is the module name
/// of the extended module.
llvm::StringMap<ExtendedModule> ExtendedModules;

/// The Symbol Graph format version used by this serializer.
static const VersionTuple FormatVersion;

/// Indicates whether child symbols should be visited. This is mainly
/// Indicates whether to take into account the extended module. This is only
/// useful for \c serializeSingleSymbolSGF.
bool ShouldRecurse;
bool ForceEmitToMainModule;

public:
/// Serialize the APIs in \c APISet in the Symbol Graph format.
// Stores the references required to construct path components for the
// currently visited APIRecord.
llvm::SmallVector<SymbolReference, 8> Hierarchy;

/// The list of symbols to ignore.
///
/// \returns a JSON object that contains the root of the formatted
/// Symbol Graph.
Object serialize();
/// Note: This should be consulted before emitting a symbol.
const APIIgnoresList &IgnoresList;

/// Wrap serialize(void) and write out the serialized JSON object to \p os.
void serialize(raw_ostream &os);
const bool EmitSymbolLabelsForTesting = false;

/// The object instantiated by the last call to serializeAPIRecord.
Object *CurrentSymbol = nullptr;

/// The module to which \p CurrentSymbol belongs too.
ExtendedModule *ModuleForCurrentSymbol = nullptr;

public:
static void
serializeMainSymbolGraph(raw_ostream &OS, const APISet &API,
const APIIgnoresList &IgnoresList,
SymbolGraphSerializerOption Options = {});

static void serializeWithExtensionGraphs(
raw_ostream &MainOutput, const APISet &API,
const APIIgnoresList &IgnoresList,
llvm::function_ref<
std::unique_ptr<llvm::raw_pwrite_stream>(llvm::Twine BaseFileName)>
CreateOutputStream,
SymbolGraphSerializerOption Options = {});

/// Serialize a single symbol SGF. This is primarily used for libclang.
///
Expand All @@ -75,6 +130,7 @@ class SymbolGraphSerializer : public APISetVisitor<SymbolGraphSerializer> {
static std::optional<Object> serializeSingleSymbolSGF(StringRef USR,
const APISet &API);

private:
/// The kind of a relationship between two symbols.
enum RelationshipKind {
/// The source symbol is a member of the target symbol.
Expand All @@ -94,16 +150,32 @@ class SymbolGraphSerializer : public APISetVisitor<SymbolGraphSerializer> {
ExtensionTo,
};

/// Serialize a single record.
void serializeSingleRecord(const APIRecord *Record);

/// Get the string representation of the relationship kind.
static StringRef getRelationshipString(RelationshipKind Kind);

void serializeRelationship(RelationshipKind Kind,
const SymbolReference &Source,
const SymbolReference &Target,
ExtendedModule &Into);

enum ConstraintKind { Conformance, ConditionalConformance };

static StringRef getConstraintString(ConstraintKind Kind);

private:
/// Just serialize the currently recorded objects in Symbol Graph format.
Object serializeCurrentGraph();
/// Serialize the APIs in \c ExtendedModule.
///
/// \returns a JSON object that contains the root of the formatted
/// Symbol Graph.
Object serializeGraph(StringRef ModuleName, ExtendedModule &&EM);

/// Serialize the APIs in \c ExtendedModule in the Symbol Graph format and
/// write them to the provide stream.
void serializeGraphToStream(raw_ostream &OS,
SymbolGraphSerializerOption Options,
StringRef ModuleName, ExtendedModule &&EM);

/// Synthesize the metadata section of the Symbol Graph format.
///
Expand All @@ -117,124 +189,92 @@ class SymbolGraphSerializer : public APISetVisitor<SymbolGraphSerializer> {
/// by the given API set.
/// Note that "module" here is not to be confused with the Clang/C++ module
/// concept.
Object serializeModule() const;
Object serializeModuleObject(StringRef ModuleName) const;

Array serializePathComponents(const APIRecord *Record) const;

/// Determine if the given \p Record should be skipped during serialization.
bool shouldSkip(const APIRecord &Record) const;
bool shouldSkip(const APIRecord *Record) const;

ExtendedModule &getModuleForCurrentSymbol();

/// Format the common API information for \p Record.
///
/// This handles the shared information of all kinds of API records,
/// for example identifier and source location. The resulting object is then
/// augmented with kind-specific symbol information by the caller.
/// This method also checks if the given \p Record should be skipped during
/// serialization.
/// for example identifier, source location and path components. The resulting
/// object is then augmented with kind-specific symbol information in
/// subsequent visit* methods by accessing the \p State member variable. This
/// method also checks if the given \p Record should be skipped during
/// serialization. This should be called only once per concrete APIRecord
/// instance and the first visit* method to be called is responsible for
/// calling this. This is normally visitAPIRecord unless a walkUpFromFoo
/// method is implemented along the inheritance hierarchy in which case the
/// visitFoo method needs to call this.
///
/// \returns \c std::nullopt if this \p Record should be skipped, or a JSON
/// object containing common symbol information of \p Record.
template <typename RecordTy>
std::optional<Object> serializeAPIRecord(const RecordTy &Record) const;

/// Helper method to serialize second-level member records of \p Record and
/// the member-of relationships.
template <typename MemberTy>
void serializeMembers(const APIRecord &Record,
const SmallVector<std::unique_ptr<MemberTy>> &Members);

/// Serialize the \p Kind relationship between \p Source and \p Target.
///
/// Record the relationship between the two symbols in
/// SymbolGraphSerializer::Relationships.
void serializeRelationship(RelationshipKind Kind, SymbolReference Source,
SymbolReference Target);

protected:
/// The list of symbols to ignore.
///
/// Note: This should be consulted before emitting a symbol.
const APIIgnoresList &IgnoresList;

SymbolGraphSerializerOption Options;

llvm::StringSet<> visitedCategories;
/// \returns \c nullptr if this \p Record should be skipped, or a pointer to
/// JSON object containing common symbol information of \p Record. Do not
/// store the returned pointer only use it to augment the object with record
/// specific information as it directly points to the object in the
/// \p ExtendedModule, the pointer won't be valid as soon as another object is
/// inserted into the module.
void serializeAPIRecord(const APIRecord *Record);

public:
void visitNamespaceRecord(const NamespaceRecord &Record);

/// Visit a global function record.
void visitGlobalFunctionRecord(const GlobalFunctionRecord &Record);

/// Visit a global variable record.
void visitGlobalVariableRecord(const GlobalVariableRecord &Record);

/// Visit an enum record.
void visitEnumRecord(const EnumRecord &Record);

/// Visit a record record.
void visitRecordRecord(const RecordRecord &Record);

void visitStaticFieldRecord(const StaticFieldRecord &Record);
// Handle if records should be skipped at this level of the traversal to
// ensure that children of skipped records aren't serialized.
bool traverseAPIRecord(const APIRecord *Record);

void visitCXXClassRecord(const CXXClassRecord &Record);
bool visitAPIRecord(const APIRecord *Record);

void visitClassTemplateRecord(const ClassTemplateRecord &Record);

void visitClassTemplateSpecializationRecord(
const ClassTemplateSpecializationRecord &Record);

void visitClassTemplatePartialSpecializationRecord(
const ClassTemplatePartialSpecializationRecord &Record);

void visitCXXInstanceMethodRecord(const CXXInstanceMethodRecord &Record);
/// Visit a global function record.
bool visitGlobalFunctionRecord(const GlobalFunctionRecord *Record);

void visitCXXStaticMethodRecord(const CXXStaticMethodRecord &Record);
bool visitCXXClassRecord(const CXXClassRecord *Record);

void visitMethodTemplateRecord(const CXXMethodTemplateRecord &Record);
bool visitClassTemplateRecord(const ClassTemplateRecord *Record);

void visitMethodTemplateSpecializationRecord(
const CXXMethodTemplateSpecializationRecord &Record);
bool visitClassTemplatePartialSpecializationRecord(
const ClassTemplatePartialSpecializationRecord *Record);

void visitCXXFieldRecord(const CXXFieldRecord &Record);
bool visitCXXMethodRecord(const CXXMethodRecord *Record);

void visitCXXFieldTemplateRecord(const CXXFieldTemplateRecord &Record);
bool visitCXXMethodTemplateRecord(const CXXMethodTemplateRecord *Record);

void visitConceptRecord(const ConceptRecord &Record);
bool visitCXXFieldTemplateRecord(const CXXFieldTemplateRecord *Record);

void
visitGlobalVariableTemplateRecord(const GlobalVariableTemplateRecord &Record);
bool visitConceptRecord(const ConceptRecord *Record);

void visitGlobalVariableTemplateSpecializationRecord(
const GlobalVariableTemplateSpecializationRecord &Record);
bool
visitGlobalVariableTemplateRecord(const GlobalVariableTemplateRecord *Record);

void visitGlobalVariableTemplatePartialSpecializationRecord(
const GlobalVariableTemplatePartialSpecializationRecord &Record);
bool visitGlobalVariableTemplatePartialSpecializationRecord(
const GlobalVariableTemplatePartialSpecializationRecord *Record);

void
visitGlobalFunctionTemplateRecord(const GlobalFunctionTemplateRecord &Record);
bool
visitGlobalFunctionTemplateRecord(const GlobalFunctionTemplateRecord *Record);

void visitGlobalFunctionTemplateSpecializationRecord(
const GlobalFunctionTemplateSpecializationRecord &Record);
bool visitObjCContainerRecord(const ObjCContainerRecord *Record);

/// Visit an Objective-C container record.
void visitObjCContainerRecord(const ObjCContainerRecord &Record);
bool visitObjCInterfaceRecord(const ObjCInterfaceRecord *Record);

/// Visit an Objective-C category record.
void visitObjCCategoryRecord(const ObjCCategoryRecord &Record);
bool traverseObjCCategoryRecord(const ObjCCategoryRecord *Record);
bool walkUpFromObjCCategoryRecord(const ObjCCategoryRecord *Record);
bool visitObjCCategoryRecord(const ObjCCategoryRecord *Record);

/// Visit a macro definition record.
void visitMacroDefinitionRecord(const MacroDefinitionRecord &Record);
bool visitObjCMethodRecord(const ObjCMethodRecord *Record);

/// Visit a typedef record.
void visitTypedefRecord(const TypedefRecord &Record);
bool
visitObjCInstanceVariableRecord(const ObjCInstanceVariableRecord *Record);

/// Serialize a single record.
void serializeSingleRecord(const APIRecord *Record);
bool walkUpFromTypedefRecord(const TypedefRecord *Record);
bool visitTypedefRecord(const TypedefRecord *Record);

SymbolGraphSerializer(const APISet &API, const APIIgnoresList &IgnoresList,
SymbolGraphSerializerOption Options = {},
bool ShouldRecurse = true)
: APISetVisitor(API), ShouldRecurse(ShouldRecurse),
IgnoresList(IgnoresList), Options(Options) {}
bool EmitSymbolLabelsForTesting = false,
bool ForceEmitToMainModule = false)
: Base(API), ForceEmitToMainModule(ForceEmitToMainModule),
IgnoresList(IgnoresList),
EmitSymbolLabelsForTesting(EmitSymbolLabelsForTesting) {}
};

} // namespace extractapi
Expand Down
25 changes: 21 additions & 4 deletions clang/include/clang/Frontend/FrontendOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "clang/Sema/CodeCompleteOptions.h"
#include "clang/Serialization/ModuleFileExtension.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/MemoryBuffer.h"
#include <cassert>
#include <map>
Expand Down Expand Up @@ -387,6 +388,22 @@ class FrontendOptions {
LLVM_PREFERRED_TYPE(bool)
unsigned ModulesShareFileManager : 1;

/// Whether to emit symbol graph files as a side effect of compilation.
LLVM_PREFERRED_TYPE(bool)
unsigned EmitSymbolGraph : 1;

/// Whether to emit additional symbol graphs for extended modules.
LLVM_PREFERRED_TYPE(bool)
unsigned EmitExtensionSymbolGraphs : 1;

/// Whether to emit symbol labels for testing in generated symbol graphs
LLVM_PREFERRED_TYPE(bool)
unsigned EmitSymbolGraphSymbolLabelsForTesting : 1;

/// Whether to emit symbol labels for testing in generated symbol graphs
LLVM_PREFERRED_TYPE(bool)
unsigned EmitPrettySymbolGraphs : 1;

CodeCompleteOptions CodeCompleteOpts;

/// Specifies the output format of the AST.
Expand Down Expand Up @@ -496,10 +513,8 @@ class FrontendOptions {
// ignore when extracting documentation.
std::vector<std::string> ExtractAPIIgnoresFileList;

// Currently this is only used as part of the `-emit-symbol-graph`
// action.
// Location of output directory where symbol graph information would
// be dumped
// be dumped. This overrides regular -o output file specification
std::string SymbolGraphOutputDir;

/// Args to pass to the plugins
Expand Down Expand Up @@ -565,7 +580,9 @@ class FrontendOptions {
BuildingImplicitModuleUsesLock(true), ModulesEmbedAllFiles(false),
IncludeTimestamps(true), UseTemporary(true),
AllowPCMWithCompilerErrors(false), ModulesShareFileManager(true),
TimeTraceGranularity(500) {}
EmitSymbolGraph(false), EmitExtensionSymbolGraphs(false),
EmitSymbolGraphSymbolLabelsForTesting(false),
EmitPrettySymbolGraphs(false), TimeTraceGranularity(500) {}

/// getInputKindForExtension - Return the appropriate input kind for a file
/// extension. For example, "c" would return Language::C.
Expand Down
2 changes: 1 addition & 1 deletion clang/include/clang/Serialization/ASTBitCodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ namespace serialization {
/// Version 4 of AST files also requires that the version control branch and
/// revision match exactly, since there is no backward compatibility of
/// AST files at this time.
const unsigned VERSION_MAJOR = 29;
const unsigned VERSION_MAJOR = 30;

/// AST file minor version number supported by this version of
/// Clang.
Expand Down
8 changes: 5 additions & 3 deletions clang/include/clang/Serialization/ASTWriter.h
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,7 @@ class ASTWriter : public ASTDeserializationListener,
void WriteReferencedSelectorsPool(Sema &SemaRef);
void WriteIdentifierTable(Preprocessor &PP, IdentifierResolver &IdResolver,
bool IsModule);
void WriteDeclAndTypes(ASTContext &Context);
void WriteDeclUpdatesBlocks(RecordDataImpl &OffsetsRecord);
void WriteDeclContextVisibleUpdate(const DeclContext *DC);
void WriteFPPragmaOptions(const FPOptionsOverride &Opts);
Expand Down Expand Up @@ -846,7 +847,7 @@ class ASTWriter : public ASTDeserializationListener,
/// AST and semantic-analysis consumer that generates a
/// precompiled header from the parsed source code.
class PCHGenerator : public SemaConsumer {
const Preprocessor &PP;
Preprocessor &PP;
std::string OutputFile;
std::string isysroot;
Sema *SemaPtr;
Expand All @@ -867,11 +868,12 @@ class PCHGenerator : public SemaConsumer {
DiagnosticsEngine &getDiagnostics() const {
return SemaPtr->getDiagnostics();
}
Preprocessor &getPreprocessor() { return PP; }

virtual Module *getEmittingModule(ASTContext &Ctx);

public:
PCHGenerator(const Preprocessor &PP, InMemoryModuleCache &ModuleCache,
PCHGenerator(Preprocessor &PP, InMemoryModuleCache &ModuleCache,
StringRef OutputFile, StringRef isysroot,
std::shared_ptr<PCHBuffer> Buffer,
ArrayRef<std::shared_ptr<ModuleFileExtension>> Extensions,
Expand All @@ -893,7 +895,7 @@ class ReducedBMIGenerator : public PCHGenerator {
virtual Module *getEmittingModule(ASTContext &Ctx) override;

public:
ReducedBMIGenerator(const Preprocessor &PP, InMemoryModuleCache &ModuleCache,
ReducedBMIGenerator(Preprocessor &PP, InMemoryModuleCache &ModuleCache,
StringRef OutputFile);

void HandleTranslationUnit(ASTContext &Ctx) override;
Expand Down
6 changes: 3 additions & 3 deletions clang/lib/AST/DeclBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1852,9 +1852,9 @@ DeclContext::lookup(DeclarationName Name) const {

DeclContext::lookup_result
DeclContext::noload_lookup(DeclarationName Name) {
assert(getDeclKind() != Decl::LinkageSpec &&
getDeclKind() != Decl::Export &&
"should not perform lookups into transparent contexts");
// For transparent DeclContext, we should lookup in their enclosing context.
if (getDeclKind() == Decl::LinkageSpec || getDeclKind() == Decl::Export)
return getParent()->noload_lookup(Name);

DeclContext *PrimaryContext = getPrimaryContext();
if (PrimaryContext != this)
Expand Down
13 changes: 7 additions & 6 deletions clang/lib/AST/TypePrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1746,14 +1746,15 @@ void TypePrinter::printPackExpansionAfter(const PackExpansionType *T,
static void printCountAttributedImpl(const CountAttributedType *T,
raw_ostream &OS,
const PrintingPolicy &Policy) {
OS << ' ';
if (T->isCountInBytes() && T->isOrNull())
OS << " __sized_by_or_null(";
OS << "__sized_by_or_null(";
else if (T->isCountInBytes())
OS << " __sized_by(";
OS << "__sized_by(";
else if (T->isOrNull())
OS << " __counted_by_or_null(";
OS << "__counted_by_or_null(";
else
OS << " __counted_by(";
OS << "__counted_by(";
if (T->getCountExpr())
T->getCountExpr()->printPretty(OS, nullptr, Policy);
OS << ')';
Expand All @@ -1762,14 +1763,14 @@ static void printCountAttributedImpl(const CountAttributedType *T,
void TypePrinter::printCountAttributedBefore(const CountAttributedType *T,
raw_ostream &OS) {
printBefore(T->desugar(), OS);
if (!T->desugar()->isArrayType())
if (!T->isArrayType())
printCountAttributedImpl(T, OS, Policy);
}

void TypePrinter::printCountAttributedAfter(const CountAttributedType *T,
raw_ostream &OS) {
printAfter(T->desugar(), OS);
if (T->desugar()->isArrayType())
if (T->isArrayType())
printCountAttributedImpl(T, OS, Policy);
}

Expand Down
4 changes: 2 additions & 2 deletions clang/lib/CodeGen/BackendUtil.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,8 @@ using namespace llvm;
namespace llvm {
extern cl::opt<bool> PrintPipelinePasses;

cl::opt<bool> ClRemoveTraps("clang-remove-traps", cl::Optional,
cl::desc("Insert remove-traps pass."));
static cl::opt<bool> ClRemoveTraps("clang-remove-traps", cl::Optional,
cl::desc("Insert remove-traps pass."));

// Experiment to move sanitizers earlier.
static cl::opt<bool> ClSanitizeOnOptimizerEarlyEP(
Expand Down
10 changes: 0 additions & 10 deletions clang/lib/CodeGen/CGBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7281,8 +7281,6 @@ static const std::pair<unsigned, unsigned> NEONEquivalentIntrinsicMap[] = {
{ NEON::BI__builtin_neon_vabdq_f16, NEON::BI__builtin_neon_vabdq_v, },
{ NEON::BI__builtin_neon_vabs_f16, NEON::BI__builtin_neon_vabs_v, },
{ NEON::BI__builtin_neon_vabsq_f16, NEON::BI__builtin_neon_vabsq_v, },
{ NEON::BI__builtin_neon_vbsl_f16, NEON::BI__builtin_neon_vbsl_v, },
{ NEON::BI__builtin_neon_vbslq_f16, NEON::BI__builtin_neon_vbslq_v, },
{ NEON::BI__builtin_neon_vcage_f16, NEON::BI__builtin_neon_vcage_v, },
{ NEON::BI__builtin_neon_vcageq_f16, NEON::BI__builtin_neon_vcageq_v, },
{ NEON::BI__builtin_neon_vcagt_f16, NEON::BI__builtin_neon_vcagt_v, },
Expand All @@ -7301,8 +7299,6 @@ static const std::pair<unsigned, unsigned> NEONEquivalentIntrinsicMap[] = {
{ NEON::BI__builtin_neon_vclezq_f16, NEON::BI__builtin_neon_vclezq_v, },
{ NEON::BI__builtin_neon_vcltz_f16, NEON::BI__builtin_neon_vcltz_v, },
{ NEON::BI__builtin_neon_vcltzq_f16, NEON::BI__builtin_neon_vcltzq_v, },
{ NEON::BI__builtin_neon_vext_f16, NEON::BI__builtin_neon_vext_v, },
{ NEON::BI__builtin_neon_vextq_f16, NEON::BI__builtin_neon_vextq_v, },
{ NEON::BI__builtin_neon_vfma_f16, NEON::BI__builtin_neon_vfma_v, },
{ NEON::BI__builtin_neon_vfma_lane_f16, NEON::BI__builtin_neon_vfma_lane_v, },
{ NEON::BI__builtin_neon_vfma_laneq_f16, NEON::BI__builtin_neon_vfma_laneq_v, },
Expand Down Expand Up @@ -7405,12 +7401,6 @@ static const std::pair<unsigned, unsigned> NEONEquivalentIntrinsicMap[] = {
{ NEON::BI__builtin_neon_vst4_lane_bf16, NEON::BI__builtin_neon_vst4_lane_v },
{ NEON::BI__builtin_neon_vst4q_bf16, NEON::BI__builtin_neon_vst4q_v },
{ NEON::BI__builtin_neon_vst4q_lane_bf16, NEON::BI__builtin_neon_vst4q_lane_v },
{ NEON::BI__builtin_neon_vtrn_f16, NEON::BI__builtin_neon_vtrn_v, },
{ NEON::BI__builtin_neon_vtrnq_f16, NEON::BI__builtin_neon_vtrnq_v, },
{ NEON::BI__builtin_neon_vuzp_f16, NEON::BI__builtin_neon_vuzp_v, },
{ NEON::BI__builtin_neon_vuzpq_f16, NEON::BI__builtin_neon_vuzpq_v, },
{ NEON::BI__builtin_neon_vzip_f16, NEON::BI__builtin_neon_vzip_v, },
{ NEON::BI__builtin_neon_vzipq_f16, NEON::BI__builtin_neon_vzipq_v, },
// The mangling rules cause us to have one ID for each type for vldap1(q)_lane
// and vstl1(q)_lane, but codegen is equivalent for all of them. Choose an
// arbitrary one to be handled as tha canonical variation.
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/CodeGen/Targets/X86.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1069,6 +1069,12 @@ Address X86_32ABIInfo::EmitVAArg(CodeGenFunction &CGF,

auto TypeInfo = getContext().getTypeInfoInChars(Ty);

CCState State(*const_cast<CGFunctionInfo *>(CGF.CurFnInfo));
ABIArgInfo AI = classifyArgumentType(Ty, State, /*ArgIndex*/ 0);
// Empty records are ignored for parameter passing purposes.
if (AI.isIgnore())
return CGF.CreateMemTemp(Ty);

// x86-32 changes the alignment of certain arguments on the stack.
//
// Just messing with TypeInfo like this works because we never pass
Expand Down
7 changes: 7 additions & 0 deletions clang/lib/Driver/Driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
#include "ToolChains/WebAssembly.h"
#include "ToolChains/XCore.h"
#include "ToolChains/ZOS.h"
#include "clang/Basic/DiagnosticDriver.h"
#include "clang/Basic/TargetID.h"
#include "clang/Basic/Version.h"
#include "clang/Config/config.h"
Expand Down Expand Up @@ -5889,6 +5890,12 @@ const char *Driver::GetNamedOutputPath(Compilation &C, const JobAction &JA,
&JA);
}

if (JA.getType() == types::TY_API_INFO &&
C.getArgs().hasArg(options::OPT_emit_extension_symbol_graphs) &&
C.getArgs().hasArg(options::OPT_o))
Diag(clang::diag::err_drv_unexpected_symbol_graph_output)
<< C.getArgs().getLastArgValue(options::OPT_o);

// DXC defaults to standard out when generating assembly. We check this after
// any DXC flags that might specify a file.
if (AtTopLevel && JA.getType() == types::TY_PP_Asm && IsDXCMode())
Expand Down
30 changes: 27 additions & 3 deletions clang/lib/Driver/ToolChains/Clang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4045,9 +4045,18 @@ static bool RenderModulesOptions(Compilation &C, const Driver &D,
// module fragment.
CmdArgs.push_back("-fskip-odr-check-in-gmf");

// Claim `-fmodule-output` and `-fmodule-output=` to avoid unused warnings.
Args.ClaimAllArgs(options::OPT_fmodule_output);
Args.ClaimAllArgs(options::OPT_fmodule_output_EQ);
// We need to include the case the input file is a module file here.
// Since the default compilation model for C++ module interface unit will
// create temporary module file and compile the temporary module file
// to get the object file. Then the `-fmodule-output` flag will be
// brought to the second compilation process. So we have to claim it for
// the case too.
if (Input.getType() == driver::types::TY_CXXModule ||
Input.getType() == driver::types::TY_PP_CXXModule ||
Input.getType() == driver::types::TY_ModuleFile) {
Args.ClaimAllArgs(options::OPT_fmodule_output);
Args.ClaimAllArgs(options::OPT_fmodule_output_EQ);
}

return HaveModules;
}
Expand Down Expand Up @@ -5037,11 +5046,26 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
assert(JA.getType() == types::TY_API_INFO &&
"Extract API actions must generate a API information.");
CmdArgs.push_back("-extract-api");

if (Arg *PrettySGFArg = Args.getLastArg(options::OPT_emit_pretty_sgf))
PrettySGFArg->render(Args, CmdArgs);

Arg *SymbolGraphDirArg = Args.getLastArg(options::OPT_symbol_graph_dir_EQ);

if (Arg *ProductNameArg = Args.getLastArg(options::OPT_product_name_EQ))
ProductNameArg->render(Args, CmdArgs);
if (Arg *ExtractAPIIgnoresFileArg =
Args.getLastArg(options::OPT_extract_api_ignores_EQ))
ExtractAPIIgnoresFileArg->render(Args, CmdArgs);
if (Arg *EmitExtensionSymbolGraphs =
Args.getLastArg(options::OPT_emit_extension_symbol_graphs)) {
if (!SymbolGraphDirArg)
D.Diag(diag::err_drv_missing_symbol_graph_dir);

EmitExtensionSymbolGraphs->render(Args, CmdArgs);
}
if (SymbolGraphDirArg)
SymbolGraphDirArg->render(Args, CmdArgs);
} else {
assert((isa<CompileJobAction>(JA) || isa<BackendJobAction>(JA)) &&
"Invalid action for clang tool.");
Expand Down
544 changes: 53 additions & 491 deletions clang/lib/ExtractAPI/API.cpp

Large diffs are not rendered by default.

71 changes: 46 additions & 25 deletions clang/lib/ExtractAPI/DeclarationFragments.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,23 +57,44 @@ void findTypeLocForBlockDecl(const clang::TypeSourceInfo *TSInfo,

} // namespace

DeclarationFragments &DeclarationFragments::appendSpace() {
DeclarationFragments &
DeclarationFragments::appendUnduplicatedTextCharacter(char Character) {
if (!Fragments.empty()) {
Fragment &Last = Fragments.back();
if (Last.Kind == FragmentKind::Text) {
// Merge the extra space into the last fragment if the last fragment is
// also text.
if (Last.Spelling.back() != ' ') { // avoid extra trailing spaces.
Last.Spelling.push_back(' ');
if (Last.Spelling.back() != Character) { // avoid duplicates at end
Last.Spelling.push_back(Character);
}
} else {
append(" ", FragmentKind::Text);
append("", FragmentKind::Text);
Fragments.back().Spelling.push_back(Character);
}
}

return *this;
}

DeclarationFragments &DeclarationFragments::appendSpace() {
return appendUnduplicatedTextCharacter(' ');
}

DeclarationFragments &DeclarationFragments::appendSemicolon() {
return appendUnduplicatedTextCharacter(';');
}

DeclarationFragments &DeclarationFragments::removeTrailingSemicolon() {
if (Fragments.empty())
return *this;

Fragment &Last = Fragments.back();
if (Last.Kind == FragmentKind::Text && Last.Spelling.back() == ';')
Last.Spelling.pop_back();

return *this;
}

StringRef DeclarationFragments::getFragmentKindString(
DeclarationFragments::FragmentKind Kind) {
switch (Kind) {
Expand Down Expand Up @@ -466,7 +487,7 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForNamespace(
if (!Decl->isAnonymousNamespace())
Fragments.appendSpace().append(
Decl->getName(), DeclarationFragments::FragmentKind::Identifier);
return Fragments.append(";", DeclarationFragments::FragmentKind::Text);
return Fragments.appendSemicolon();
}

DeclarationFragments
Expand Down Expand Up @@ -508,7 +529,7 @@ DeclarationFragmentsBuilder::getFragmentsForVar(const VarDecl *Var) {
return Fragments
.append(Var->getName(), DeclarationFragments::FragmentKind::Identifier)
.append(std::move(After))
.append(";", DeclarationFragments::FragmentKind::Text);
.appendSemicolon();
}

DeclarationFragments
Expand Down Expand Up @@ -538,7 +559,7 @@ DeclarationFragmentsBuilder::getFragmentsForVarTemplate(const VarDecl *Var) {
Fragments.append(std::move(ArgumentFragment))
.appendSpace()
.append(Var->getName(), DeclarationFragments::FragmentKind::Identifier)
.append(";", DeclarationFragments::FragmentKind::Text);
.appendSemicolon();
return Fragments;
}

Expand Down Expand Up @@ -698,7 +719,7 @@ DeclarationFragmentsBuilder::getFragmentsForFunction(const FunctionDecl *Func) {
Fragments.append(DeclarationFragments::getExceptionSpecificationString(
Func->getExceptionSpecType()));

return Fragments.append(";", DeclarationFragments::FragmentKind::Text);
return Fragments.appendSemicolon();
}

DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForEnumConstant(
Expand Down Expand Up @@ -727,7 +748,7 @@ DeclarationFragmentsBuilder::getFragmentsForEnum(const EnumDecl *EnumDecl) {
getFragmentsForType(IntegerType, EnumDecl->getASTContext(), After))
.append(std::move(After));

return Fragments.append(";", DeclarationFragments::FragmentKind::Text);
return Fragments.appendSemicolon();
}

DeclarationFragments
Expand All @@ -743,7 +764,7 @@ DeclarationFragmentsBuilder::getFragmentsForField(const FieldDecl *Field) {
.appendSpace()
.append(Field->getName(), DeclarationFragments::FragmentKind::Identifier)
.append(std::move(After))
.append(";", DeclarationFragments::FragmentKind::Text);
.appendSemicolon();
}

DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForRecordDecl(
Expand All @@ -761,7 +782,7 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForRecordDecl(
Fragments.appendSpace().append(
Record->getName(), DeclarationFragments::FragmentKind::Identifier);

return Fragments.append(";", DeclarationFragments::FragmentKind::Text);
return Fragments.appendSemicolon();
}

DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForCXXClass(
Expand All @@ -776,7 +797,7 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForCXXClass(
Fragments.appendSpace().append(
Record->getName(), DeclarationFragments::FragmentKind::Identifier);

return Fragments.append(";", DeclarationFragments::FragmentKind::Text);
return Fragments.appendSemicolon();
}

DeclarationFragments
Expand Down Expand Up @@ -806,7 +827,7 @@ DeclarationFragmentsBuilder::getFragmentsForSpecialCXXMethod(
Fragments.append(DeclarationFragments::getExceptionSpecificationString(
Method->getExceptionSpecType()));

return Fragments.append(";", DeclarationFragments::FragmentKind::Text);
return Fragments.appendSemicolon();
}

DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForCXXMethod(
Expand Down Expand Up @@ -846,7 +867,7 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForCXXMethod(
Fragments.append(DeclarationFragments::getExceptionSpecificationString(
Method->getExceptionSpecType()));

return Fragments.append(";", DeclarationFragments::FragmentKind::Text);
return Fragments.appendSemicolon();
}

DeclarationFragments
Expand Down Expand Up @@ -877,7 +898,7 @@ DeclarationFragmentsBuilder::getFragmentsForConversionFunction(
Fragments.appendSpace().append("const",
DeclarationFragments::FragmentKind::Keyword);

return Fragments.append(";", DeclarationFragments::FragmentKind::Text);
return Fragments.appendSemicolon();
}

DeclarationFragments
Expand Down Expand Up @@ -909,7 +930,7 @@ DeclarationFragmentsBuilder::getFragmentsForOverloadedOperator(
Fragments.append(DeclarationFragments::getExceptionSpecificationString(
Method->getExceptionSpecType()));

return Fragments.append(";", DeclarationFragments::FragmentKind::Text);
return Fragments.appendSemicolon();
}

// Get fragments for template parameters, e.g. T in tempalte<typename T> ...
Expand Down Expand Up @@ -997,7 +1018,7 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForConcept(
.appendSpace()
.append(Concept->getName().str(),
DeclarationFragments::FragmentKind::Identifier)
.append(";", DeclarationFragments::FragmentKind::Text);
.appendSemicolon();
}

DeclarationFragments
Expand Down Expand Up @@ -1038,7 +1059,7 @@ DeclarationFragmentsBuilder::getFragmentsForClassTemplateSpecialization(
getFragmentsForTemplateArguments(Decl->getTemplateArgs().asArray(),
Decl->getASTContext(), std::nullopt))
.append(">", DeclarationFragments::FragmentKind::Text)
.append(";", DeclarationFragments::FragmentKind::Text);
.appendSemicolon();
}

DeclarationFragments
Expand All @@ -1060,7 +1081,7 @@ DeclarationFragmentsBuilder::getFragmentsForClassTemplatePartialSpecialization(
Decl->getTemplateArgs().asArray(), Decl->getASTContext(),
Decl->getTemplateArgsAsWritten()->arguments()))
.append(">", DeclarationFragments::FragmentKind::Text)
.append(";", DeclarationFragments::FragmentKind::Text);
.appendSemicolon();
}

DeclarationFragments
Expand All @@ -1079,7 +1100,7 @@ DeclarationFragmentsBuilder::getFragmentsForVarTemplateSpecialization(
getFragmentsForTemplateArguments(Decl->getTemplateArgs().asArray(),
Decl->getASTContext(), std::nullopt))
.append(">", DeclarationFragments::FragmentKind::Text)
.append(";", DeclarationFragments::FragmentKind::Text);
.appendSemicolon();
}

DeclarationFragments
Expand All @@ -1101,7 +1122,7 @@ DeclarationFragmentsBuilder::getFragmentsForVarTemplatePartialSpecialization(
Decl->getTemplateArgs().asArray(), Decl->getASTContext(),
Decl->getTemplateArgsAsWritten()->arguments()))
.append(">", DeclarationFragments::FragmentKind::Text)
.append(";", DeclarationFragments::FragmentKind::Text);
.appendSemicolon();
}

DeclarationFragments
Expand Down Expand Up @@ -1172,7 +1193,7 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForObjCCategory(

Fragments.append("@interface", DeclarationFragments::FragmentKind::Keyword)
.appendSpace()
.append(Category->getClassInterface()->getName(),
.append(Interface->getName(),
DeclarationFragments::FragmentKind::TypeIdentifier, InterfaceUSR,
Interface)
.append(" (", DeclarationFragments::FragmentKind::Text)
Expand Down Expand Up @@ -1246,7 +1267,7 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForObjCMethod(
Fragments.append(getFragmentsForParam(Param));
}

return Fragments.append(";", DeclarationFragments::FragmentKind::Text);
return Fragments.appendSemicolon();
}

DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForObjCProperty(
Expand Down Expand Up @@ -1347,7 +1368,7 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForObjCProperty(
.append(Property->getName(),
DeclarationFragments::FragmentKind::Identifier)
.append(std::move(After))
.append(";", DeclarationFragments::FragmentKind::Text);
.appendSemicolon();
}

DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForObjCProtocol(
Expand Down Expand Up @@ -1391,7 +1412,7 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForTypedef(
.appendSpace()
.append(Decl->getName(), DeclarationFragments::FragmentKind::Identifier);

return Fragments.append(";", DeclarationFragments::FragmentKind::Text);
return Fragments.appendSemicolon();
}

// Instantiate template for FunctionDecl.
Expand Down
112 changes: 53 additions & 59 deletions clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendOptions.h"
#include "clang/Frontend/MultiplexConsumer.h"
#include "clang/Index/USRGeneration.h"
#include "clang/InstallAPI/HeaderFile.h"
#include "clang/Lex/MacroInfo.h"
#include "clang/Lex/PPCallbacks.h"
Expand All @@ -39,6 +40,7 @@
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/FileSystem.h"
Expand Down Expand Up @@ -327,11 +329,12 @@ class MacroCallback : public PPCallbacks {

StringRef Name = PM.MacroNameToken.getIdentifierInfo()->getName();
PresumedLoc Loc = SM.getPresumedLoc(PM.MacroNameToken.getLocation());
StringRef USR =
API.recordUSRForMacro(Name, PM.MacroNameToken.getLocation(), SM);
SmallString<128> USR;
index::generateUSRForMacro(Name, PM.MacroNameToken.getLocation(), SM,
USR);

API.addMacroDefinition(
Name, USR, Loc,
API.createRecord<extractapi::MacroDefinitionRecord>(
USR, Name, SymbolReference(), Loc,
DeclarationFragmentsBuilder::getFragmentsForMacro(Name, PM.MD),
DeclarationFragmentsBuilder::getSubHeadingForMacro(Name),
SM.isInSystemHeader(PM.MacroNameToken.getLocation()));
Expand Down Expand Up @@ -372,40 +375,57 @@ class APIMacroCallback : public MacroCallback {
LocationFileChecker &LCF;
};

std::unique_ptr<llvm::raw_pwrite_stream>
createAdditionalSymbolGraphFile(CompilerInstance &CI, Twine BaseName) {
auto OutputDirectory = CI.getFrontendOpts().SymbolGraphOutputDir;

SmallString<256> FileName;
llvm::sys::path::append(FileName, OutputDirectory,
BaseName + ".symbols.json");
return CI.createOutputFile(
FileName, /*Binary*/ false, /*RemoveFileOnSignal*/ false,
/*UseTemporary*/ true, /*CreateMissingDirectories*/ true);
}

} // namespace

void ExtractAPIActionBase::ImplEndSourceFileAction() {
if (!OS)
return;
void ExtractAPIActionBase::ImplEndSourceFileAction(CompilerInstance &CI) {
SymbolGraphSerializerOption SerializationOptions;
SerializationOptions.Compact = !CI.getFrontendOpts().EmitPrettySymbolGraphs;
SerializationOptions.EmitSymbolLabelsForTesting =
CI.getFrontendOpts().EmitSymbolGraphSymbolLabelsForTesting;

if (CI.getFrontendOpts().EmitExtensionSymbolGraphs) {
auto ConstructOutputFile = [&CI](Twine BaseName) {
return createAdditionalSymbolGraphFile(CI, BaseName);
};

SymbolGraphSerializer::serializeWithExtensionGraphs(
*OS, *API, IgnoresList, ConstructOutputFile, SerializationOptions);
} else {
SymbolGraphSerializer::serializeMainSymbolGraph(*OS, *API, IgnoresList,
SerializationOptions);
}

// Setup a SymbolGraphSerializer to write out collected API information in
// the Symbol Graph format.
// FIXME: Make the kind of APISerializer configurable.
SymbolGraphSerializer SGSerializer(*API, IgnoresList);
SGSerializer.serialize(*OS);
// Flush the stream and close the main output stream.
OS.reset();
}

std::unique_ptr<raw_pwrite_stream>
ExtractAPIAction::CreateOutputFile(CompilerInstance &CI, StringRef InFile) {
std::unique_ptr<raw_pwrite_stream> OS;
OS = CI.createDefaultOutputFile(/*Binary=*/false, InFile,
/*Extension=*/"json",
/*RemoveFileOnSignal=*/false);
if (!OS)
return nullptr;
return OS;
}

std::unique_ptr<ASTConsumer>
ExtractAPIAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
OS = CreateOutputFile(CI, InFile);
auto ProductName = CI.getFrontendOpts().ProductName;

if (CI.getFrontendOpts().SymbolGraphOutputDir.empty())
OS = CI.createDefaultOutputFile(/*Binary*/ false, InFile,
/*Extension*/ "symbols.json",
/*RemoveFileOnSignal*/ false,
/*CreateMissingDirectories*/ true);
else
OS = createAdditionalSymbolGraphFile(CI, ProductName);

if (!OS)
return nullptr;

auto ProductName = CI.getFrontendOpts().ProductName;

// Now that we have enough information about the language options and the
// target triple, let's create the APISet before anyone uses it.
API = std::make_unique<APISet>(
Expand Down Expand Up @@ -495,7 +515,9 @@ bool ExtractAPIAction::PrepareToExecuteAction(CompilerInstance &CI) {
return true;
}

void ExtractAPIAction::EndSourceFileAction() { ImplEndSourceFileAction(); }
void ExtractAPIAction::EndSourceFileAction() {
ImplEndSourceFileAction(getCompilerInstance());
}

std::unique_ptr<ASTConsumer>
WrappingExtractAPIAction::CreateASTConsumer(CompilerInstance &CI,
Expand All @@ -506,11 +528,9 @@ WrappingExtractAPIAction::CreateASTConsumer(CompilerInstance &CI,

CreatedASTConsumer = true;

OS = CreateOutputFile(CI, InFile);
if (!OS)
return nullptr;

auto ProductName = CI.getFrontendOpts().ProductName;
ProductName = CI.getFrontendOpts().ProductName;
auto InputFilename = llvm::sys::path::filename(InFile);
OS = createAdditionalSymbolGraphFile(CI, InputFilename);

// Now that we have enough information about the language options and the
// target triple, let's create the APISet before anyone uses it.
Expand Down Expand Up @@ -552,32 +572,6 @@ void WrappingExtractAPIAction::EndSourceFileAction() {
WrapperFrontendAction::EndSourceFileAction();

if (CreatedASTConsumer) {
ImplEndSourceFileAction();
ImplEndSourceFileAction(getCompilerInstance());
}
}

std::unique_ptr<raw_pwrite_stream>
WrappingExtractAPIAction::CreateOutputFile(CompilerInstance &CI,
StringRef InFile) {
std::unique_ptr<raw_pwrite_stream> OS;
std::string OutputDir = CI.getFrontendOpts().SymbolGraphOutputDir;

// The symbol graphs need to be generated as a side effect of regular
// compilation so the output should be dumped in the directory provided with
// the command line option.
llvm::SmallString<128> OutFilePath(OutputDir);
auto Seperator = llvm::sys::path::get_separator();
auto Infilename = llvm::sys::path::filename(InFile);
OutFilePath.append({Seperator, Infilename});
llvm::sys::path::replace_extension(OutFilePath, "json");
// StringRef outputFilePathref = *OutFilePath;

// don't use the default output file
OS = CI.createOutputFile(/*OutputPath=*/OutFilePath, /*Binary=*/false,
/*RemoveFileOnSignal=*/true,
/*UseTemporary=*/true,
/*CreateMissingDirectories=*/true);
if (!OS)
return nullptr;
return OS;
}
943 changes: 379 additions & 564 deletions clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp

Large diffs are not rendered by default.

6 changes: 5 additions & 1 deletion clang/lib/ExtractAPI/TypedefUnderlyingTypeResolver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
//===----------------------------------------------------------------------===//

#include "clang/ExtractAPI/TypedefUnderlyingTypeResolver.h"
#include "clang/Basic/Module.h"
#include "clang/Index/USRGeneration.h"

using namespace clang;
Expand Down Expand Up @@ -50,17 +51,20 @@ TypedefUnderlyingTypeResolver::getSymbolReferenceForType(QualType Type,
SmallString<128> TypeUSR;
const NamedDecl *TypeDecl = getUnderlyingTypeDecl(Type);
const TypedefType *TypedefTy = Type->getAs<TypedefType>();
StringRef OwningModuleName;

if (TypeDecl) {
if (!TypedefTy)
TypeName = TypeDecl->getName().str();

clang::index::generateUSRForDecl(TypeDecl, TypeUSR);
if (auto *OwningModule = TypeDecl->getImportedOwningModule())
OwningModuleName = OwningModule->Name;
} else {
clang::index::generateUSRForType(Type, Context, TypeUSR);
}

return {API.copyString(TypeName), API.copyString(TypeUSR)};
return API.createSymbolReference(TypeName, TypeUSR, OwningModuleName);
}

std::string TypedefUnderlyingTypeResolver::getUSRForType(QualType Type) const {
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Format/Format.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3581,7 +3581,7 @@ cleanupAroundReplacements(StringRef Code, const tooling::Replacements &Replaces,
// We need to use lambda function here since there are two versions of
// `cleanup`.
auto Cleanup = [](const FormatStyle &Style, StringRef Code,
std::vector<tooling::Range> Ranges,
ArrayRef<tooling::Range> Ranges,
StringRef FileName) -> tooling::Replacements {
return cleanup(Style, Code, Ranges, FileName);
};
Expand Down
3 changes: 1 addition & 2 deletions clang/lib/Frontend/PrecompiledPreamble.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -290,8 +290,7 @@ class PrecompilePreambleAction : public ASTFrontendAction {

class PrecompilePreambleConsumer : public PCHGenerator {
public:
PrecompilePreambleConsumer(PrecompilePreambleAction &Action,
const Preprocessor &PP,
PrecompilePreambleConsumer(PrecompilePreambleAction &Action, Preprocessor &PP,
InMemoryModuleCache &ModuleCache,
StringRef isysroot,
std::shared_ptr<PCHBuffer> Buffer)
Expand Down
10 changes: 7 additions & 3 deletions clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -181,9 +181,13 @@ CreateFrontendAction(CompilerInstance &CI) {
#endif

// Wrap the base FE action in an extract api action to generate
// symbol graph as a biproduct of compilation ( enabled with
// --emit-symbol-graph option )
if (!FEOpts.SymbolGraphOutputDir.empty()) {
// symbol graph as a biproduct of compilation (enabled with
// --emit-symbol-graph option)
if (FEOpts.EmitSymbolGraph) {
if (FEOpts.SymbolGraphOutputDir.empty()) {
CI.getDiagnostics().Report(diag::warn_missing_symbol_graph_dir);
CI.getFrontendOpts().SymbolGraphOutputDir = ".";
}
CI.getCodeGenOpts().ClearASTBeforeBackend = false;
Act = std::make_unique<WrappingExtractAPIAction>(std::move(Act));
}
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Sema/SemaExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2751,7 +2751,7 @@ Sema::ActOnIdExpression(Scope *S, CXXScopeSpec &SS,
QualType type = VD->getType().getNonReferenceType();
// This will eventually be translated into MemberExpr upon
// the use of instantiated struct fields.
return BuildDeclRefExpr(VD, type, VK_PRValue, NameLoc);
return BuildDeclRefExpr(VD, type, VK_LValue, NameLoc);
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Sema/SemaStmtAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -406,8 +406,8 @@ static void CheckForDuplicateLoopAttrs(Sema &S, ArrayRef<const Attr *> Attrs) {
<< *FirstItr;
S.Diag((*FirstItr)->getLocation(), diag::note_previous_attribute);
}
return;
}
return;
}

static Attr *handleMSConstexprAttr(Sema &S, Stmt *St, const ParsedAttr &A,
Expand Down
10 changes: 5 additions & 5 deletions clang/lib/Serialization/ASTReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6622,17 +6622,17 @@ void ASTReader::ReadPragmaDiagnosticMappings(DiagnosticsEngine &Diag) {
while (NumLocations--) {
assert(Idx < Record.size() &&
"Invalid data, missing pragma diagnostic states");
SourceLocation Loc = ReadSourceLocation(F, Record[Idx++]);
auto IDAndOffset = SourceMgr.getDecomposedLoc(Loc);
assert(IDAndOffset.first.isValid() && "invalid FileID for transition");
assert(IDAndOffset.second == 0 && "not a start location for a FileID");
FileID FID = ReadFileID(F, Record, Idx);
assert(FID.isValid() && "invalid FileID for transition");
// FIXME: Remove this once we don't need the side-effects.
(void)SourceMgr.getSLocEntryOrNull(FID);
unsigned Transitions = Record[Idx++];

// Note that we don't need to set up Parent/ParentOffset here, because
// we won't be changing the diagnostic state within imported FileIDs
// (other than perhaps appending to the main source file, which has no
// parent).
auto &F = Diag.DiagStatesByLoc.Files[IDAndOffset.first];
auto &F = Diag.DiagStatesByLoc.Files[FID];
F.StateTransitions.reserve(F.StateTransitions.size() + Transitions);
for (unsigned I = 0; I != Transitions; ++I) {
unsigned Offset = Record[Idx++];
Expand Down
138 changes: 68 additions & 70 deletions clang/lib/Serialization/ASTWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3131,9 +3131,7 @@ void ASTWriter::WritePragmaDiagnosticMappings(const DiagnosticsEngine &Diag,
continue;
++NumLocations;

SourceLocation Loc = Diag.SourceMgr->getComposedLoc(FileIDAndFile.first, 0);
assert(!Loc.isInvalid() && "start loc for valid FileID is invalid");
AddSourceLocation(Loc, Record);
AddFileID(FileIDAndFile.first, Record);

Record.push_back(FileIDAndFile.second.StateTransitions.size());
for (auto &StatePoint : FileIDAndFile.second.StateTransitions) {
Expand Down Expand Up @@ -5109,69 +5107,7 @@ ASTFileSignature ASTWriter::WriteASTCore(Sema &SemaRef, StringRef isysroot,
for (auto *D : SemaRef.DeclsToCheckForDeferredDiags)
DeclsToCheckForDeferredDiags.push_back(GetDeclRef(D));

{
auto Abv = std::make_shared<BitCodeAbbrev>();
Abv->Add(llvm::BitCodeAbbrevOp(UPDATE_VISIBLE));
Abv->Add(llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::VBR, 6));
Abv->Add(llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Blob));
UpdateVisibleAbbrev = Stream.EmitAbbrev(std::move(Abv));
}

RecordData DeclUpdatesOffsetsRecord;

// Keep writing types, declarations, and declaration update records
// until we've emitted all of them.
Stream.EnterSubblock(DECLTYPES_BLOCK_ID, /*bits for abbreviations*/5);
DeclTypesBlockStartOffset = Stream.GetCurrentBitNo();
WriteTypeAbbrevs();
WriteDeclAbbrevs();
do {
WriteDeclUpdatesBlocks(DeclUpdatesOffsetsRecord);
while (!DeclTypesToEmit.empty()) {
DeclOrType DOT = DeclTypesToEmit.front();
DeclTypesToEmit.pop();
if (DOT.isType())
WriteType(DOT.getType());
else
WriteDecl(Context, DOT.getDecl());
}
} while (!DeclUpdates.empty());
Stream.ExitBlock();

DoneWritingDeclsAndTypes = true;

// These things can only be done once we've written out decls and types.
WriteTypeDeclOffsets();
if (!DeclUpdatesOffsetsRecord.empty())
Stream.EmitRecord(DECL_UPDATE_OFFSETS, DeclUpdatesOffsetsRecord);

// Create a lexical update block containing all of the declarations in the
// translation unit that do not come from other AST files.
{
SmallVector<uint32_t, 128> NewGlobalKindDeclPairs;
for (const auto *D : TU->noload_decls()) {
if (!D->isFromASTFile()) {
NewGlobalKindDeclPairs.push_back(D->getKind());
NewGlobalKindDeclPairs.push_back(GetDeclRef(D));
}
}

auto Abv = std::make_shared<BitCodeAbbrev>();
Abv->Add(llvm::BitCodeAbbrevOp(TU_UPDATE_LEXICAL));
Abv->Add(llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Blob));
unsigned TuUpdateLexicalAbbrev = Stream.EmitAbbrev(std::move(Abv));

RecordData::value_type Record[] = {TU_UPDATE_LEXICAL};
Stream.EmitRecordWithBlob(TuUpdateLexicalAbbrev, Record,
bytes(NewGlobalKindDeclPairs));
}

// And a visible updates block for the translation unit.
WriteDeclContextVisibleUpdate(TU);

// If we have any extern "C" names, write out a visible update for them.
if (Context.ExternCContext)
WriteDeclContextVisibleUpdate(Context.ExternCContext);
WriteDeclAndTypes(Context);

WriteFileDeclIDsMap();
WriteSourceManagerBlock(Context.getSourceManager(), PP);
Expand Down Expand Up @@ -5257,10 +5193,6 @@ ASTFileSignature ASTWriter::WriteASTCore(Sema &SemaRef, StringRef isysroot,
if (!DeleteExprsToAnalyze.empty())
Stream.EmitRecord(DELETE_EXPRS_TO_ANALYZE, DeleteExprsToAnalyze);

// Write the visible updates to DeclContexts.
for (auto *DC : UpdatedDeclContexts)
WriteDeclContextVisibleUpdate(DC);

if (!WritingModule) {
// Write the submodules that were imported, if any.
struct ModuleInfo {
Expand Down Expand Up @@ -5325,6 +5257,72 @@ ASTFileSignature ASTWriter::WriteASTCore(Sema &SemaRef, StringRef isysroot,
return backpatchSignature();
}

void ASTWriter::WriteDeclAndTypes(ASTContext &Context) {
// Keep writing types, declarations, and declaration update records
// until we've emitted all of them.
RecordData DeclUpdatesOffsetsRecord;
Stream.EnterSubblock(DECLTYPES_BLOCK_ID, /*bits for abbreviations*/5);
DeclTypesBlockStartOffset = Stream.GetCurrentBitNo();
WriteTypeAbbrevs();
WriteDeclAbbrevs();
do {
WriteDeclUpdatesBlocks(DeclUpdatesOffsetsRecord);
while (!DeclTypesToEmit.empty()) {
DeclOrType DOT = DeclTypesToEmit.front();
DeclTypesToEmit.pop();
if (DOT.isType())
WriteType(DOT.getType());
else
WriteDecl(Context, DOT.getDecl());
}
} while (!DeclUpdates.empty());
Stream.ExitBlock();

DoneWritingDeclsAndTypes = true;

// These things can only be done once we've written out decls and types.
WriteTypeDeclOffsets();
if (!DeclUpdatesOffsetsRecord.empty())
Stream.EmitRecord(DECL_UPDATE_OFFSETS, DeclUpdatesOffsetsRecord);

const TranslationUnitDecl *TU = Context.getTranslationUnitDecl();
// Create a lexical update block containing all of the declarations in the
// translation unit that do not come from other AST files.
SmallVector<uint32_t, 128> NewGlobalKindDeclPairs;
for (const auto *D : TU->noload_decls()) {
if (!D->isFromASTFile()) {
NewGlobalKindDeclPairs.push_back(D->getKind());
NewGlobalKindDeclPairs.push_back(GetDeclRef(D));
}
}

auto Abv = std::make_shared<llvm::BitCodeAbbrev>();
Abv->Add(llvm::BitCodeAbbrevOp(TU_UPDATE_LEXICAL));
Abv->Add(llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Blob));
unsigned TuUpdateLexicalAbbrev = Stream.EmitAbbrev(std::move(Abv));

RecordData::value_type Record[] = {TU_UPDATE_LEXICAL};
Stream.EmitRecordWithBlob(TuUpdateLexicalAbbrev, Record,
bytes(NewGlobalKindDeclPairs));

Abv = std::make_shared<llvm::BitCodeAbbrev>();
Abv->Add(llvm::BitCodeAbbrevOp(UPDATE_VISIBLE));
Abv->Add(llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::VBR, 6));
Abv->Add(llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Blob));
UpdateVisibleAbbrev = Stream.EmitAbbrev(std::move(Abv));

// And a visible updates block for the translation unit.
WriteDeclContextVisibleUpdate(TU);

// If we have any extern "C" names, write out a visible update for them.
if (Context.ExternCContext)
WriteDeclContextVisibleUpdate(Context.ExternCContext);

// Write the visible updates to DeclContexts.
for (auto *DC : UpdatedDeclContexts)
WriteDeclContextVisibleUpdate(DC);
}

void ASTWriter::WriteDeclUpdatesBlocks(RecordDataImpl &OffsetsRecord) {
if (DeclUpdates.empty())
return;
Expand Down
21 changes: 17 additions & 4 deletions clang/lib/Serialization/GeneratePCH.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "clang/AST/ASTContext.h"
#include "clang/Frontend/FrontendDiagnostic.h"
#include "clang/Lex/HeaderSearch.h"
#include "clang/Lex/HeaderSearchOptions.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Sema/SemaConsumer.h"
#include "clang/Serialization/ASTReader.h"
Expand All @@ -23,8 +24,8 @@
using namespace clang;

PCHGenerator::PCHGenerator(
const Preprocessor &PP, InMemoryModuleCache &ModuleCache,
StringRef OutputFile, StringRef isysroot, std::shared_ptr<PCHBuffer> Buffer,
Preprocessor &PP, InMemoryModuleCache &ModuleCache, StringRef OutputFile,
StringRef isysroot, std::shared_ptr<PCHBuffer> Buffer,
ArrayRef<std::shared_ptr<ModuleFileExtension>> Extensions,
bool AllowASTWithErrors, bool IncludeTimestamps,
bool BuildingImplicitModule, bool ShouldCacheASTInMemory,
Expand Down Expand Up @@ -88,7 +89,7 @@ ASTDeserializationListener *PCHGenerator::GetASTDeserializationListener() {
return &Writer;
}

ReducedBMIGenerator::ReducedBMIGenerator(const Preprocessor &PP,
ReducedBMIGenerator::ReducedBMIGenerator(Preprocessor &PP,
InMemoryModuleCache &ModuleCache,
StringRef OutputFile)
: PCHGenerator(
Expand All @@ -101,12 +102,24 @@ ReducedBMIGenerator::ReducedBMIGenerator(const Preprocessor &PP,

Module *ReducedBMIGenerator::getEmittingModule(ASTContext &Ctx) {
Module *M = Ctx.getCurrentNamedModule();
assert(M->isNamedModuleUnit() &&
assert(M && M->isNamedModuleUnit() &&
"ReducedBMIGenerator should only be used with C++20 Named modules.");
return M;
}

void ReducedBMIGenerator::HandleTranslationUnit(ASTContext &Ctx) {
// We need to do this to make sure the size of reduced BMI not to be larger
// than full BMI.
//
// FIMXE: We'd better to wrap such options to a new class ASTWriterOptions
// since this is not about searching header really.
// FIXME2: We'd better to move the class writing full BMI with reduced BMI.
HeaderSearchOptions &HSOpts =
getPreprocessor().getHeaderSearchInfo().getHeaderSearchOpts();
HSOpts.ModulesSkipDiagnosticOptions = true;
HSOpts.ModulesSkipHeaderSearchPaths = true;
HSOpts.ModulesSkipPragmaDiagnosticMappings = true;

PCHGenerator::HandleTranslationUnit(Ctx);

if (!isComplete())
Expand Down
6 changes: 4 additions & 2 deletions clang/lib/Tooling/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,16 @@ else()
list(APPEND implicitDirs -I ${implicitDir})
endforeach()

setup_host_tool(clang-ast-dump CLANG_AST_DUMP clang_ast_dump_exe clang_ast_dump_target)

include(GetClangResourceDir)
get_clang_resource_dir(resource_dir PREFIX ${LLVM_BINARY_DIR})
add_custom_command(
COMMENT Generate ASTNodeAPI.json
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/ASTNodeAPI.json
DEPENDS clang-ast-dump clang-resource-headers
DEPENDS ${clang_ast_dump_target} clang-resource-headers
COMMAND
$<TARGET_FILE:clang-ast-dump>
${clang_ast_dump_exe}
# Skip this in debug mode because parsing AST.h is too slow
--skip-processing=${skip_expensive_processing}
-I ${resource_dir}/include
Expand Down
485 changes: 485 additions & 0 deletions clang/test/CodeGen/aarch64-v8.2a-neon-intrinsics-generic.c

Large diffs are not rendered by default.

472 changes: 0 additions & 472 deletions clang/test/CodeGen/aarch64-v8.2a-neon-intrinsics.c

Large diffs are not rendered by default.

207 changes: 207 additions & 0 deletions clang/test/CodeGen/allow-ubsan-check.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 4
// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -emit-llvm -o - %s -fsanitize=signed-integer-overflow,integer-divide-by-zero,null | FileCheck %s
// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -emit-llvm -o - %s -fsanitize=signed-integer-overflow,integer-divide-by-zero,null -fsanitize-trap=signed-integer-overflow,integer-divide-by-zero,null | FileCheck %s --check-prefixes=TRAP
// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -emit-llvm -o - %s -fsanitize=signed-integer-overflow,integer-divide-by-zero,null -fsanitize-recover=signed-integer-overflow,integer-divide-by-zero,null | FileCheck %s --check-prefixes=RECOVER


// CHECK-LABEL: define dso_local i32 @div(
// CHECK-SAME: i32 noundef [[X:%.*]], i32 noundef [[Y:%.*]]) #[[ATTR0:[0-9]+]] {
// CHECK-NEXT: entry:
// CHECK-NEXT: [[X_ADDR:%.*]] = alloca i32, align 4
// CHECK-NEXT: [[Y_ADDR:%.*]] = alloca i32, align 4
// CHECK-NEXT: store i32 [[X]], ptr [[X_ADDR]], align 4
// CHECK-NEXT: store i32 [[Y]], ptr [[Y_ADDR]], align 4
// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[X_ADDR]], align 4
// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[Y_ADDR]], align 4
// CHECK-NEXT: [[TMP2:%.*]] = icmp ne i32 [[TMP1]], 0, !nosanitize [[META2:![0-9]+]]
// CHECK-NEXT: [[TMP3:%.*]] = icmp ne i32 [[TMP0]], -2147483648, !nosanitize [[META2]]
// CHECK-NEXT: [[TMP4:%.*]] = icmp ne i32 [[TMP1]], -1, !nosanitize [[META2]]
// CHECK-NEXT: [[OR:%.*]] = or i1 [[TMP3]], [[TMP4]], !nosanitize [[META2]]
// CHECK-NEXT: [[TMP5:%.*]] = and i1 [[TMP2]], [[OR]], !nosanitize [[META2]]
// CHECK-NEXT: br i1 [[TMP5]], label [[CONT:%.*]], label [[HANDLER_DIVREM_OVERFLOW:%.*]], !prof [[PROF3:![0-9]+]], !nosanitize [[META2]]
// CHECK: handler.divrem_overflow:
// CHECK-NEXT: [[TMP6:%.*]] = zext i32 [[TMP0]] to i64, !nosanitize [[META2]]
// CHECK-NEXT: [[TMP7:%.*]] = zext i32 [[TMP1]] to i64, !nosanitize [[META2]]
// CHECK-NEXT: call void @__ubsan_handle_divrem_overflow_abort(ptr @[[GLOB1:[0-9]+]], i64 [[TMP6]], i64 [[TMP7]]) #[[ATTR3:[0-9]+]], !nosanitize [[META2]]
// CHECK-NEXT: unreachable, !nosanitize [[META2]]
// CHECK: cont:
// CHECK-NEXT: [[DIV:%.*]] = sdiv i32 [[TMP0]], [[TMP1]]
// CHECK-NEXT: ret i32 [[DIV]]
//
// TRAP-LABEL: define dso_local i32 @div(
// TRAP-SAME: i32 noundef [[X:%.*]], i32 noundef [[Y:%.*]]) #[[ATTR0:[0-9]+]] {
// TRAP-NEXT: entry:
// TRAP-NEXT: [[X_ADDR:%.*]] = alloca i32, align 4
// TRAP-NEXT: [[Y_ADDR:%.*]] = alloca i32, align 4
// TRAP-NEXT: store i32 [[X]], ptr [[X_ADDR]], align 4
// TRAP-NEXT: store i32 [[Y]], ptr [[Y_ADDR]], align 4
// TRAP-NEXT: [[TMP0:%.*]] = load i32, ptr [[X_ADDR]], align 4
// TRAP-NEXT: [[TMP1:%.*]] = load i32, ptr [[Y_ADDR]], align 4
// TRAP-NEXT: [[TMP2:%.*]] = icmp ne i32 [[TMP1]], 0, !nosanitize [[META2:![0-9]+]]
// TRAP-NEXT: [[TMP3:%.*]] = icmp ne i32 [[TMP0]], -2147483648, !nosanitize [[META2]]
// TRAP-NEXT: [[TMP4:%.*]] = icmp ne i32 [[TMP1]], -1, !nosanitize [[META2]]
// TRAP-NEXT: [[OR:%.*]] = or i1 [[TMP3]], [[TMP4]], !nosanitize [[META2]]
// TRAP-NEXT: [[TMP5:%.*]] = and i1 [[TMP2]], [[OR]], !nosanitize [[META2]]
// TRAP-NEXT: br i1 [[TMP5]], label [[CONT:%.*]], label [[TRAP:%.*]], !nosanitize [[META2]]
// TRAP: trap:
// TRAP-NEXT: call void @llvm.ubsantrap(i8 3) #[[ATTR3:[0-9]+]], !nosanitize [[META2]]
// TRAP-NEXT: unreachable, !nosanitize [[META2]]
// TRAP: cont:
// TRAP-NEXT: [[DIV:%.*]] = sdiv i32 [[TMP0]], [[TMP1]]
// TRAP-NEXT: ret i32 [[DIV]]
//
// RECOVER-LABEL: define dso_local i32 @div(
// RECOVER-SAME: i32 noundef [[X:%.*]], i32 noundef [[Y:%.*]]) #[[ATTR0:[0-9]+]] {
// RECOVER-NEXT: entry:
// RECOVER-NEXT: [[X_ADDR:%.*]] = alloca i32, align 4
// RECOVER-NEXT: [[Y_ADDR:%.*]] = alloca i32, align 4
// RECOVER-NEXT: store i32 [[X]], ptr [[X_ADDR]], align 4
// RECOVER-NEXT: store i32 [[Y]], ptr [[Y_ADDR]], align 4
// RECOVER-NEXT: [[TMP0:%.*]] = load i32, ptr [[X_ADDR]], align 4
// RECOVER-NEXT: [[TMP1:%.*]] = load i32, ptr [[Y_ADDR]], align 4
// RECOVER-NEXT: [[TMP2:%.*]] = icmp ne i32 [[TMP1]], 0, !nosanitize [[META2:![0-9]+]]
// RECOVER-NEXT: [[TMP3:%.*]] = icmp ne i32 [[TMP0]], -2147483648, !nosanitize [[META2]]
// RECOVER-NEXT: [[TMP4:%.*]] = icmp ne i32 [[TMP1]], -1, !nosanitize [[META2]]
// RECOVER-NEXT: [[OR:%.*]] = or i1 [[TMP3]], [[TMP4]], !nosanitize [[META2]]
// RECOVER-NEXT: [[TMP5:%.*]] = and i1 [[TMP2]], [[OR]], !nosanitize [[META2]]
// RECOVER-NEXT: br i1 [[TMP5]], label [[CONT:%.*]], label [[HANDLER_DIVREM_OVERFLOW:%.*]], !prof [[PROF3:![0-9]+]], !nosanitize [[META2]]
// RECOVER: handler.divrem_overflow:
// RECOVER-NEXT: [[TMP6:%.*]] = zext i32 [[TMP0]] to i64, !nosanitize [[META2]]
// RECOVER-NEXT: [[TMP7:%.*]] = zext i32 [[TMP1]] to i64, !nosanitize [[META2]]
// RECOVER-NEXT: call void @__ubsan_handle_divrem_overflow(ptr @[[GLOB1:[0-9]+]], i64 [[TMP6]], i64 [[TMP7]]) #[[ATTR3:[0-9]+]], !nosanitize [[META2]]
// RECOVER-NEXT: br label [[CONT]], !nosanitize [[META2]]
// RECOVER: cont:
// RECOVER-NEXT: [[DIV:%.*]] = sdiv i32 [[TMP0]], [[TMP1]]
// RECOVER-NEXT: ret i32 [[DIV]]
//
int div(int x, int y) {
return x / y;
}

// CHECK-LABEL: define dso_local i32 @null(
// CHECK-SAME: ptr noundef [[X:%.*]]) #[[ATTR0]] {
// CHECK-NEXT: entry:
// CHECK-NEXT: [[X_ADDR:%.*]] = alloca ptr, align 8
// CHECK-NEXT: store ptr [[X]], ptr [[X_ADDR]], align 8
// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[X_ADDR]], align 8
// CHECK-NEXT: [[TMP1:%.*]] = icmp ne ptr [[TMP0]], null, !nosanitize [[META2]]
// CHECK-NEXT: br i1 [[TMP1]], label [[CONT:%.*]], label [[HANDLER_TYPE_MISMATCH:%.*]], !prof [[PROF3]], !nosanitize [[META2]]
// CHECK: handler.type_mismatch:
// CHECK-NEXT: [[TMP2:%.*]] = ptrtoint ptr [[TMP0]] to i64, !nosanitize [[META2]]
// CHECK-NEXT: call void @__ubsan_handle_type_mismatch_v1_abort(ptr @[[GLOB2:[0-9]+]], i64 [[TMP2]]) #[[ATTR3]], !nosanitize [[META2]]
// CHECK-NEXT: unreachable, !nosanitize [[META2]]
// CHECK: cont:
// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[TMP0]], align 4
// CHECK-NEXT: ret i32 [[TMP3]]
//
// TRAP-LABEL: define dso_local i32 @null(
// TRAP-SAME: ptr noundef [[X:%.*]]) #[[ATTR0]] {
// TRAP-NEXT: entry:
// TRAP-NEXT: [[X_ADDR:%.*]] = alloca ptr, align 8
// TRAP-NEXT: store ptr [[X]], ptr [[X_ADDR]], align 8
// TRAP-NEXT: [[TMP0:%.*]] = load ptr, ptr [[X_ADDR]], align 8
// TRAP-NEXT: [[TMP1:%.*]] = icmp ne ptr [[TMP0]], null, !nosanitize [[META2]]
// TRAP-NEXT: br i1 [[TMP1]], label [[CONT:%.*]], label [[TRAP:%.*]], !nosanitize [[META2]]
// TRAP: trap:
// TRAP-NEXT: call void @llvm.ubsantrap(i8 22) #[[ATTR3]], !nosanitize [[META2]]
// TRAP-NEXT: unreachable, !nosanitize [[META2]]
// TRAP: cont:
// TRAP-NEXT: [[TMP2:%.*]] = load i32, ptr [[TMP0]], align 4
// TRAP-NEXT: ret i32 [[TMP2]]
//
// RECOVER-LABEL: define dso_local i32 @null(
// RECOVER-SAME: ptr noundef [[X:%.*]]) #[[ATTR0]] {
// RECOVER-NEXT: entry:
// RECOVER-NEXT: [[X_ADDR:%.*]] = alloca ptr, align 8
// RECOVER-NEXT: store ptr [[X]], ptr [[X_ADDR]], align 8
// RECOVER-NEXT: [[TMP0:%.*]] = load ptr, ptr [[X_ADDR]], align 8
// RECOVER-NEXT: [[TMP1:%.*]] = icmp ne ptr [[TMP0]], null, !nosanitize [[META2]]
// RECOVER-NEXT: br i1 [[TMP1]], label [[CONT:%.*]], label [[HANDLER_TYPE_MISMATCH:%.*]], !prof [[PROF3]], !nosanitize [[META2]]
// RECOVER: handler.type_mismatch:
// RECOVER-NEXT: [[TMP2:%.*]] = ptrtoint ptr [[TMP0]] to i64, !nosanitize [[META2]]
// RECOVER-NEXT: call void @__ubsan_handle_type_mismatch_v1(ptr @[[GLOB2:[0-9]+]], i64 [[TMP2]]) #[[ATTR3]], !nosanitize [[META2]]
// RECOVER-NEXT: br label [[CONT]], !nosanitize [[META2]]
// RECOVER: cont:
// RECOVER-NEXT: [[TMP3:%.*]] = load i32, ptr [[TMP0]], align 4
// RECOVER-NEXT: ret i32 [[TMP3]]
//
int null(int* x) {
return *x;
}

// CHECK-LABEL: define dso_local i32 @overflow(
// CHECK-SAME: i32 noundef [[X:%.*]], i32 noundef [[Y:%.*]]) #[[ATTR0]] {
// CHECK-NEXT: entry:
// CHECK-NEXT: [[X_ADDR:%.*]] = alloca i32, align 4
// CHECK-NEXT: [[Y_ADDR:%.*]] = alloca i32, align 4
// CHECK-NEXT: store i32 [[X]], ptr [[X_ADDR]], align 4
// CHECK-NEXT: store i32 [[Y]], ptr [[Y_ADDR]], align 4
// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[X_ADDR]], align 4
// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[Y_ADDR]], align 4
// CHECK-NEXT: [[TMP2:%.*]] = call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 [[TMP0]], i32 [[TMP1]]), !nosanitize [[META2]]
// CHECK-NEXT: [[TMP3:%.*]] = extractvalue { i32, i1 } [[TMP2]], 0, !nosanitize [[META2]]
// CHECK-NEXT: [[TMP4:%.*]] = extractvalue { i32, i1 } [[TMP2]], 1, !nosanitize [[META2]]
// CHECK-NEXT: [[TMP5:%.*]] = xor i1 [[TMP4]], true, !nosanitize [[META2]]
// CHECK-NEXT: br i1 [[TMP5]], label [[CONT:%.*]], label [[HANDLER_ADD_OVERFLOW:%.*]], !prof [[PROF3]], !nosanitize [[META2]]
// CHECK: handler.add_overflow:
// CHECK-NEXT: [[TMP6:%.*]] = zext i32 [[TMP0]] to i64, !nosanitize [[META2]]
// CHECK-NEXT: [[TMP7:%.*]] = zext i32 [[TMP1]] to i64, !nosanitize [[META2]]
// CHECK-NEXT: call void @__ubsan_handle_add_overflow_abort(ptr @[[GLOB3:[0-9]+]], i64 [[TMP6]], i64 [[TMP7]]) #[[ATTR3]], !nosanitize [[META2]]
// CHECK-NEXT: unreachable, !nosanitize [[META2]]
// CHECK: cont:
// CHECK-NEXT: ret i32 [[TMP3]]
//
// TRAP-LABEL: define dso_local i32 @overflow(
// TRAP-SAME: i32 noundef [[X:%.*]], i32 noundef [[Y:%.*]]) #[[ATTR0]] {
// TRAP-NEXT: entry:
// TRAP-NEXT: [[X_ADDR:%.*]] = alloca i32, align 4
// TRAP-NEXT: [[Y_ADDR:%.*]] = alloca i32, align 4
// TRAP-NEXT: store i32 [[X]], ptr [[X_ADDR]], align 4
// TRAP-NEXT: store i32 [[Y]], ptr [[Y_ADDR]], align 4
// TRAP-NEXT: [[TMP0:%.*]] = load i32, ptr [[X_ADDR]], align 4
// TRAP-NEXT: [[TMP1:%.*]] = load i32, ptr [[Y_ADDR]], align 4
// TRAP-NEXT: [[TMP2:%.*]] = call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 [[TMP0]], i32 [[TMP1]]), !nosanitize [[META2]]
// TRAP-NEXT: [[TMP3:%.*]] = extractvalue { i32, i1 } [[TMP2]], 0, !nosanitize [[META2]]
// TRAP-NEXT: [[TMP4:%.*]] = extractvalue { i32, i1 } [[TMP2]], 1, !nosanitize [[META2]]
// TRAP-NEXT: [[TMP5:%.*]] = xor i1 [[TMP4]], true, !nosanitize [[META2]]
// TRAP-NEXT: br i1 [[TMP5]], label [[CONT:%.*]], label [[TRAP:%.*]], !nosanitize [[META2]]
// TRAP: trap:
// TRAP-NEXT: call void @llvm.ubsantrap(i8 0) #[[ATTR3]], !nosanitize [[META2]]
// TRAP-NEXT: unreachable, !nosanitize [[META2]]
// TRAP: cont:
// TRAP-NEXT: ret i32 [[TMP3]]
//
// RECOVER-LABEL: define dso_local i32 @overflow(
// RECOVER-SAME: i32 noundef [[X:%.*]], i32 noundef [[Y:%.*]]) #[[ATTR0]] {
// RECOVER-NEXT: entry:
// RECOVER-NEXT: [[X_ADDR:%.*]] = alloca i32, align 4
// RECOVER-NEXT: [[Y_ADDR:%.*]] = alloca i32, align 4
// RECOVER-NEXT: store i32 [[X]], ptr [[X_ADDR]], align 4
// RECOVER-NEXT: store i32 [[Y]], ptr [[Y_ADDR]], align 4
// RECOVER-NEXT: [[TMP0:%.*]] = load i32, ptr [[X_ADDR]], align 4
// RECOVER-NEXT: [[TMP1:%.*]] = load i32, ptr [[Y_ADDR]], align 4
// RECOVER-NEXT: [[TMP2:%.*]] = call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 [[TMP0]], i32 [[TMP1]]), !nosanitize [[META2]]
// RECOVER-NEXT: [[TMP3:%.*]] = extractvalue { i32, i1 } [[TMP2]], 0, !nosanitize [[META2]]
// RECOVER-NEXT: [[TMP4:%.*]] = extractvalue { i32, i1 } [[TMP2]], 1, !nosanitize [[META2]]
// RECOVER-NEXT: [[TMP5:%.*]] = xor i1 [[TMP4]], true, !nosanitize [[META2]]
// RECOVER-NEXT: br i1 [[TMP5]], label [[CONT:%.*]], label [[HANDLER_ADD_OVERFLOW:%.*]], !prof [[PROF3]], !nosanitize [[META2]]
// RECOVER: handler.add_overflow:
// RECOVER-NEXT: [[TMP6:%.*]] = zext i32 [[TMP0]] to i64, !nosanitize [[META2]]
// RECOVER-NEXT: [[TMP7:%.*]] = zext i32 [[TMP1]] to i64, !nosanitize [[META2]]
// RECOVER-NEXT: call void @__ubsan_handle_add_overflow(ptr @[[GLOB3:[0-9]+]], i64 [[TMP6]], i64 [[TMP7]]) #[[ATTR3]], !nosanitize [[META2]]
// RECOVER-NEXT: br label [[CONT]], !nosanitize [[META2]]
// RECOVER: cont:
// RECOVER-NEXT: ret i32 [[TMP3]]
//
int overflow(int x, int y) {
return x + y;
}
//.
// CHECK: [[META2]] = !{}
// CHECK: [[PROF3]] = !{!"branch_weights", i32 1048575, i32 1}
//.
// TRAP: [[META2]] = !{}
//.
// RECOVER: [[META2]] = !{}
// RECOVER: [[PROF3]] = !{!"branch_weights", i32 1048575, i32 1}
//.
Loading