diff --git a/libcextract/Closure.cpp b/libcextract/Closure.cpp new file mode 100644 index 0000000..7bb6277 --- /dev/null +++ b/libcextract/Closure.cpp @@ -0,0 +1,74 @@ +//===- Closure.hh - Compute dependencies of a given symbol *- C++ -*-===// +// +// This project is licensed 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 +/// Compute a set of Decls that is necessary to compile a given symbol. +// +//===----------------------------------------------------------------------===// + +/* Author: Giuliano Belinassi */ + +#include "Closure.hh" + +/** Add a decl to the Dependencies set and all its previous declarations in the + AST. A function can have multiple definitions but its body may only be + defined later. */ +bool ClosureSet::Add_Decl_And_Prevs(Decl *decl) +{ + bool inserted = false; + + /* Do not insert builtin decls. */ + if (Is_Builtin_Decl(decl)) { + return false; + } + + while (decl) { + if (!Is_Decl_Marked(decl)) { + Dependencies.insert(decl); + inserted = true; + } + + decl = decl->getPreviousDecl(); + } + return inserted; +} + +void DeclClosureVisitor::Compute_Closure_Of_Symbols(const std::vector &names, + std::unordered_set *matched_names) +{ + /* FIXME: clang has a mechanism (DeclContext::lookup) which is SUPPOSED TO + return the list of all Decls that matches the lookup name. However, this + method doesn't work as intented. In kernel (see github issue #20) it + results in the lookup_result set not having the Decl that has the body of + the function, which is the most important! + + Hence, do our own thing here. Create a set and sweep the top level decls, + analyzing the symbol that matches the name in the set. */ + + std::unordered_set setof_names(std::make_move_iterator(names.begin()), + std::make_move_iterator(names.end())); + + for (auto it = AST->top_level_begin(); it != AST->top_level_end(); ++it) { + NamedDecl *decl = dyn_cast(*it); + + if (!decl) { + /* Decl does not have a name, thus skip it. */ + continue; + } + + const std::string &decl_name = decl->getName().str(); + /* If the symbol name is in the set... */ + if (setof_names.find(decl_name) != setof_names.end()) { + /* Mark that name as matched. */ + if (matched_names) + matched_names->insert(decl_name); + /* Find its dependencies. */ + TraverseDecl(decl); + } + } +} diff --git a/libcextract/Closure.hh b/libcextract/Closure.hh new file mode 100644 index 0000000..e4a364c --- /dev/null +++ b/libcextract/Closure.hh @@ -0,0 +1,543 @@ +//===- Closure.hh - Compute dependencies of a given symbol *- C++ -*-===// +// +// This project is licensed 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 +/// Compute a set of Decls that is necessary to compile a given symbol. +// +//===----------------------------------------------------------------------===// + +/* Author: Giuliano Belinassi */ + +#pragma once + +#include +#include +#include +#include +#include + +#include "EnumConstTbl.hh" +#include "LLVMMisc.hh" + +using namespace clang; + +class ClosureSet +{ + public: + /** Check if a given declaration was already marked as dependency. */ + inline bool Is_Decl_Marked(Decl *decl) + { return Dependencies.find(decl) != Dependencies.end(); } + + /** Mark decl as dependencies and all its previous decls versions. */ + bool Add_Decl_And_Prevs(Decl *decl); + + /** Add a single decl to the set. */ + void Add_Single_Decl(Decl *decl) + { + Dependencies.insert(decl); + } + + inline std::unordered_set &Get_Set(void) + { + return Dependencies; + } + + inline void Remove_Decl(Decl *decl) + { + Dependencies.erase(decl); + } + + private: + /** Datastructure holding all Decls required for the functions. This is + then used to mark which Decls we need to output. + + We use a unordered_set because we need quick lookup, so a hash table + may be (?) the ideal datastructure for this. */ + std::unordered_set Dependencies; +}; + + +/// AST visitor for computing the closure of given symbol. From clang: +/// +/// A class that does preorder or postorder +/// depth-first traversal on the entire Clang AST and visits each node. +/// +/// This class performs three distinct tasks: +/// 1. traverse the AST (i.e. go to each node); +/// 2. at a given node, walk up the class hierarchy, starting from +/// the node's dynamic type, until the top-most class (e.g. Stmt, +/// Decl, or Type) is reached. +/// 3. given a (node, class) combination, where 'class' is some base +/// class of the dynamic type of 'node', call a user-overridable +/// function to actually visit the node. +/// +/// These tasks are done by three groups of methods, respectively: +/// 1. TraverseDecl(Decl *x) does task #1. It is the entry point +/// for traversing an AST rooted at x. This method simply +/// dispatches (i.e. forwards) to TraverseFoo(Foo *x) where Foo +/// is the dynamic type of *x, which calls WalkUpFromFoo(x) and +/// then recursively visits the child nodes of x. +/// TraverseStmt(Stmt *x) and TraverseType(QualType x) work +/// similarly. +/// 2. WalkUpFromFoo(Foo *x) does task #2. It does not try to visit +/// any child node 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) (see the next list item). +/// 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 node are called in the +/// top-down order (e.g. for a node of type NamespaceDecl, the order will +/// be VisitDecl(), VisitNamedDecl(), and then VisitNamespaceDecl()). +/// +/// This scheme guarantees that all Visit*() calls for the same AST +/// node are grouped together. In other words, Visit*() methods for +/// different nodes 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 declarations, types, statements, +/// expressions, or other AST nodes 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. +/// +/// By default, this visitor tries to visit every part of the explicit +/// source code exactly once. The default policy towards templates +/// is to descend into the 'pattern' class or function body, not any +/// explicit or implicit instantiations. Explicit specializations +/// are still visited, and the patterns of partial specializations +/// are visited separately. This behavior can be changed by +/// overriding shouldVisitTemplateInstantiations() in the derived class +/// to return true, in which case all known implicit and explicit +/// instantiations will be visited at the same time as the pattern +/// from which they were produced. +/// +/// By default, this visitor preorder traverses the AST. If postorder traversal +/// is needed, the \c shouldTraversePostOrder method needs to be overridden +/// to return \c true. +class DeclClosureVisitor : public RecursiveASTVisitor +{ + public: + DeclClosureVisitor(ASTUnit *ast, EnumConstantTable &table) + : RecursiveASTVisitor(), + AST(ast), + EnumTable(table) + { + } + + /* ---------------- Administrative stuff --------------- */ + + /** Tell the RecursiveASTVisitor that we want to visit template instantiations. */ + bool shouldVisitTemplateInstantiations(void) const //override + { + return true; + } + + /** Check if the Decl was already analyized. */ + inline bool Already_Analyzed(Decl *decl) + { + return AnalyzedDecls.find(decl) != AnalyzedDecls.end(); + } + + inline void Mark_As_Analyzed(Decl *decl) + { + AnalyzedDecls.insert(decl); + } + + /** Check if we can get a partial declaration of `decl` rather than a full + declaration. */ + inline TagDecl *Maybe_Partial_Decl(TagDecl *decl) + { + if (decl->isCompleteDefinitionRequired()) { + /* We need the full definition of this struct, enum, or union. */ + return decl; + } else { + /* We don't need the full defintion. Hide the body for the PrettyPrint + class. */ + decl->setCompleteDefinition(false); + /* FIXME: The PrettyPrint class will attempt to write this declaration + as the user wrote, that means WITH a body. To avoid this, we set + the StartLocation to equal the End of location, which will trigger + the "clang got confused" mechanism in PrettyPrint and force it to + be output as a tree dump instead of what the user wrote. The + correct way of doing this would be update the source location to + the correct range. */ + decl->setLocStart(decl->getEndLoc()); + return decl; + } + } + + enum { + VISITOR_CONTINUE = true, // Return this for the AST transversal to continue; + VISITOR_STOP = false, // Return this for the AST tranversal to stop completely; + }; + +#define TRY_TO(CALL_EXPR) \ + if ((CALL_EXPR) == VISITOR_STOP) \ + return VISITOR_STOP + +#define DO_NOT_RUN_IF_ALREADY_ANALYZED(decl) \ + if (Already_Analyzed(decl) == true) \ + return VISITOR_CONTINUE + + /* Special Traversal functions which marks if a Decl was already analyzed. + This macro generates them. */ +#define DEF_MARKING_TRAVERSE_DECL(DECL) \ + bool Traverse##DECL(DECL *decl) \ + { \ + DO_NOT_RUN_IF_ALREADY_ANALYZED(decl); \ + Mark_As_Analyzed(decl); \ + return RecursiveASTVisitor::Traverse##DECL(decl); \ + } + + /* Override of TraverseDecl that marks that the given Decl was analyzed. So + far it seems we only need this function for now. */ + DEF_MARKING_TRAVERSE_DECL(Decl); + + /* -------- C Declarations ----------------- */ + + bool VisitFunctionDecl(FunctionDecl *decl) + { + if (decl->getBuiltinID() != 0) { + /* Do not redeclare compiler builtin functions. */ + return VISITOR_CONTINUE; + } + + /* If this function is actually a template function specialization, then + look into it. */ + if (FunctionTemplateSpecializationInfo *ftsi = + decl->getTemplateSpecializationInfo()) { + TRY_TO(VisitFunctionTemplateDecl(ftsi->getTemplate())); + } + + /* Mark function for output. */ + FunctionDecl *to_mark = decl; + if (decl->isStatic()) { + FunctionDecl *definition = decl->getDefinition(); + /* FIXME: Declared static function in closure without a body? */ + if (definition != nullptr) { + to_mark = definition; + /* Make sure we parse the function version with a body. */ + TRY_TO(TraverseDecl(to_mark)); + } + } + + Closure.Add_Decl_And_Prevs(to_mark); + + return VISITOR_CONTINUE; + } + + bool VisitValueDecl(ValueDecl *decl) + { + TRY_TO(TraverseType(decl->getType())); + return VISITOR_CONTINUE; + } + + bool VisitRecordDecl(RecordDecl *decl) + { + /* If this is a C++ record decl, then analyze it as such. */ + if (CXXRecordDecl *cxxdecl = dyn_cast(decl)) { + TRY_TO(VisitCXXRecordDecl(cxxdecl)); + } + + if (TypedefNameDecl *typedecl = decl->getTypedefNameForAnonDecl()) { + /* In case this struct is defined without a name, such as: + struct { + int a; + }; + + then do not add it to the output. This can happen in the case where the + original code was declared as: + + typedef struct { int a; } A; */ + return VisitTypedefNameDecl(typedecl); + } else { + Closure.Add_Decl_And_Prevs(Maybe_Partial_Decl(decl)); + } + + return VISITOR_CONTINUE; + } + + bool VisitEnumDecl(EnumDecl *decl) + { + if (TypedefNameDecl *typedecl = decl->getTypedefNameForAnonDecl()) { + /* In case this enum is defined without a name, such as: + enum { + CONSTANT = 1, + }; + + then do not add it to the output. This can happen in the case where the + original code was declared as: + + typedef enum { CONSTANT = 1 } A; */ + return VisitTypedefNameDecl(typedecl); + } else { + Closure.Add_Decl_And_Prevs(decl); + } + + return VISITOR_CONTINUE; + } + + bool VisitTagDecl(TagDecl *decl) + { + DeclContext *parent = decl->getParent(); + if (parent && parent->hasValidDeclKind()) { + /* Check if parent declaration is a namespace. If yes, then add it + as well BUT DO NOT add its children. */ + if (parent->isNamespace()) { + NamespaceDecl *nm = cast(parent); + TRY_TO(VisitNamespaceDecl(nm)); + } + } + + return VISITOR_CONTINUE; + } + + bool VisitEnumConstantDecl(EnumConstantDecl *decl) + { + /* Add original EnumDecl it originated. */ + EnumDecl *enum_decl = EnumTable.Get(decl); + if (enum_decl) { + return TraverseDecl(enum_decl); + } + + return VISITOR_CONTINUE; + } + + /* Not called automatically by Transverse. */ + bool VisitTypedefNameDecl(TypedefNameDecl *decl) + { + TRY_TO(TraverseType(decl->getUnderlyingType())); + Closure.Add_Decl_And_Prevs(decl); + + return VISITOR_CONTINUE; + } + + bool VisitRecordType(const RecordType *type) + { + return VISITOR_CONTINUE; + } + + bool VisitVarDecl(VarDecl *decl) + { + /* Avoid adding variables that are not global. */ + if (decl->hasGlobalStorage()) { + Closure.Add_Decl_And_Prevs(decl); + } + + return VISITOR_CONTINUE; + } + + /* --------- C++ Declarations ---------- */ + + bool VisitFunctionTemplateDecl(FunctionTemplateDecl *decl) + { + Closure.Add_Decl_And_Prevs(decl); + + return VISITOR_CONTINUE; + } + + bool VisitTemplateDecl(TemplateDecl *decl) + { + Closure.Add_Decl_And_Prevs(decl); + + return VISITOR_CONTINUE; + } + + bool VisitNamespaceDecl(NamespaceDecl *decl) + { + Closure.Add_Decl_And_Prevs(decl); + + return VISITOR_CONTINUE; + } + + bool VisitCXXRecordDecl(CXXRecordDecl *decl) + { + /* In case this is a ClassTemplateSpecialDecl, then analyze it as such. */ + if (ClassTemplateSpecializationDecl *ctsd = + dyn_cast(decl)) { + TRY_TO(VisitClassTemplateSpecializationDecl(ctsd)); + } + + Closure.Add_Decl_And_Prevs(decl); + + return VISITOR_CONTINUE; + } + + bool VisitClassTemplateDecl(ClassTemplateDecl *decl) + { + Closure.Add_Decl_And_Prevs(decl); + + return VISITOR_CONTINUE; + } + + bool VisitClassTemplateSpecializationDecl(ClassTemplateSpecializationDecl *decl) + { + /* Assume + template + class V{ + T x; + }; + struct A {int x; } + + V u; + This call will add the A to the closure. + */ + TRY_TO(TraverseTemplateArguments(decl->getTemplateArgs().asArray())); + + /* This call will add the V to the closure. */ + return VisitClassTemplateDecl(decl->getSpecializedTemplate()); + } + + /* ----------- Statements -------------- */ + + bool VisitDeclRefExpr(DeclRefExpr *expr) + { + TRY_TO(TraverseDecl(expr->getDecl())); + /* For C++ we also need to analyze the name specifier. */ + if (NestedNameSpecifier *nns = expr->getQualifier()) { + TRY_TO(TraverseNestedNameSpecifier(nns)); + } + + /* Analyze the decl it references to. */ + return TraverseDecl(expr->getDecl()); + } + + bool VisitOffsetOfExpr(OffsetOfExpr *expr) + { + /* Handle the following case: + * + * typedef struct { + * int x; + * int y; + * } Head; + * + * int x = __builtin_offsetof(Head, y); + */ + unsigned num_components = expr->getNumComponents(); + for (unsigned i = 0; i < num_components; i++) { + /* Get the RecordDecl referenced in the builtin_offsetof and add to the + dependency list. */ + const OffsetOfNode &component = expr->getComponent(i); + if (component.getKind() == OffsetOfNode::Field) { + const FieldDecl *field = component.getField(); + + RecordDecl *record = (RecordDecl *)field->getParent(); + TRY_TO(TraverseDecl(record)); + } + } + + return VISITOR_CONTINUE; + } + + /* -------- Types ----------------- */ + bool VisitTagType(const TagType *type) + { + TRY_TO(TraverseDecl(type->getDecl())); + return VISITOR_CONTINUE; + } + + bool VisitTypedefType(const TypedefType *type) + { + TRY_TO(TraverseDecl(type->getDecl())); + return VISITOR_CONTINUE; + } + + /* -------- C++ Types ------------- */ + bool VisitTemplateSpecializationType(const TemplateSpecializationType *type) + { + /* For some reason the Traverse do not run on the original template + C++ Record, only on its specializations. Hence do it here. */ + return TraverseDecl(type->getAsCXXRecordDecl()); + } + + bool VisitDeducedTemplateSpecializationType( + const DeducedTemplateSpecializationType *type) + { + TemplateName template_name = type->getTemplateName(); + return TraverseDecl(template_name.getAsTemplateDecl()); + } + + bool VisitElaboratedType(const ElaboratedType *type) + { + return VISITOR_CONTINUE; + } + + /* ----------- Other C++ stuff ----------- */ + bool TraverseNestedNameSpecifier(NestedNameSpecifier *nns) + { + switch (nns->getKind()) { + case NestedNameSpecifier::Namespace: + /* Do not traverse to avoid adding unecessary childs. */ + TRY_TO(VisitNamespaceDecl(nns->getAsNamespace())); + break; + case NestedNameSpecifier::NamespaceAlias: + TRY_TO(TraverseDecl(nns->getAsNamespaceAlias())); + break; + + case NestedNameSpecifier::Super: + /* Something regarding MSVC, but lets put this here. */ + TRY_TO(TraverseDecl(nns->getAsRecordDecl())); + break; + + default: + break; + } + return RecursiveASTVisitor::TraverseNestedNameSpecifier(nns); + } + + bool TraverseCXXBaseSpecifier(const CXXBaseSpecifier base) + { + TRY_TO(TraverseType(base.getType())); + return RecursiveASTVisitor::TraverseCXXBaseSpecifier(base); + } + + ClosureSet &Get_Closure(void) + { + return Closure; + } + + const ClosureSet &Get_Closure(void) const + { + return Closure; + } + + void Compute_Closure_Of_Symbols(const std::vector &names, + std::unordered_set *matched_names = nullptr); + + private: + + /** The ASTUnit object. */ + ASTUnit *AST; + + /** Datastructure holding all Decls required for the functions. This is + then used to mark which Decls we need to output. + + We use a unordered_set because we need quick lookup, so a hash table + may be (?) the ideal datastructure for this. */ + ClosureSet Closure; + + /** The table maping EnumConstantDecl to its original EnumDecl, used to find + out where a certain EnumConstantDecl was defined. */ + EnumConstantTable &EnumTable; + + /** The set of all analyzed Decls. */ + std::unordered_set AnalyzedDecls; +#undef TRY_TO +#undef DO_NOT_RUN_IF_ALREADY_ANALYZED +#undef DEF_MARKING_TRAVERSE_DECL +}; diff --git a/libcextract/FunctionDepsFinder.cpp b/libcextract/FunctionDepsFinder.cpp index fff7fee..6f93755 100644 --- a/libcextract/FunctionDepsFinder.cpp +++ b/libcextract/FunctionDepsFinder.cpp @@ -21,535 +21,21 @@ #include "Error.hh" #include "LLVMMisc.hh" -/** Add a decl to the Dependencies set and all its previous declarations in the - AST. A function can have multiple definitions but its body may only be - defined later. */ -bool ClosureSet::Add_Decl_And_Prevs(Decl *decl) -{ - bool inserted = false; - - /* Do not insert builtin decls. */ - if (Is_Builtin_Decl(decl)) { - return false; - } - - while (decl) { - if (!Is_Decl_Marked(decl)) { - Dependencies.insert(decl); - inserted = true; - } - - decl = decl->getPreviousDecl(); - } - return inserted; -} - -/// AST visitor for computing the closure of given symbol. From clang: -/// -/// A class that does preorder or postorder -/// depth-first traversal on the entire Clang AST and visits each node. -/// -/// This class performs three distinct tasks: -/// 1. traverse the AST (i.e. go to each node); -/// 2. at a given node, walk up the class hierarchy, starting from -/// the node's dynamic type, until the top-most class (e.g. Stmt, -/// Decl, or Type) is reached. -/// 3. given a (node, class) combination, where 'class' is some base -/// class of the dynamic type of 'node', call a user-overridable -/// function to actually visit the node. -/// -/// These tasks are done by three groups of methods, respectively: -/// 1. TraverseDecl(Decl *x) does task #1. It is the entry point -/// for traversing an AST rooted at x. This method simply -/// dispatches (i.e. forwards) to TraverseFoo(Foo *x) where Foo -/// is the dynamic type of *x, which calls WalkUpFromFoo(x) and -/// then recursively visits the child nodes of x. -/// TraverseStmt(Stmt *x) and TraverseType(QualType x) work -/// similarly. -/// 2. WalkUpFromFoo(Foo *x) does task #2. It does not try to visit -/// any child node 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) (see the next list item). -/// 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 node are called in the -/// top-down order (e.g. for a node of type NamespaceDecl, the order will -/// be VisitDecl(), VisitNamedDecl(), and then VisitNamespaceDecl()). -/// -/// This scheme guarantees that all Visit*() calls for the same AST -/// node are grouped together. In other words, Visit*() methods for -/// different nodes 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 declarations, types, statements, -/// expressions, or other AST nodes 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. -/// -/// By default, this visitor tries to visit every part of the explicit -/// source code exactly once. The default policy towards templates -/// is to descend into the 'pattern' class or function body, not any -/// explicit or implicit instantiations. Explicit specializations -/// are still visited, and the patterns of partial specializations -/// are visited separately. This behavior can be changed by -/// overriding shouldVisitTemplateInstantiations() in the derived class -/// to return true, in which case all known implicit and explicit -/// instantiations will be visited at the same time as the pattern -/// from which they were produced. -/// -/// By default, this visitor preorder traverses the AST. If postorder traversal -/// is needed, the \c shouldTraversePostOrder method needs to be overridden -/// to return \c true. -class DeclClosureVisitor : public RecursiveASTVisitor -{ - public: - DeclClosureVisitor(ClosureSet &closure, EnumConstantTable &table) - : RecursiveASTVisitor(), - Closure(closure), - EnumTable(table) - { - } - - /* ---------------- Administrative stuff --------------- */ - - /** Tell the RecursiveASTVisitor that we want to visit template instantiations. */ - bool shouldVisitTemplateInstantiations(void) const //override - { - return true; - } - - /** Check if the Decl was already analyized. */ - inline bool Already_Analyzed(Decl *decl) - { - return AnalyzedDecls.find(decl) != AnalyzedDecls.end(); - } - - inline void Mark_As_Analyzed(Decl *decl) - { - AnalyzedDecls.insert(decl); - } - - /** Check if we can get a partial declaration of `decl` rather than a full - declaration. */ - inline TagDecl *Maybe_Partial_Decl(TagDecl *decl) - { - if (decl->isCompleteDefinitionRequired()) { - /* We need the full definition of this struct, enum, or union. */ - return decl; - } else { - /* We don't need the full defintion. Hide the body for the PrettyPrint - class. */ - decl->setCompleteDefinition(false); - /* FIXME: The PrettyPrint class will attempt to write this declaration - as the user wrote, that means WITH a body. To avoid this, we set - the StartLocation to equal the End of location, which will trigger - the "clang got confused" mechanism in PrettyPrint and force it to - be output as a tree dump instead of what the user wrote. The - correct way of doing this would be update the source location to - the correct range. */ - decl->setLocStart(decl->getEndLoc()); - return decl; - } - } - - enum { - VISITOR_CONTINUE = true, // Return this for the AST transversal to continue; - VISITOR_STOP = false, // Return this for the AST tranversal to stop completely; - }; - -#define TRY_TO(CALL_EXPR) \ - if ((CALL_EXPR) == VISITOR_STOP) \ - return VISITOR_STOP - -#define DO_NOT_RUN_IF_ALREADY_ANALYZED(decl) \ - if (Already_Analyzed(decl) == true) \ - return VISITOR_CONTINUE - - /* Special Traversal functions which marks if a Decl was already analyzed. - This macro generates them. */ -#define DEF_MARKING_TRAVERSE_DECL(DECL) \ - bool Traverse##DECL(DECL *decl) \ - { \ - DO_NOT_RUN_IF_ALREADY_ANALYZED(decl); \ - Mark_As_Analyzed(decl); \ - return RecursiveASTVisitor::Traverse##DECL(decl); \ - } - - /* Override of TraverseDecl that marks that the given Decl was analyzed. So - far it seems we only need this function for now. */ - DEF_MARKING_TRAVERSE_DECL(Decl); - - /* -------- C Declarations ----------------- */ - - bool VisitFunctionDecl(FunctionDecl *decl) - { - if (decl->getBuiltinID() != 0) { - /* Do not redeclare compiler builtin functions. */ - return VISITOR_CONTINUE; - } - - /* If this function is actually a template function specialization, then - look into it. */ - if (FunctionTemplateSpecializationInfo *ftsi = - decl->getTemplateSpecializationInfo()) { - TRY_TO(VisitFunctionTemplateDecl(ftsi->getTemplate())); - } - - /* Mark function for output. */ - FunctionDecl *to_mark = decl; - if (decl->isStatic()) { - FunctionDecl *definition = decl->getDefinition(); - /* FIXME: Declared static function in closure without a body? */ - if (definition != nullptr) { - to_mark = definition; - /* Make sure we parse the function version with a body. */ - TRY_TO(TraverseDecl(to_mark)); - } - } - - Closure.Add_Decl_And_Prevs(to_mark); - - return VISITOR_CONTINUE; - } - - bool VisitValueDecl(ValueDecl *decl) - { - TRY_TO(TraverseType(decl->getType())); - return VISITOR_CONTINUE; - } - - bool VisitRecordDecl(RecordDecl *decl) - { - /* If this is a C++ record decl, then analyze it as such. */ - if (CXXRecordDecl *cxxdecl = dyn_cast(decl)) { - TRY_TO(VisitCXXRecordDecl(cxxdecl)); - } - - if (TypedefNameDecl *typedecl = decl->getTypedefNameForAnonDecl()) { - /* In case this struct is defined without a name, such as: - struct { - int a; - }; - - then do not add it to the output. This can happen in the case where the - original code was declared as: - - typedef struct { int a; } A; */ - return VisitTypedefNameDecl(typedecl); - } else { - Closure.Add_Decl_And_Prevs(Maybe_Partial_Decl(decl)); - } - - return VISITOR_CONTINUE; - } - - bool VisitEnumDecl(EnumDecl *decl) - { - if (TypedefNameDecl *typedecl = decl->getTypedefNameForAnonDecl()) { - /* In case this enum is defined without a name, such as: - enum { - CONSTANT = 1, - }; - - then do not add it to the output. This can happen in the case where the - original code was declared as: - - typedef enum { CONSTANT = 1 } A; */ - return VisitTypedefNameDecl(typedecl); - } else { - Closure.Add_Decl_And_Prevs(decl); - } - - return VISITOR_CONTINUE; - } - - bool VisitTagDecl(TagDecl *decl) - { - DeclContext *parent = decl->getParent(); - if (parent && parent->hasValidDeclKind()) { - /* Check if parent declaration is a namespace. If yes, then add it - as well BUT DO NOT add its children. */ - if (parent->isNamespace()) { - NamespaceDecl *nm = cast(parent); - TRY_TO(VisitNamespaceDecl(nm)); - } - } - - return VISITOR_CONTINUE; - } - - bool VisitEnumConstantDecl(EnumConstantDecl *decl) - { - /* Add original EnumDecl it originated. */ - EnumDecl *enum_decl = EnumTable.Get(decl); - if (enum_decl) { - return TraverseDecl(enum_decl); - } - - return VISITOR_CONTINUE; - } - - /* Not called automatically by Transverse. */ - bool VisitTypedefNameDecl(TypedefNameDecl *decl) - { - TRY_TO(TraverseType(decl->getUnderlyingType())); - Closure.Add_Decl_And_Prevs(decl); - - return VISITOR_CONTINUE; - } - - bool VisitRecordType(const RecordType *type) - { - return VISITOR_CONTINUE; - } - - bool VisitVarDecl(VarDecl *decl) - { - /* Avoid adding variables that are not global. */ - if (decl->hasGlobalStorage()) { - Closure.Add_Decl_And_Prevs(decl); - } - - return VISITOR_CONTINUE; - } - - /* --------- C++ Declarations ---------- */ - - bool VisitFunctionTemplateDecl(FunctionTemplateDecl *decl) - { - Closure.Add_Decl_And_Prevs(decl); - - return VISITOR_CONTINUE; - } - - bool VisitTemplateDecl(TemplateDecl *decl) - { - Closure.Add_Decl_And_Prevs(decl); - - return VISITOR_CONTINUE; - } - - bool VisitNamespaceDecl(NamespaceDecl *decl) - { - Closure.Add_Decl_And_Prevs(decl); - - return VISITOR_CONTINUE; - } - - bool VisitCXXRecordDecl(CXXRecordDecl *decl) - { - /* In case this is a ClassTemplateSpecialDecl, then analyze it as such. */ - if (ClassTemplateSpecializationDecl *ctsd = - dyn_cast(decl)) { - TRY_TO(VisitClassTemplateSpecializationDecl(ctsd)); - } - - Closure.Add_Decl_And_Prevs(decl); - - return VISITOR_CONTINUE; - } - - bool VisitClassTemplateDecl(ClassTemplateDecl *decl) - { - Closure.Add_Decl_And_Prevs(decl); - - return VISITOR_CONTINUE; - } - - bool VisitClassTemplateSpecializationDecl(ClassTemplateSpecializationDecl *decl) - { - /* Assume - template - class V{ - T x; - }; - struct A {int x; } - - V u; - This call will add the A to the closure. - */ - TRY_TO(TraverseTemplateArguments(decl->getTemplateArgs().asArray())); - - /* This call will add the V to the closure. */ - return VisitClassTemplateDecl(decl->getSpecializedTemplate()); - } - - /* ----------- Statements -------------- */ - - bool VisitDeclRefExpr(DeclRefExpr *expr) - { - TRY_TO(TraverseDecl(expr->getDecl())); - /* For C++ we also need to analyze the name specifier. */ - if (NestedNameSpecifier *nns = expr->getQualifier()) { - TRY_TO(TraverseNestedNameSpecifier(nns)); - } - - /* Analyze the decl it references to. */ - return TraverseDecl(expr->getDecl()); - } - - bool VisitOffsetOfExpr(OffsetOfExpr *expr) - { - /* Handle the following case: - * - * typedef struct { - * int x; - * int y; - * } Head; - * - * int x = __builtin_offsetof(Head, y); - */ - unsigned num_components = expr->getNumComponents(); - for (unsigned i = 0; i < num_components; i++) { - /* Get the RecordDecl referenced in the builtin_offsetof and add to the - dependency list. */ - const OffsetOfNode &component = expr->getComponent(i); - if (component.getKind() == OffsetOfNode::Field) { - const FieldDecl *field = component.getField(); - - RecordDecl *record = (RecordDecl *)field->getParent(); - TRY_TO(TraverseDecl(record)); - } - } - - return VISITOR_CONTINUE; - } - - /* -------- Types ----------------- */ - bool VisitTagType(const TagType *type) - { - TRY_TO(TraverseDecl(type->getDecl())); - return VISITOR_CONTINUE; - } - - bool VisitTypedefType(const TypedefType *type) - { - TRY_TO(TraverseDecl(type->getDecl())); - return VISITOR_CONTINUE; - } - - /* -------- C++ Types ------------- */ - bool VisitTemplateSpecializationType(const TemplateSpecializationType *type) - { - /* For some reason the Traverse do not run on the original template - C++ Record, only on its specializations. Hence do it here. */ - return TraverseDecl(type->getAsCXXRecordDecl()); - } - - bool VisitDeducedTemplateSpecializationType( - const DeducedTemplateSpecializationType *type) - { - TemplateName template_name = type->getTemplateName(); - return TraverseDecl(template_name.getAsTemplateDecl()); - } - - bool VisitElaboratedType(const ElaboratedType *type) - { - return VISITOR_CONTINUE; - } - - /* ----------- Other C++ stuff ----------- */ - bool TraverseNestedNameSpecifier(NestedNameSpecifier *nns) - { - switch (nns->getKind()) { - case NestedNameSpecifier::Namespace: - /* Do not traverse to avoid adding unecessary childs. */ - TRY_TO(VisitNamespaceDecl(nns->getAsNamespace())); - break; - case NestedNameSpecifier::NamespaceAlias: - TRY_TO(TraverseDecl(nns->getAsNamespaceAlias())); - break; - - case NestedNameSpecifier::Super: - /* Something regarding MSVC, but lets put this here. */ - TRY_TO(TraverseDecl(nns->getAsRecordDecl())); - break; - - default: - break; - } - return RecursiveASTVisitor::TraverseNestedNameSpecifier(nns); - } - - bool TraverseCXXBaseSpecifier(const CXXBaseSpecifier base) - { - TRY_TO(TraverseType(base.getType())); - return RecursiveASTVisitor::TraverseCXXBaseSpecifier(base); - } - - - private: - /** The resulting closure set. */ - ClosureSet &Closure; - - /** The table maping EnumConstantDecl to its original EnumDecl, used to find - out where a certain EnumConstantDecl was defined. */ - EnumConstantTable &EnumTable; - - /** The set of all analyzed Decls. */ - std::unordered_set AnalyzedDecls; -}; - /** FunctionDependencyFinder class methods implementation. */ FunctionDependencyFinder::FunctionDependencyFinder(PassManager::Context *ctx) : AST(ctx->AST.get()), EnumTable(AST), IT(AST, ctx->IncExpansionPolicy, ctx->HeadersToExpand), KeepIncludes(ctx->KeepIncludes), - Visitor(new DeclClosureVisitor(Closure, EnumTable)) -{ -} - -FunctionDependencyFinder::~FunctionDependencyFinder(void) + Visitor(AST, EnumTable) { - delete Visitor; } bool FunctionDependencyFinder::Find_Functions_Required( std::vector const &funcnames) { - /* FIXME: clang has a mechanism (DeclContext::lookup) which is SUPPOSED TO - return the list of all Decls that matches the lookup name. However, this - method doesn't work as intented. In kernel (see github issue #20) it - results in the lookup_result set not having the Decl that has the body of - the function, which is the most important! - - Hence, do our own thing here. Create a set and sweep the top level decls, - analyzing the symbol that matches the name in the set. */ - - std::unordered_set setof_names(std::make_move_iterator(funcnames.begin()), - std::make_move_iterator(funcnames.end())); - std::unordered_set matched_names; - - for (auto it = AST->top_level_begin(); it != AST->top_level_end(); ++it) { - NamedDecl *decl = dyn_cast(*it); - - if (!decl) { - /* Decl does not have a name, thus skip it. */ - continue; - } - - const std::string &decl_name = decl->getName().str(); - /* If the symbol name is in the set... */ - if (setof_names.find(decl_name) != setof_names.end()) { - /* Mark that name as matched. */ - matched_names.insert(decl_name); - /* Find its dependencies. */ - Visitor->TraverseDecl(decl); - } - } + Visitor.Compute_Closure_Of_Symbols(funcnames, &matched_names); /* Find which names did not match any declaration name. */ for (const std::string &funcname : funcnames) { @@ -564,11 +50,14 @@ bool FunctionDependencyFinder::Find_Functions_Required( void FunctionDependencyFinder::Print(void) { - RecursivePrint(AST, Closure.Get_Set(), IT, KeepIncludes).Print(); + ClosureSet &closure = Visitor.Get_Closure(); + RecursivePrint(AST, closure.Get_Set(), IT, KeepIncludes).Print(); } -void FunctionDependencyFinder::Remove_Redundant_Decls(void) { - std::unordered_set &closure_set = Closure.Get_Set(); +void FunctionDependencyFinder::Remove_Redundant_Decls(void) +{ + ClosureSet &closure = Visitor.Get_Closure(); + std::unordered_set &closure_set = closure.Get_Set(); for (auto it = closure_set.begin(); it != closure_set.end(); ++it) { /* Handle the case where a enum or struct is declared as: @@ -609,7 +98,7 @@ void FunctionDependencyFinder::Remove_Redundant_Decls(void) { if (type) { TagDecl *typedecl = type->getAsTagDecl(); - if (typedecl && Closure.Is_Decl_Marked(typedecl)) { + if (typedecl && closure.Is_Decl_Marked(typedecl)) { SourceRange type_range = typedecl->getSourceRange(); /* Check if the strings regarding an decl empty. In that case we @@ -619,7 +108,7 @@ void FunctionDependencyFinder::Remove_Redundant_Decls(void) { /* Using .fullyContains() fails in some declarations. */ if (PrettyPrint::Contains_From_LineCol(range, type_range)) { - Closure.Remove_Decl(typedecl); + closure.Remove_Decl(typedecl); } } } @@ -687,12 +176,12 @@ void FunctionDependencyFinder::Remove_Redundant_Decls(void) { } TagDecl *typedecl = type ? type->getAsTagDecl() : nullptr; - if (typedecl && Closure.Is_Decl_Marked(typedecl)) { + if (typedecl && closure.Is_Decl_Marked(typedecl)) { SourceRange type_range = typedecl->getSourceRange(); /* Using .fullyContains() fails in some declarations. */ if (PrettyPrint::Contains_From_LineCol(range, type_range)) { - Closure.Remove_Decl(typedecl); + closure.Remove_Decl(typedecl); } } } @@ -703,7 +192,7 @@ void FunctionDependencyFinder::Remove_Redundant_Decls(void) { TypedefDecl *prev = nullptr; for (it = AST->top_level_begin(); it != AST->top_level_end(); ++it) { if (TypedefDecl *decl = dyn_cast(*it)) { - if (!Closure.Is_Decl_Marked(decl)) + if (!closure.Is_Decl_Marked(decl)) continue; // Set prev and exit, since we don't have anything to compare agains't @@ -744,7 +233,7 @@ void FunctionDependencyFinder::Remove_Redundant_Decls(void) { * different ending, remove the prev from the closure and set the * new prev. */ - Closure.Remove_Decl(prev); + closure.Remove_Decl(prev); } prev = decl; @@ -762,7 +251,7 @@ void FunctionDependencyFinder::Insert_Decls_From_Non_Expanded_Includes(void) IncludeNode *node = IT.Get(loc); if (node && node->Has_Parent_Marked_For_Output()) { - Visitor->TraverseDecl(decl); + Visitor.TraverseDecl(decl); } } } diff --git a/libcextract/FunctionDepsFinder.hh b/libcextract/FunctionDepsFinder.hh index 928e859..3f662cb 100644 --- a/libcextract/FunctionDepsFinder.hh +++ b/libcextract/FunctionDepsFinder.hh @@ -19,6 +19,7 @@ #include "MacroWalker.hh" #include "IncludeTree.hh" #include "PrettyPrint.hh" +#include "Closure.hh" #include "Passes.hh" #include @@ -27,43 +28,6 @@ using namespace clang; -class ClosureSet -{ - public: - /** Check if a given declaration was already marked as dependency. */ - inline bool Is_Decl_Marked(Decl *decl) - { return Dependencies.find(decl) != Dependencies.end(); } - - /** Mark decl as dependencies and all its previous decls versions. */ - bool Add_Decl_And_Prevs(Decl *decl); - - /** Add a single decl to the set. */ - void Add_Single_Decl(Decl *decl) - { - Dependencies.insert(decl); - } - - inline std::unordered_set &Get_Set(void) - { - return Dependencies; - } - - inline void Remove_Decl(Decl *decl) - { - Dependencies.erase(decl); - } - - private: - /** Datastructure holding all Decls required for the functions. This is - then used to mark which Decls we need to output. - - We use a unordered_set because we need quick lookup, so a hash table - may be (?) the ideal datastructure for this. */ - std::unordered_set Dependencies; -}; - -class DeclClosureVisitor; - /** Function Dependency Finder. * * This class wraps all code necessary to find all Decl in the @@ -93,7 +57,6 @@ class FunctionDependencyFinder { public: FunctionDependencyFinder(PassManager::Context *); - ~FunctionDependencyFinder(void); /** Print the marked nodes as they appear in the AST. */ void Print(void); @@ -116,13 +79,6 @@ class FunctionDependencyFinder /* Remove redundant decls, whose source location is inside another decl. */ void Remove_Redundant_Decls(); - /** Datastructure holding all Decls required for the functions. This is - then used to mark which Decls we need to output. - - We use a unordered_set because we need quick lookup, so a hash table - may be (?) the ideal datastructure for this. */ - ClosureSet Closure; - /** The AST that are being used in our analysis. */ ASTUnit* AST; @@ -138,5 +94,5 @@ class FunctionDependencyFinder /* Visitor that sweeps through the AST. Kept as pointer to avoid declaring the DeclClosureVisitor class into this .h to speedup build time. */ - DeclClosureVisitor *Visitor; + DeclClosureVisitor Visitor; }; diff --git a/libcextract/meson.build b/libcextract/meson.build index 434fea5..b9915cc 100644 --- a/libcextract/meson.build +++ b/libcextract/meson.build @@ -33,7 +33,8 @@ libcextract_sources = [ 'SymversParser.cpp', 'TopLevelASTIterator.cpp', 'ExpansionPolicy.cpp', - 'HeaderGenerate.cpp' + 'HeaderGenerate.cpp', + 'Closure.cpp' ] libcextract_static = static_library('cextract', libcextract_sources) diff --git a/testsuite/lateext/lateext-6.c b/testsuite/lateext/lateext-6.c new file mode 100644 index 0000000..dfde21f --- /dev/null +++ b/testsuite/lateext/lateext-6.c @@ -0,0 +1,15 @@ +/* { dg-options "-DCE_EXTRACT_FUNCTIONS=f -DCE_LATE_EXTERNALIZE -DCE_EXPORT_SYMBOLS=g -DCE_KEEP_INCLUDES" }*/ + +int g(void) +{ + return 0; +} + +int f(void) +{ + return g(); +} + +/* { dg-final { scan-tree-dump-not "int g\(void\)" } } */ +/* { dg-final { scan-tree-dump "static int \(\*klpe_g\)\(void\);" } } */ +/* { dg-final { scan-tree-dump "return \(\*klpe_g\)\(\);" } } */