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