File indexing completed on 2024-05-19 05:42:04

0001 // ct_lvtclp_staticfnhandler.cpp                                      -*-C++-*-
0002 
0003 /*
0004 // Copyright 2023 Codethink Ltd <codethink@codethink.co.uk>
0005 // SPDX-License-Identifier: Apache-2.0
0006 //
0007 // Licensed under the Apache License, Version 2.0 (the "License");
0008 // you may not use this file except in compliance with the License.
0009 // You may obtain a copy of the License at
0010 //
0011 //     http://www.apache.org/licenses/LICENSE-2.0
0012 //
0013 // Unless required by applicable law or agreed to in writing, software
0014 // distributed under the License is distributed on an "AS IS" BASIS,
0015 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0016 // See the License for the specific language governing permissions and
0017 // limitations under the License.
0018 */
0019 
0020 #include <ct_lvtclp_staticfnhandler.h>
0021 
0022 #include <ct_lvtclp_clputil.h>
0023 
0024 #include <clang/AST/DeclTemplate.h>
0025 
0026 #include <cassert>
0027 #include <optional>
0028 #include <string>
0029 #include <unordered_map>
0030 #include <unordered_set>
0031 
0032 namespace {
0033 
0034 using FnId = std::size_t;
0035 // std::hash of the qualified name of the function plus arg types.
0036 // This whole data structure is per-file so we won't be confused by static
0037 // functions.
0038 
0039 FnId funcDeclToId(const clang::FunctionDecl *decl)
0040 // To uniquely identify a function we need its template parameters, qualified
0041 // name and argument types (because of overloading)
0042 {
0043     std::string ret;
0044 
0045     // template parameters
0046     if (const clang::FunctionTemplateDecl *templateDecl = decl->getDescribedFunctionTemplate()) {
0047         // template declaration
0048         ret += "template <";
0049         bool first = true;
0050         for (const clang::NamedDecl *namedDecl : *templateDecl->getTemplateParameters()) {
0051             if (first) {
0052                 first = false;
0053             } else {
0054                 ret += ", ";
0055             }
0056 
0057             ret += "typename ";
0058             ret += namedDecl->getQualifiedNameAsString();
0059         }
0060         ret += "> ";
0061     } else if (const auto *spec = decl->getTemplateSpecializationInfo()) {
0062         // template specialization
0063         ret += "template <";
0064         bool first = true;
0065         const clang::TemplateArgumentList *list = spec->TemplateArguments;
0066         for (unsigned i = 0; i < list->size(); ++i) {
0067             if (first) {
0068                 first = false;
0069             } else {
0070                 ret += ", ";
0071             }
0072 
0073             const clang::TemplateArgument& arg = (*list)[i];
0074             llvm::raw_string_ostream s(ret);
0075             arg.dump(s);
0076         }
0077         ret += "> ";
0078     }
0079 
0080     ret += decl->getQualifiedNameAsString();
0081 
0082     // parameter types
0083     ret += '(';
0084     bool first = true;
0085     for (const clang::ParmVarDecl *param : decl->parameters()) {
0086         if (first) {
0087             first = false;
0088         } else {
0089             ret += ", ";
0090         }
0091 
0092         ret += param->getType().getAsString();
0093     }
0094     ret += ')';
0095 
0096     // store hashes of the qualified name string instead of the string itself.
0097     // this makes debugging hard but should run much faster and use much less
0098     // memory. Otherwise we run out of memory building bde database for
0099     // bde/thirdparty/pcre2
0100     std::hash<std::string> hash;
0101     return hash(ret);
0102 }
0103 
0104 } // end unnamed namespace
0105 
0106 namespace Codethink::lvtclp {
0107 
0108 struct StaticFnHandler::Private {
0109     lvtmdb::ObjectStore& d_memDb;
0110 
0111     std::unordered_map<FnId, std::unordered_set<lvtmdb::TypeObject *>> fnDepUdt;
0112     // stores dependency relationships from functions to UDTs
0113     std::unordered_map<FnId, std::unordered_set<FnId>> fnDepFn;
0114     // stores dependency relationships from functions to functions
0115     std::unordered_map<lvtmdb::TypeObject *, std::unordered_set<FnId>> udtDepFn;
0116     // stores dependency relationships from UDTs to functions
0117     std::unordered_map<lvtmdb::FunctionObject *, std::unordered_set<lvtmdb::FunctionObject *>> callgraphDeps;
0118     // stores the callgraph dependency for functions
0119 
0120     std::unordered_set<FnId> visited;
0121     // used in flattenFn
0122 
0123     explicit Private(lvtmdb::ObjectStore& memDb): d_memDb(memDb)
0124     {
0125     }
0126 
0127     void clear()
0128     {
0129         fnDepUdt.clear();
0130         fnDepFn.clear();
0131         udtDepFn.clear();
0132         visited.clear();
0133         callgraphDeps.clear();
0134     }
0135 };
0136 
0137 StaticFnHandler::StaticFnHandler(lvtmdb::ObjectStore& memDb): d(std::make_unique<StaticFnHandler::Private>(memDb))
0138 {
0139 }
0140 
0141 StaticFnHandler::~StaticFnHandler() noexcept = default;
0142 
0143 void StaticFnHandler::addFnUses(const clang::FunctionDecl *decl, lvtmdb::TypeObject *udt)
0144 {
0145     FnId fnId = funcDeclToId(decl);
0146     d->fnDepUdt[fnId].insert(udt);
0147 }
0148 
0149 void StaticFnHandler::addFnUses(const clang::FunctionDecl *decl, const clang::FunctionDecl *dep)
0150 {
0151     FnId fnId = funcDeclToId(decl);
0152     FnId depId = funcDeclToId(dep);
0153     if (fnId != depId) {
0154         d->fnDepFn[fnId].insert(depId);
0155     }
0156 }
0157 
0158 void StaticFnHandler::addCallgraphDep(lvtmdb::FunctionObject *source_f, lvtmdb::FunctionObject *target_f)
0159 {
0160     if (!source_f || !target_f || source_f == target_f) {
0161         return;
0162     }
0163 
0164     d->callgraphDeps[source_f].insert(target_f);
0165 }
0166 
0167 void StaticFnHandler::addUdtUses(lvtmdb::TypeObject *udt, const clang::FunctionDecl *decl)
0168 {
0169     FnId fnId = funcDeclToId(decl);
0170     d->udtDepFn[udt].insert(fnId);
0171 }
0172 
0173 void StaticFnHandler::flattenFn(const FnId sourceFn)
0174 {
0175     if (d->visited.find(sourceFn) != d->visited.end()) {
0176         // don't recurse forever for circular function dependencies
0177         return;
0178     }
0179     d->visited.insert(sourceFn);
0180 
0181     // for each function used by sourceFn
0182     for (const FnId fnDep : d->fnDepFn[sourceFn]) {
0183         if (fnDep == sourceFn) {
0184             continue;
0185         }
0186 
0187         // first we need to flatten the fn dependency to make sure it has all of
0188         // its udts
0189         flattenFn(fnDep);
0190 
0191         // for each udt used by the function dependency
0192         for (lvtmdb::TypeObject *udtDep : d->fnDepUdt[fnDep]) {
0193             // add as a dependency of sourceFn
0194             d->fnDepUdt[sourceFn].insert(udtDep);
0195         }
0196     }
0197 }
0198 
0199 void StaticFnHandler::writeOutToDb()
0200 {
0201     // Save function dependencies
0202     for (auto const& [sourceFn, targetFns] : d->callgraphDeps) {
0203         for (auto const& targetFn : targetFns) {
0204             ClpUtil::addFnDependency(sourceFn, targetFn);
0205         }
0206     }
0207 
0208     // short circuit to skip flattening functions where we would end up with
0209     // nothing to write to the database anyway
0210     if (d->udtDepFn.empty() || d->fnDepUdt.empty()) {
0211         return;
0212     }
0213 
0214     // for each function with a function dependency, flatten out the fn dep
0215     // so we only have fn -> udt dependencies
0216     for (const auto& [sourceFn, _] : d->fnDepFn) {
0217         flattenFn(sourceFn);
0218     }
0219 
0220     // for each udt -> func dependency
0221     for (const auto& [sourceUdt, fnIds] : d->udtDepFn) {
0222         for (const FnId fnId : fnIds) {
0223             // for each udt dependency of that func
0224             for (lvtmdb::TypeObject *targetUdt : d->fnDepUdt[fnId]) {
0225                 // sourceUdt -> fnId -> targetUdt
0226                 // add udt -> udt dependency to db
0227                 ClpUtil::addUsesInImpl(sourceUdt, targetUdt);
0228             }
0229         }
0230     }
0231 }
0232 
0233 void StaticFnHandler::reset()
0234 {
0235     d->clear();
0236 }
0237 
0238 } // end namespace Codethink::lvtclp