From dcc46f7b4509a371573e36957a3fd699f0548f9e Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Tue, 19 Dec 2023 15:36:17 +0000 Subject: [PATCH] oilgen: migrate to source parsing WIP: This change needs to be integrated with the internal build system before landing. Using debug information generated from partial source (that is, not the final binary) has been insufficient to generally generate OIL code. A particular example is pointers to templates: ```cpp #include template struct Foo { T t; }; template struct Bar { Foo& f; }; void foo(const Bar& b) { oi::introspect(b); } ``` The pointer/reference to `Foo` appears in DWARF with `DW_AT_declaration(true)` because it could be specialised before its usage. However, with OIL, we are creating an implicit usage site in the `oi::introspect` call that the compiler is unable to see. This change reworks OILGen to work from a Clang command line rather than debug information. We setup and run a compiler on the source, giving us access to an AST and Semantic Analyser. We then: - Find the `oi::introspect` template. - Iterate through each of its callsites for their type. - Run `ClangTypeParser::parse` on each type. - Run codegen. - Compile into an object file. Having access to the semantic analyser allows us to forcefully complete a type, as it would be if it was used in the initial code. Test Plan: - TBD --- CMakeLists.txt | 3 +- oi/CodeGen.cpp | 30 +- oi/CodeGen.h | 6 +- oi/Config.cpp | 9 +- oi/ContainerInfo.cpp | 2 +- oi/OICodeGen.h | 2 +- oi/OIGenerator.cpp | 370 +++++++++------- oi/OIGenerator.h | 29 +- oi/type_graph/CMakeLists.txt | 2 + oi/type_graph/ClangTypeParser.cpp | 416 ++++++++++++++++++ oi/type_graph/ClangTypeParser.h | 131 ++++++ oi/type_graph/DrgnExporter.cpp | 8 + oi/type_graph/DrgnExporter.h | 1 + oi/type_graph/NameGen.cpp | 8 + oi/type_graph/NameGen.h | 1 + oi/type_graph/Printer.cpp | 11 + oi/type_graph/Printer.h | 1 + oi/type_graph/RemoveTopLevelPointer.cpp | 4 + oi/type_graph/RemoveTopLevelPointer.h | 1 + oi/type_graph/Types.h | 56 +++ oi/type_graph/Visitor.h | 7 + test/integration/template_template_param.toml | 16 + tools/OILGen.cpp | 148 +++---- types/string_type.toml | 31 ++ 24 files changed, 1004 insertions(+), 289 deletions(-) create mode 100644 oi/type_graph/ClangTypeParser.cpp create mode 100644 oi/type_graph/ClangTypeParser.h create mode 100644 test/integration/template_template_param.toml diff --git a/CMakeLists.txt b/CMakeLists.txt index d7f9ee7..b6fecd1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -289,7 +289,7 @@ add_library(oicore oi/Serialize.cpp ) add_dependencies(oicore libdrgn) -target_include_directories(oicore SYSTEM PRIVATE ${LLVM_INCLUDE_DIRS} ${CLANG_INCLUDE_DIRS}) +target_include_directories(oicore SYSTEM PUBLIC ${LLVM_INCLUDE_DIRS} ${CLANG_INCLUDE_DIRS}) target_compile_definitions(oicore PRIVATE ${LLVM_DEFINITIONS}) target_include_directories(oicore PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) @@ -365,6 +365,7 @@ add_executable(oilgen target_link_libraries(oilgen drgn_utils oicore + clangTooling ) ### Object Introspection cache Printer (OIP) diff --git a/oi/CodeGen.cpp b/oi/CodeGen.cpp index 74c6f39..869fc20 100644 --- a/oi/CodeGen.cpp +++ b/oi/CodeGen.cpp @@ -299,6 +299,10 @@ void genDefsThriftClass(const Class& c, std::string& code) { } // namespace +CodeGen::CodeGen(const OICodeGen::Config& config) : config_(config) { + DCHECK(!config.features[Feature::PolymorphicInheritance]) << "polymorphic inheritance requires symbol service!"; +} + void CodeGen::genDefsThrift(const TypeGraph& typeGraph, std::string& code) { for (const Type& t : typeGraph.finalTypes) { if (const auto* c = dynamic_cast(&t)) { @@ -542,7 +546,7 @@ void CodeGen::getClassSizeFuncDef(const Class& c, std::string& code) { std::string childVtableName = "vtable for "; childVtableName += fqChildName; - auto optVtableSym = symbols_.locateSymbol(childVtableName, true); + auto optVtableSym = symbols_->locateSymbol(childVtableName, true); if (!optVtableSym) { // LOG(ERROR) << "Failed to find vtable address for '" << // childVtableName; LOG(ERROR) << "Falling back to non dynamic @@ -1115,15 +1119,8 @@ bool CodeGen::codegenFromDrgn(struct drgn_type* drgnType, std::string& code) { bool CodeGen::codegenFromDrgn(struct drgn_type* drgnType, std::string& code, RootFunctionName name) { - try { - containerInfos_.reserve(config_.containerConfigPaths.size()); - for (const auto& path : config_.containerConfigPaths) { - registerContainer(path); - } - } catch (const ContainerInfoError& err) { - LOG(ERROR) << "Error reading container TOML file " << err.what(); + if (!registerContainers()) return false; - } try { addDrgnRoot(drgnType, typeGraph_); @@ -1148,6 +1145,19 @@ void CodeGen::exportDrgnTypes(TypeHierarchy& th, } } +bool CodeGen::registerContainers() { + try { + containerInfos_.reserve(config_.containerConfigPaths.size()); + for (const auto& path : config_.containerConfigPaths) { + registerContainer(path); + } + return true; + } catch (const ContainerInfoError& err) { + LOG(ERROR) << "Error reading container TOML file " << err.what(); + return false; + } +} + void CodeGen::registerContainer(std::unique_ptr info) { VLOG(1) << "Registered container: " << info->typeName; containerInfos_.emplace_back(std::move(info)); @@ -1189,7 +1199,7 @@ void CodeGen::transform(TypeGraph& typeGraph) { .chaseRawPointers = config_.features[Feature::ChaseRawPointers], }; DrgnParser drgnParser{typeGraph, options}; - pm.addPass(AddChildren::createPass(drgnParser, symbols_)); + pm.addPass(AddChildren::createPass(drgnParser, *symbols_)); // Re-run passes over newly added children pm.addPass(IdentifyContainers::createPass(containerInfos_)); diff --git a/oi/CodeGen.h b/oi/CodeGen.h index 9c76238..931aa83 100644 --- a/oi/CodeGen.h +++ b/oi/CodeGen.h @@ -41,8 +41,9 @@ namespace oi::detail { class CodeGen { public: + CodeGen(const OICodeGen::Config& config); CodeGen(const OICodeGen::Config& config, SymbolService& symbols) - : config_(config), symbols_(symbols) { + : config_(config), symbols_(&symbols) { } struct ExactName { @@ -65,6 +66,7 @@ class CodeGen { std::list& drgnTypes, drgn_type** rootType) const; + bool registerContainers(); void registerContainer(std::unique_ptr containerInfo); void registerContainer(const std::filesystem::path& path); void addDrgnRoot(struct drgn_type* drgnType, @@ -77,7 +79,7 @@ class CodeGen { private: type_graph::TypeGraph typeGraph_; const OICodeGen::Config& config_; - SymbolService& symbols_; + SymbolService* symbols_; std::vector> containerInfos_; std::unordered_set definedContainers_; std::unordered_map diff --git a/oi/Config.cpp b/oi/Config.cpp index 157b279..66798bb 100644 --- a/oi/Config.cpp +++ b/oi/Config.cpp @@ -22,6 +22,8 @@ #include #include #include +#include +#include #include "oi/support/Toml.h" @@ -54,6 +56,9 @@ std::optional processConfigFiles( enables |= *fs; } + ranges::actions::sort(generatorConfig.containerConfigPaths); + ranges::actions::unique(generatorConfig.containerConfigPaths); + // Override anything from the config files with command line options for (auto [k, v] : featureMap) { enables[k] = v; @@ -108,8 +113,8 @@ std::optional processConfigFile( if the right path is absolute, else append the right path to the left path. */ - generatorConfig.containerConfigPaths.emplace(configDirectory / - el.get()); + generatorConfig.containerConfigPaths.emplace_back(configDirectory / + el.get()); } }); } diff --git a/oi/ContainerInfo.cpp b/oi/ContainerInfo.cpp index 4cb20b8..b83b7e4 100644 --- a/oi/ContainerInfo.cpp +++ b/oi/ContainerInfo.cpp @@ -202,7 +202,7 @@ std::regex getMatcher(const std::string& typeName) { ContainerInfo::ContainerInfo(const fs::path& path) { toml::table container; try { - container = toml::parse_file(std::string(path)); + container = toml::parse_file(path.string()); } catch (const toml::parse_error& err) { // Convert into a ContainerInfoError, just to avoid having to include // the huge TOML++ header in the caller's file. Use toml::parse_error's diff --git a/oi/OICodeGen.h b/oi/OICodeGen.h index a01548f..7226a4e 100644 --- a/oi/OICodeGen.h +++ b/oi/OICodeGen.h @@ -66,7 +66,7 @@ class OICodeGen { }; FeatureSet features; - std::set containerConfigPaths; + std::vector containerConfigPaths; std::set defaultHeaders; std::set defaultNamespaces; std::vector> membersToStub; diff --git a/oi/OIGenerator.cpp b/oi/OIGenerator.cpp index 1cb2a49..cf7ec44 100644 --- a/oi/OIGenerator.cpp +++ b/oi/OIGenerator.cpp @@ -16,130 +16,156 @@ #include "oi/OIGenerator.h" +#include +#include +#include +#include +#include +#include #include #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // TODO: remove #include #include #include #include "oi/CodeGen.h" #include "oi/Config.h" -#include "oi/DrgnUtils.h" #include "oi/Headers.h" +#include "oi/type_graph/ClangTypeParser.h" +#include "oi/type_graph/Printer.h" +#include "oi/type_graph/TypeGraph.h" +#include "oi/type_graph/Types.h" namespace oi::detail { +namespace { -std::unordered_map -OIGenerator::oilStrongToWeakSymbolsMap(drgnplusplus::program& prog) { - static constexpr std::string_view strongSymbolPrefix = - "oi::IntrospectionResult oi::introspect<"; - static constexpr std::string_view weakSymbolPrefix = - "oi::IntrospectionResult oi::introspectImpl<"; +class ConsumerContext; - std::unordered_map> - templateArgsToSymbolsMap; - - auto symbols = prog.find_all_symbols(); - for (drgn_symbol* sym : *symbols) { - auto symName = drgnplusplus::symbol::name(sym); - if (symName == nullptr || *symName == '\0') - continue; - auto demangled = boost::core::demangle(symName); - - if (demangled.starts_with(strongSymbolPrefix)) { - auto& matchedSyms = templateArgsToSymbolsMap[demangled.substr( - strongSymbolPrefix.length())]; - if (!matchedSyms.first.empty()) { - LOG(WARNING) << "non-unique symbols found: `" << matchedSyms.first - << "` and `" << symName << '`'; - } - matchedSyms.first = symName; - } else if (demangled.starts_with(weakSymbolPrefix)) { - auto& matchedSyms = - templateArgsToSymbolsMap[demangled.substr(weakSymbolPrefix.length())]; - if (!matchedSyms.second.empty()) { - LOG(WARNING) << "non-unique symbols found: `" << matchedSyms.second - << "` and `" << symName << "`"; - } - matchedSyms.second = symName; - } +class CreateTypeGraphConsumer; +class CreateTypeGraphAction : public clang::ASTFrontendAction { + public: + CreateTypeGraphAction(ConsumerContext& ctx_) : ctx{ctx_} { } - std::unordered_map strongToWeakSymbols; - for (auto& [_, val] : templateArgsToSymbolsMap) { - if (val.first.empty() || val.second.empty()) { - continue; - } - strongToWeakSymbols[std::move(val.first)] = std::move(val.second); + void ExecuteAction() override; + std::unique_ptr CreateASTConsumer( + clang::CompilerInstance& CI, clang::StringRef file) override; + + private: + ConsumerContext& ctx; +}; + +class CreateTypeGraphActionFactory + : public clang::tooling::FrontendActionFactory { + public: + CreateTypeGraphActionFactory(ConsumerContext& ctx_) : ctx{ctx_} { } - return strongToWeakSymbols; -} + std::unique_ptr create() override { + return std::make_unique(ctx); + } -std::unordered_map -OIGenerator::findOilTypesAndNames(drgnplusplus::program& prog) { - auto strongToWeakSymbols = oilStrongToWeakSymbolsMap(prog); + private: + ConsumerContext& ctx; +}; - std::unordered_map out; +class ConsumerContext { + public: + ConsumerContext(const std::vector>& cis) + : containerInfos{cis} { + } - for (drgn_qualified_type& func : drgnplusplus::func_iterator(prog)) { - std::string strongLinkageName; - { - const char* linkageNameCstr; - if (auto err = drgnplusplus::error( - drgn_type_linkage_name(func.type, &linkageNameCstr))) { - // throw err; + type_graph::TypeGraph typeGraph; + std::unordered_map nameToTypeMap; + std::optional pic; + const std::vector>& containerInfos; + + private: + clang::Sema* sema = nullptr; + friend CreateTypeGraphConsumer; + friend CreateTypeGraphAction; +}; + +} // namespace + +int OIGenerator::generate(clang::tooling::CompilationDatabase& db, + const std::vector& sourcePaths) { + std::map featuresMap = { + {Feature::TypeGraph, true}, + {Feature::TreeBuilderV2, true}, + {Feature::Library, true}, + {Feature::PackStructs, true}, + {Feature::PruneTypeGraph, true}, + }; + + OICodeGen::Config generatorConfig{}; + OICompiler::Config compilerConfig{}; + + auto features = config::processConfigFiles( + configFilePaths, featuresMap, compilerConfig, generatorConfig); + if (!features) { + LOG(ERROR) << "failed to process config file"; + return -1; + } + generatorConfig.features = *features; + compilerConfig.features = *features; + + std::vector> containerInfos; + containerInfos.reserve(generatorConfig.containerConfigPaths.size()); + try { + for (const auto& path : generatorConfig.containerConfigPaths) { + auto info = std::make_unique(path); + if (info->requiredFeatures != (*features & info->requiredFeatures)) { + VLOG(1) << "Skipping container (feature conflict): " << info->typeName; continue; } - strongLinkageName = linkageNameCstr; + containerInfos.emplace_back(std::move(info)); } - - std::string weakLinkageName; - if (auto search = strongToWeakSymbols.find(strongLinkageName); - search != strongToWeakSymbols.end()) { - weakLinkageName = search->second; - } else { - continue; // not an oil strong symbol - } - - // IntrospectionResult (*)(const T&) - CHECK(drgn_type_has_parameters(func.type)) << "functions have parameters"; - CHECK(drgn_type_num_parameters(func.type) == 1) - << "introspection func has one parameter"; - - auto* params = drgn_type_parameters(func.type); - drgn_qualified_type tType; - if (auto err = - drgnplusplus::error(drgn_parameter_type(¶ms[0], &tType))) { - throw err; - } - - if (drgn_type_has_name(tType.type)) { - LOG(INFO) << "found OIL type: " << drgn_type_name(tType.type); - } else { - LOG(INFO) << "found OIL type: (no name)"; - } - out.emplace(std::move(weakLinkageName), tType); + } catch (const ContainerInfoError& err) { + LOG(ERROR) << "Error reading container TOML file " << err.what(); + return -1; } - return out; -} + ConsumerContext ctx{containerInfos}; + CreateTypeGraphActionFactory factory{ctx}; -fs::path OIGenerator::generateForType(const OICodeGen::Config& generatorConfig, - const OICompiler::Config& compilerConfig, - const drgn_qualified_type& type, - const std::string& linkageName, - SymbolService& symbols) { - CodeGen codegen{generatorConfig, symbols}; + clang::tooling::ClangTool tool{db, sourcePaths}; + if (auto ret = tool.run(&factory); ret != 0) { + return ret; + } + + if (ctx.nameToTypeMap.size() > 1) + throw std::logic_error( + "found more than one type to generate for but we can't currently " + "handle this case"); + + if (ctx.nameToTypeMap.empty()) { + LOG(ERROR) << "Nothing to generate!"; + return -1; + } + const auto& linkageName = ctx.nameToTypeMap.begin()->first; + + compilerConfig.usePIC = ctx.pic.value(); + CodeGen codegen{generatorConfig}; + for (auto&& ptr : containerInfos) + codegen.registerContainer(std::move(ptr)); + codegen.transform(ctx.typeGraph); std::string code; - if (!codegen.codegenFromDrgn(type.type, linkageName, code)) { - LOG(ERROR) << "codegen failed!"; - return {}; - } + codegen.generate(ctx.typeGraph, code, CodeGen::ExactName{linkageName}); std::string sourcePath = sourceFileDumpPath; if (sourceFileDumpPath.empty()) { @@ -152,78 +178,110 @@ fs::path OIGenerator::generateForType(const OICodeGen::Config& generatorConfig, } OICompiler compiler{{}, compilerConfig}; - - // TODO: Revert to outputPath and remove printing when typegraph is done. - fs::path tmpObject = outputPath; - tmpObject.replace_extension( - "." + std::to_string(std::hash{}(linkageName)) + ".o"); - - if (!compiler.compile(code, sourcePath, tmpObject)) { - return {}; - } - return tmpObject; + return compiler.compile(code, sourcePath, outputPath) ? 0 : -1; } -int OIGenerator::generate(fs::path& primaryObject, SymbolService& symbols) { - drgnplusplus::program prog; +namespace { - { - std::array objectPaths = {{primaryObject.c_str()}}; - if (auto err = drgnplusplus::error( - drgn_program_load_debug_info(prog.get(), - std::data(objectPaths), - std::size(objectPaths), - false, - false))) { - LOG(ERROR) << "error loading debug info program: " << err; - throw err; +class CreateTypeGraphConsumer : public clang::ASTConsumer { + private: + ConsumerContext& ctx; + + public: + CreateTypeGraphConsumer(ConsumerContext& ctx_) : ctx(ctx_) { + } + + void HandleTranslationUnit(clang::ASTContext& Context) override { + auto* tu_decl = Context.getTranslationUnitDecl(); + auto decls = tu_decl->decls(); + auto oi_namespaces = decls | ranges::views::transform([](auto* p) { + return llvm::dyn_cast(p); + }) | + ranges::views::filter([](auto* ns) { + return ns != nullptr && ns->getName() == "oi"; + }); + if (oi_namespaces.empty()) { + LOG(WARNING) << "Failed to find `oi` namespace. Does this input " + "include ?"; + return; } - } - auto oilTypes = findOilTypesAndNames(prog); - - std::map featuresMap = { - {Feature::TypeGraph, true}, - {Feature::TreeBuilderV2, true}, - {Feature::Library, true}, - {Feature::PackStructs, true}, - {Feature::PruneTypeGraph, true}, - }; - - OICodeGen::Config generatorConfig{}; - OICompiler::Config compilerConfig{}; - compilerConfig.usePIC = pic; - - auto features = config::processConfigFiles( - configFilePaths, featuresMap, compilerConfig, generatorConfig); - if (!features) { - LOG(ERROR) << "failed to process config file"; - return -1; - } - generatorConfig.features = *features; - compilerConfig.features = *features; - - size_t failures = 0; - for (const auto& [linkageName, type] : oilTypes) { - if (auto obj = generateForType( - generatorConfig, compilerConfig, type, linkageName, symbols); - !obj.empty()) { - std::cout << obj.string() << std::endl; - } else { - LOG(WARNING) << "failed to generate for symbol `" << linkageName - << "`. this is non-fatal but the call will not work."; - failures++; + auto introspectImpl = + std::move(oi_namespaces) | + ranges::views::for_each([](auto* ns) { return ns->decls(); }) | + ranges::views::transform([](auto* p) { + return llvm::dyn_cast(p); + }) | + ranges::views::filter([](auto* td) { + return td != nullptr && td->getName() == "introspectImpl"; + }) | + ranges::views::take(1) | ranges::to(); + if (introspectImpl.empty()) { + LOG(WARNING) + << "Failed to find `oi::introspect` within the `oi` namespace. Did " + "you compile with `OIL_AOT_COMPILATION=1`?"; + return; } - } - size_t successes = oilTypes.size() - failures; - LOG(INFO) << "object introspection generation complete. " << successes - << " successes and " << failures << " failures."; + auto nameToClangTypeMap = + ranges::views::single(introspectImpl[0]) | + ranges::views::for_each( + [](auto* td) { return td->specializations(); }) | + ranges::views::transform( + [](auto* p) { return llvm::dyn_cast(p); }) | + ranges::views::filter([](auto* p) { return p != nullptr; }) | + ranges::views::transform( + [](auto* fd) -> std::pair { + clang::ASTContext& Ctx = fd->getASTContext(); + clang::ASTNameGenerator ASTNameGen(Ctx); + std::string name = ASTNameGen.getName(fd); - if (failures > 0 || (failIfNothingGenerated && successes == 0)) { - return -1; + assert(fd->getNumParams() == 1); + const clang::Type* type = + fd->parameters()[0]->getType().getTypePtr(); + return {name, type}; + }) | + ranges::to(); + if (nameToClangTypeMap.empty()) + return; + + type_graph::ClangTypeParserOptions opts; + type_graph::ClangTypeParser parser{ctx.typeGraph, ctx.containerInfos, opts}; + + auto& Sema = *ctx.sema; + ctx.nameToTypeMap = + nameToClangTypeMap | + ranges::views::transform( + [&parser, &Context, &Sema]( + auto& p) -> std::pair { + return {p.first, &parser.parse(Context, Sema, *p.second)}; + }) | + ranges::to(); + + for (const auto& [name, type] : ctx.nameToTypeMap) + ctx.typeGraph.addRoot(*type); } - return 0; +}; + +void CreateTypeGraphAction::ExecuteAction() { + clang::CompilerInstance& CI = getCompilerInstance(); + + // Compile the output as position independent if any input is position + // independent + bool pic = CI.getCodeGenOpts().RelocationModel == llvm::Reloc::PIC_; + ctx.pic = ctx.pic.value_or(false) || pic; + + if (!CI.hasSema()) + CI.createSema(clang::TU_Complete, nullptr); + ctx.sema = &CI.getSema(); + + clang::ASTFrontendAction::ExecuteAction(); } +std::unique_ptr CreateTypeGraphAction::CreateASTConsumer( + clang::CompilerInstance& CI, clang::StringRef file) { + return std::make_unique(ctx); +} + +} // namespace } // namespace oi::detail diff --git a/oi/OIGenerator.h b/oi/OIGenerator.h index dd38384..fac947a 100644 --- a/oi/OIGenerator.h +++ b/oi/OIGenerator.h @@ -23,11 +23,19 @@ #include "oi/OICodeGen.h" #include "oi/OICompiler.h" +namespace clang::tooling { +class CompilationDatabase; +} + namespace oi::detail { +namespace type_graph { +class Type; +} class OIGenerator { public: - int generate(fs::path& primaryObject, SymbolService& symbols); + int generate(clang::tooling::CompilationDatabase&, + const std::vector&); void setOutputPath(fs::path _outputPath) { outputPath = std::move(_outputPath); @@ -41,8 +49,8 @@ class OIGenerator { void setFailIfNothingGenerated(bool fail) { failIfNothingGenerated = fail; } - void setUsePIC(bool pic_) { - pic = pic_; + void setClangArgs(std::vector args_) { + clangArgs = std::move(args_); } private: @@ -50,20 +58,7 @@ class OIGenerator { std::vector configFilePaths; std::filesystem::path sourceFileDumpPath; bool failIfNothingGenerated = false; - bool pic = false; - - std::unordered_map oilStrongToWeakSymbolsMap( - drgnplusplus::program& prog); - - std::unordered_map findOilTypesAndNames( - drgnplusplus::program& prog); - - std::filesystem::path generateForType( - const OICodeGen::Config& generatorConfig, - const OICompiler::Config& compilerConfig, - const drgn_qualified_type& type, - const std::string& linkageName, - SymbolService& symbols); + std::vector clangArgs; }; } // namespace oi::detail diff --git a/oi/type_graph/CMakeLists.txt b/oi/type_graph/CMakeLists.txt index 190bdfc..6c4da13 100644 --- a/oi/type_graph/CMakeLists.txt +++ b/oi/type_graph/CMakeLists.txt @@ -2,6 +2,7 @@ add_library(type_graph AddChildren.cpp AddPadding.cpp AlignmentCalc.cpp + ClangTypeParser.cpp DrgnExporter.cpp DrgnParser.cpp EnforceCompatibility.cpp @@ -27,3 +28,4 @@ target_link_libraries(type_graph "-L${DRGN_PATH}/.libs" drgn ) +target_include_directories(type_graph SYSTEM PUBLIC ${LLVM_INCLUDE_DIRS} ${CLANG_INCLUDE_DIRS}) diff --git a/oi/type_graph/ClangTypeParser.cpp b/oi/type_graph/ClangTypeParser.cpp new file mode 100644 index 0000000..67f7bfa --- /dev/null +++ b/oi/type_graph/ClangTypeParser.cpp @@ -0,0 +1,416 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "ClangTypeParser.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include // TODO: maybe remove +#include +#include // TODO: remove + +#include "oi/type_graph/Types.h" + +namespace oi::detail::type_graph { +namespace { + +bool requireCompleteType(clang::Sema& sema, const clang::Type& ty); + +} + +Type& ClangTypeParser::enumerateType(const clang::Type& ty) { + // Avoid re-enumerating an already-processsed type + if (auto it = clang_types_.find(&ty); it != clang_types_.end()) + return it->second; + + struct DepthTracker { + DepthTracker(ClangTypeParser& self_) : self{self_} { + self.depth_++; + } + ~DepthTracker() { + self.depth_--; + } + + private: + ClangTypeParser& self; + } d{*this}; + + if (VLOG_IS_ON(3)) { + std::string fqName = clang::TypeName::getFullyQualifiedName( + clang::QualType(&ty, 0), *ast, {ast->getLangOpts()}); + VLOG(3) << std::string(depth_ * 2, ' ') << fqName; + } + + if (!requireCompleteType(*sema, ty)) + return makeType(ty, "incomplete (TODO naming)"); + + switch (ty.getTypeClass()) { + case clang::Type::Record: + return enumerateClass(llvm::cast(ty)); + case clang::Type::LValueReference: + return enumerateReference( + llvm::cast(ty)); + case clang::Type::Pointer: + return enumeratePointer(llvm::cast(ty)); + case clang::Type::SubstTemplateTypeParm: + return enumerateSubstTemplateTypeParm( + llvm::cast(ty)); + case clang::Type::Builtin: + return enumeratePrimitive(llvm::cast(ty)); + case clang::Type::Elaborated: + return enumerateElaboratedType( + llvm::cast(ty)); + case clang::Type::TemplateSpecialization: + return enumerateTemplateSpecialization( + llvm::cast(ty)); + case clang::Type::UnaryTransform: + return enumerateUnaryTransformType( + llvm::cast(ty)); + case clang::Type::Decltype: + return enumerateDecltypeType(llvm::cast(ty)); + case clang::Type::Typedef: + return enumerateTypedef(llvm::cast(ty)); + case clang::Type::Using: + return enumerateUsing(llvm::cast(ty)); + case clang::Type::ConstantArray: + return enumerateArray(llvm::cast(ty)); + case clang::Type::Enum: + return enumerateEnum(llvm::cast(ty)); + + default: + throw std::logic_error(std::string("unsupported TypeClass `") + + ty.getTypeClassName() + '`'); + } +} + +Type& ClangTypeParser::enumerateDecltypeType(const clang::DecltypeType& ty) { + return enumerateType(*ty.getUnderlyingType()); +} + +Type& ClangTypeParser::enumerateUnaryTransformType( + const clang::UnaryTransformType& ty) { + return enumerateType(*ty.getUnderlyingType()); +} + +Typedef& ClangTypeParser::enumerateUsing(const clang::UsingType& ty) { + auto& inner = enumerateType(*ty.desugar()); + std::string name = ty.getFoundDecl()->getNameAsString(); + return makeType(ty, std::move(name), inner); +} + +Typedef& ClangTypeParser::enumerateTypedef(const clang::TypedefType& ty) { + auto& inner = enumerateType(*ty.desugar()); + + std::string name = ty.getDecl()->getNameAsString(); + return makeType(ty, std::move(name), inner); +} + +Type& ClangTypeParser::parse(clang::ASTContext& ast_, + clang::Sema& sema_, + const clang::Type& ty) { + ast = &ast_; + sema = &sema_; + + depth_ = 0; + return enumerateType(ty); +} + +Enum& ClangTypeParser::enumerateEnum(const clang::EnumType& ty) { + std::string name = ty.getDecl()->getNameAsString(); + auto size = ast->getTypeSize(clang::QualType(&ty, 0)) / 8; + + std::map enumeratorMap; + if (options_.readEnumValues) { + for (const auto* enumerator : ty.getDecl()->enumerators()) { + enumeratorMap.emplace(enumerator->getInitVal().getExtValue(), + enumerator->getNameAsString()); + } + } + + return makeType(ty, std::move(name), size, std::move(enumeratorMap)); +} + +Array& ClangTypeParser::enumerateArray(const clang::ConstantArrayType& ty) { + uint64_t len = ty.getSize().getLimitedValue(); + auto& t = enumerateType(*ty.getElementType()); + return makeType(ty, t, len); +} + +Type& ClangTypeParser::enumerateTemplateSpecialization( + const clang::TemplateSpecializationType& ty) { + if (ty.isSugared()) + return enumerateType(*ty.desugar()); + + LOG(WARNING) << "failed on a TemplateSpecializationType"; + ty.dump(); + return makeType(ty, Primitive::Kind::Int32); +} + +Type& ClangTypeParser::enumerateClass(const clang::RecordType& ty) { + std::string fqName = clang::TypeName::getFullyQualifiedName( + clang::QualType(&ty, 0), *ast, {ast->getLangOpts()}); + auto size = ast->getTypeSize(clang::QualType(&ty, 0)) / 8; + + if (auto* info = getContainerInfo(fqName)) { + auto& c = makeType(ty, *info, size, nullptr); + enumerateClassTemplateParams(ty, c.templateParams); + c.setAlign(ast->getTypeAlign(clang::QualType(&ty, 0))); + return c; + } + + auto* decl = ty.getDecl(); + + std::string name = decl->getNameAsString(); + + auto kind = Class::Kind::Struct; // TODO: kind + + int virtuality = 0; + + auto& c = makeType( + ty, kind, std::move(name), std::move(fqName), size, virtuality); + + enumerateClassTemplateParams(ty, c.templateParams); + // enumerateClassParents(type, c.parents); + enumerateClassMembers(ty, c.members); + // enumerateClassFunctions(type, c.functions); + + return c; +} + +void ClangTypeParser::enumerateClassTemplateParams( + const clang::RecordType& ty, std::vector& params) { + assert(params.empty()); + + auto* decl = dyn_cast(ty.getDecl()); + if (decl == nullptr) + return; + + const auto& list = decl->getTemplateArgs(); + + params.reserve(list.size()); + for (const auto& arg : list.asArray()) { + if (auto p = enumerateTemplateParam(arg)) + params.emplace_back(std::move(p.value())); + } +} + +std::optional ClangTypeParser::enumerateTemplateParam( + const clang::TemplateArgument& p) { + switch (p.getKind()) { + case clang::TemplateArgument::Type: { + auto qualType = p.getAsType(); + QualifierSet qualifiers; + qualifiers[Qualifier::Const] = qualType.isConstQualified(); + Type& ttype = enumerateType(*qualType); + return TemplateParam{ttype, qualifiers}; + } + case clang::TemplateArgument::Integral: { + auto& ty = enumerateType(*p.getIntegralType()); + llvm::SmallString<32> val; + p.getAsIntegral().toString(val); + return TemplateParam{ty, std::string(val)}; + } + case clang::TemplateArgument::Template: { + return enumerateTemplateTemplateParam(p.getAsTemplate()); + } + +#define X(name) \ + case clang::TemplateArgument::name: \ + throw std::logic_error("unsupported template argument kind: " #name); + + X(Null) + X(Declaration) + X(NullPtr) + X(TemplateExpansion) + X(Expression) + X(Pack) +#undef X + } +} + +std::optional ClangTypeParser::enumerateTemplateTemplateParam( + const clang::TemplateName& tn) { + switch (tn.getKind()) { + case clang::TemplateName::Template: + return std::nullopt; + +#define X(name) \ + case clang::TemplateName::name: \ + throw std::logic_error("unsupported template name kind: " #name); + + X(OverloadedTemplate) + X(AssumedTemplate) + X(QualifiedTemplate) + X(DependentTemplate) + X(SubstTemplateTemplateParm) + X(SubstTemplateTemplateParmPack) + X(UsingTemplate) +#undef X + } +} + +void ClangTypeParser::enumerateClassMembers(const clang::RecordType& ty, + std::vector& members) { + assert(members.empty()); + + auto* decl = ty.getDecl(); + + for (const auto* field : decl->fields()) { + clang::QualType qualType = field->getType(); + std::string member_name = field->getNameAsString(); + + size_t size_in_bits = 0; + if (field->isBitField()) { + size_in_bits = field->getBitWidthValue(*ast); + } + + size_t offset_in_bits = decl->getASTContext().getFieldOffset(field); + + auto& mtype = enumerateType(*qualType); + Member m{mtype, std::move(member_name), offset_in_bits, size_in_bits}; + members.push_back(m); + } + + std::sort(members.begin(), members.end(), [](const auto& a, const auto& b) { + return a.bitOffset < b.bitOffset; + }); +} + +Type& ClangTypeParser::enumerateReference( + const clang::LValueReferenceType& ty) { + // TODO: function references + Type& t = enumerateType(*ty.getPointeeType()); + if (dynamic_cast(&t)) + return makeType(ty, t); + + return makeType(ty, t); +} + +Type& ClangTypeParser::enumeratePointer(const clang::PointerType& ty) { + // TODO: function references + if (!chasePointer()) + return makeType(ty, Primitive::Kind::StubbedPointer); + + Type& t = enumerateType(*ty.getPointeeType()); + return makeType(ty, t); +} + +Type& ClangTypeParser::enumerateSubstTemplateTypeParm( + const clang::SubstTemplateTypeParmType& ty) { + // Clang wraps any type that was substituted from e.g. `T` in this type. It + // should have no representation in the type graph. + return enumerateType(*ty.getReplacementType()); +} + +Type& ClangTypeParser::enumerateElaboratedType( + const clang::ElaboratedType& ty) { + // Clang wraps any type that is name qualified in this type. It should have no + // representation in the type graph. + return enumerateType(*ty.getNamedType()); +} + +Primitive& ClangTypeParser::enumeratePrimitive(const clang::BuiltinType& ty) { + switch (ty.getKind()) { + case clang::BuiltinType::Void: + return makeType(ty, Primitive::Kind::Void); + + case clang::BuiltinType::Bool: + return makeType(ty, Primitive::Kind::Bool); + + case clang::BuiltinType::Char_U: + case clang::BuiltinType::UChar: + return makeType(ty, Primitive::Kind::UInt8); + case clang::BuiltinType::WChar_U: + return makeType(ty, Primitive::Kind::UInt32); + + case clang::BuiltinType::Char_S: + case clang::BuiltinType::SChar: + return makeType(ty, Primitive::Kind::Int8); + case clang::BuiltinType::WChar_S: + return makeType(ty, Primitive::Kind::Int32); + case clang::BuiltinType::Char16: + return makeType(ty, Primitive::Kind::Int16); + case clang::BuiltinType::Char32: + return makeType(ty, Primitive::Kind::Int32); + + case clang::BuiltinType::UShort: + return makeType(ty, Primitive::Kind::UInt16); + case clang::BuiltinType::UInt: + return makeType(ty, Primitive::Kind::UInt32); + case clang::BuiltinType::ULong: + return makeType(ty, Primitive::Kind::UInt64); + case clang::BuiltinType::ULongLong: + return makeType(ty, Primitive::Kind::Int64); + + case clang::BuiltinType::Short: + return makeType(ty, Primitive::Kind::Int16); + case clang::BuiltinType::Int: + return makeType(ty, Primitive::Kind::Int32); + case clang::BuiltinType::Long: + case clang::BuiltinType::LongLong: + return makeType(ty, Primitive::Kind::Int64); + + case clang::BuiltinType::Float: + return makeType(ty, Primitive::Kind::Float32); + case clang::BuiltinType::Double: + case clang::BuiltinType::LongDouble: + return makeType(ty, Primitive::Kind::Float64); + + case clang::BuiltinType::UInt128: + case clang::BuiltinType::Int128: + default: + throw std::logic_error(std::string("unsupported BuiltinType::Kind")); + } +} + +bool ClangTypeParser::chasePointer() const { + // Always chase top-level pointers + if (depth_ == 1) + return true; + return options_.chaseRawPointers; +} + +ContainerInfo* ClangTypeParser::getContainerInfo( + const std::string& fqName) const { + for (const auto& containerInfo : containers_) { + if (std::regex_search(fqName, containerInfo->matcher)) { + return containerInfo.get(); + } + } + return nullptr; +} + +namespace { + +bool requireCompleteType(clang::Sema& sema, const clang::Type& ty) { + if (ty.isSpecificBuiltinType(clang::BuiltinType::Void)) + return true; // treat as complete + + // TODO: This is a terrible warning. + return !sema.RequireCompleteType( + sema.getASTContext().getTranslationUnitDecl()->getEndLoc(), + clang::QualType{&ty, 0}, + clang::diag::warn_nsconsumed_attribute_mismatch); +} + +} // namespace +} // namespace oi::detail::type_graph diff --git a/oi/type_graph/ClangTypeParser.h b/oi/type_graph/ClangTypeParser.h new file mode 100644 index 0000000..10eea87 --- /dev/null +++ b/oi/type_graph/ClangTypeParser.h @@ -0,0 +1,131 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include +#include + +#include "oi/type_graph/TypeGraph.h" + +namespace clang { +class ASTContext; +class BuiltinType; +class ConstantArrayType; +class DecltypeType; +class ElaboratedType; +class EnumType; +class LValueReferenceType; +class PointerType; +class RecordType; +class Sema; +class SubstTemplateTypeParmType; +class TemplateArgument; +class TemplateName; +class TemplateSpecializationType; +class Type; +class TypedefType; +class UnaryTransformType; +class UsingType; +} // namespace clang + +struct ContainerInfo; + +namespace oi::detail::type_graph { + +class Array; +class Class; +class Enum; +class Member; +class Primitive; +class Reference; +class Type; +class TypeGraph; +class Typedef; +struct TemplateParam; + +struct ClangTypeParserOptions { + bool chaseRawPointers = false; + bool readEnumValues = false; +}; + +/* + * ClangTypeParser + * + * Reads source information from a source file to build a type graph. + * Returns a reference to the Type node corresponding to the given drgn_type. + */ +class ClangTypeParser { + public: + ClangTypeParser(TypeGraph& typeGraph, + const std::vector>& containers, + ClangTypeParserOptions options) + : typeGraph_{typeGraph}, containers_{containers}, options_{options} { + } + + // Parse from a clang type. + Type& parse(clang::ASTContext&, clang::Sema&, const clang::Type&); + + private: + TypeGraph& typeGraph_; + const std::vector>& containers_; + ClangTypeParserOptions options_; + clang::ASTContext* ast; + clang::Sema* sema; + + uint_fast32_t depth_; + std::unordered_map> + clang_types_; + + Type& enumerateType(const clang::Type&); + Type& enumerateClass(const clang::RecordType&); + Type& enumerateReference(const clang::LValueReferenceType&); + Type& enumeratePointer(const clang::PointerType&); + Type& enumerateSubstTemplateTypeParm(const clang::SubstTemplateTypeParmType&); + Primitive& enumeratePrimitive(const clang::BuiltinType&); + Type& enumerateElaboratedType(const clang::ElaboratedType&); + Type& enumerateTemplateSpecialization( + const clang::TemplateSpecializationType&); + Typedef& enumerateTypedef(const clang::TypedefType&); + Typedef& enumerateUsing(const clang::UsingType&); + Type& enumerateUnaryTransformType(const clang::UnaryTransformType&); + Type& enumerateDecltypeType(const clang::DecltypeType&); + + Array& enumerateArray(const clang::ConstantArrayType&); + Enum& enumerateEnum(const clang::EnumType&); + + void enumerateClassTemplateParams(const clang::RecordType&, + std::vector&); + std::optional enumerateTemplateParam( + const clang::TemplateArgument&); + std::optional enumerateTemplateTemplateParam( + const clang::TemplateName&); + + void enumerateClassMembers(const clang::RecordType&, std::vector&); + + ContainerInfo* getContainerInfo(const std::string& fqName) const; + + template + T& makeType(const clang::Type& clangType, Args&&... args) { + auto& newType = typeGraph_.makeType(std::forward(args)...); + clang_types_.insert({&clangType, newType}); + return newType; + } + + bool chasePointer() const; +}; + +} // namespace oi::detail::type_graph diff --git a/oi/type_graph/DrgnExporter.cpp b/oi/type_graph/DrgnExporter.cpp index 802857e..df9722d 100644 --- a/oi/type_graph/DrgnExporter.cpp +++ b/oi/type_graph/DrgnExporter.cpp @@ -200,6 +200,14 @@ drgn_type* DrgnExporter::visit(Pointer& p) { return drgnType; } +drgn_type* DrgnExporter::visit(Reference& p) { + auto* drgnType = + makeDrgnType(DRGN_TYPE_POINTER, false, DRGN_NOT_PRIMITIVE_TYPE, p); + auto* pointeeType = accept(p.pointeeType()); + th_.pointerToTypeMap[drgnType] = pointeeType; + return drgnType; +} + drgn_type* DrgnExporter::visit(Dummy& d) { return makeDrgnType(DRGN_TYPE_VOID, false, DRGN_C_TYPE_VOID, d); } diff --git a/oi/type_graph/DrgnExporter.h b/oi/type_graph/DrgnExporter.h index 45f02c6..24f4c59 100644 --- a/oi/type_graph/DrgnExporter.h +++ b/oi/type_graph/DrgnExporter.h @@ -54,6 +54,7 @@ class DrgnExporter : public Visitor { drgn_type* visit(Array&) override; drgn_type* visit(Typedef&) override; drgn_type* visit(Pointer&) override; + drgn_type* visit(Reference&) override; drgn_type* visit(Dummy&) override; drgn_type* visit(DummyAllocator&) override; drgn_type* visit(CaptureKeys&) override; diff --git a/oi/type_graph/NameGen.cpp b/oi/type_graph/NameGen.cpp index b1d1e8d..f016c13 100644 --- a/oi/type_graph/NameGen.cpp +++ b/oi/type_graph/NameGen.cpp @@ -200,6 +200,14 @@ void NameGen::visit(Pointer& p) { p.setInputName(inputName); } +void NameGen::visit(Reference& p) { + RecursiveVisitor::visit(p); + p.regenerateName(); + std::string inputName{p.pointeeType().inputName()}; + inputName += '*'; + p.setInputName(inputName); +} + void NameGen::visit(DummyAllocator& d) { RecursiveVisitor::visit(d); d.regenerateName(); diff --git a/oi/type_graph/NameGen.h b/oi/type_graph/NameGen.h index 7b01aef..e6437d0 100644 --- a/oi/type_graph/NameGen.h +++ b/oi/type_graph/NameGen.h @@ -46,6 +46,7 @@ class NameGen final : public RecursiveVisitor { void visit(Array& a) override; void visit(Typedef& td) override; void visit(Pointer& p) override; + void visit(Reference& p) override; void visit(DummyAllocator& d) override; void visit(CaptureKeys& d) override; diff --git a/oi/type_graph/Printer.cpp b/oi/type_graph/Printer.cpp index 0dae0ab..49165ab 100644 --- a/oi/type_graph/Printer.cpp +++ b/oi/type_graph/Printer.cpp @@ -150,6 +150,17 @@ void Printer::visit(const Pointer& p) { print(p.pointeeType()); } +void Printer::visit(const Reference& p) { + if (prefix(p)) + return; + + out_ << "Reference"; + if (auto inp = p.inputName(); !inp.empty()) + out_ << " [" << inp << "]"; + out_ << std::endl; + print(p.pointeeType()); +} + void Printer::visit(const Dummy& d) { if (prefix(d)) return; diff --git a/oi/type_graph/Printer.h b/oi/type_graph/Printer.h index 8d7138a..e08a943 100644 --- a/oi/type_graph/Printer.h +++ b/oi/type_graph/Printer.h @@ -40,6 +40,7 @@ class Printer : public ConstVisitor { void visit(const Array& a) override; void visit(const Typedef& td) override; void visit(const Pointer& p) override; + void visit(const Reference& p) override; void visit(const Dummy& d) override; void visit(const DummyAllocator& d) override; void visit(const CaptureKeys& d) override; diff --git a/oi/type_graph/RemoveTopLevelPointer.cpp b/oi/type_graph/RemoveTopLevelPointer.cpp index 9ecaaaa..e1909a7 100644 --- a/oi/type_graph/RemoveTopLevelPointer.cpp +++ b/oi/type_graph/RemoveTopLevelPointer.cpp @@ -42,4 +42,8 @@ void RemoveTopLevelPointer::visit(Pointer& p) { topLevelType_ = &p.pointeeType(); } +void RemoveTopLevelPointer::visit(Reference& p) { + topLevelType_ = &p.pointeeType(); +} + } // namespace oi::detail::type_graph diff --git a/oi/type_graph/RemoveTopLevelPointer.h b/oi/type_graph/RemoveTopLevelPointer.h index 611429f..6b70545 100644 --- a/oi/type_graph/RemoveTopLevelPointer.h +++ b/oi/type_graph/RemoveTopLevelPointer.h @@ -36,6 +36,7 @@ class RemoveTopLevelPointer : public LazyVisitor { void removeTopLevelPointers(std::vector>& types); void visit(Pointer& p) override; + void visit(Reference& p) override; private: Type* topLevelType_ = nullptr; diff --git a/oi/type_graph/Types.h b/oi/type_graph/Types.h index f0b3fe9..38d53c6 100644 --- a/oi/type_graph/Types.h +++ b/oi/type_graph/Types.h @@ -49,6 +49,7 @@ X(Array) \ X(Typedef) \ X(Pointer) \ + X(Reference) \ X(Dummy) \ X(DummyAllocator) \ X(CaptureKeys) @@ -734,6 +735,61 @@ class Pointer : public Type { std::string name_; }; +class Reference : public Type { + public: + explicit Reference(NodeId id, Type& pointeeType) + : pointeeType_(pointeeType), id_(id) { + regenerateName(); + } + + static inline constexpr bool has_node_id = true; + + DECLARE_ACCEPT + + virtual const std::string& name() const override { + return name_; + } + + void regenerateName() { + name_ = pointeeType_.get().name() + "&"; + } + + virtual std::string_view inputName() const override { + return inputName_; + } + + void setInputName(std::string name) { + inputName_ = std::move(name); + } + + virtual size_t size() const override { + return sizeof(uintptr_t); + } + + virtual uint64_t align() const override { + return size(); + } + + virtual NodeId id() const override { + return id_; + } + + Type& pointeeType() const { + return pointeeType_; + } + + void setPointeeType(Type& type) { + pointeeType_ = type; + } + + private: + std::reference_wrapper pointeeType_; + std::string inputName_; + NodeId id_ = -1; + + std::string name_; +}; + /* * Dummy * diff --git a/oi/type_graph/Visitor.h b/oi/type_graph/Visitor.h index a7f7eec..f055aea 100644 --- a/oi/type_graph/Visitor.h +++ b/oi/type_graph/Visitor.h @@ -108,6 +108,9 @@ class RecursiveVisitor : public Visitor { virtual void visit(Pointer& p) { accept(p.pointeeType()); } + virtual void visit(Reference& r) { + accept(r.pointeeType()); + } virtual void visit(Dummy&) { } virtual void visit(DummyAllocator& d) { @@ -175,6 +178,10 @@ class RecursiveMutator : public Visitor { p.setPointeeType(mutate(p.pointeeType())); return p; } + virtual Type& visit(Reference& p) { + p.setPointeeType(mutate(p.pointeeType())); + return p; + } virtual Type& visit(Dummy& d) { return d; } diff --git a/test/integration/template_template_param.toml b/test/integration/template_template_param.toml new file mode 100644 index 0000000..c64590d --- /dev/null +++ b/test/integration/template_template_param.toml @@ -0,0 +1,16 @@ +includes = ["vector", "utility", "string"] + +definitions = ''' +template typename Pair> +struct bad_map { + std::vector> keys; + std::vector> values; +}; +''' + +[cases] + [cases.int_int_empty] + param_types = ["const bad_map&"] + setup = "return {};" + expect_json = '[{"staticSize":24, "dynamicSize":0, "length":0, "capacity":0, "elementStaticSize":4}]' + expect_json_v2 = '[{"staticSize":24, "exclusiveSize":24, "length":0, "capacity":0, "members":[]}]' diff --git a/tools/OILGen.cpp b/tools/OILGen.cpp index d1e345d..795389e 100644 --- a/tools/OILGen.cpp +++ b/tools/OILGen.cpp @@ -14,131 +14,81 @@ * limitations under the License. */ +#include #include #include #include #include #include +#include +#include +#include +#include +#include #include #include "oi/OICodeGen.h" #include "oi/OIGenerator.h" -#include "oi/OIOpts.h" namespace fs = std::filesystem; using namespace oi::detail; -constexpr static OIOpts opts{ - OIOpt{'h', "help", no_argument, nullptr, "Print this message and exit."}, - OIOpt{'o', - "output", - required_argument, - "", - "Write output(s) to file(s) with this prefix."}, - OIOpt{'c', - "config-file", - required_argument, - "", - "Path to OI configuration file."}, - OIOpt{'d', - "debug-level", - required_argument, - "", - "Verbose level for logging"}, - OIOpt{'j', - "dump-jit", - optional_argument, - "", - "Write generated code to a file (for debugging)."}, - OIOpt{'e', - "exit-code", - no_argument, - nullptr, - "Return a bad exit code if nothing is generated."}, - OIOpt{'p', - "pic", - no_argument, - nullptr, - "Generate position independent code."}, -}; +static llvm::cl::OptionCategory OilgenCategory("oilgen options"); -void usage() { - std::cerr << "usage: oilgen ARGS INPUT_OBJECT" << std::endl; - std::cerr << opts; +static llvm::cl::list ConfigFiles("config-file", + llvm::cl::desc(R"(TODO HELP)"), + llvm::cl::cat(OilgenCategory)); +static llvm::cl::opt DebugLevel("debug-level", + llvm::cl::desc(R"(TODO HELP)"), + llvm::cl::init(-1), + llvm::cl::cat(OilgenCategory)); +static llvm::cl::opt DumpJit("dump-jit", + llvm::cl::desc(R"(TODO HELP)"), + llvm::cl::init("jit.cpp"), + llvm::cl::cat(OilgenCategory)); - std::cerr << std::endl - << "You probably shouldn't be calling this application directly. " - "It's meant to be" - << std::endl - << "called by a clang plugin automatically with BUCK." << std::endl; -} - -int main(int argc, char* argv[]) { +int main(int argc, const char* argv[]) { google::InitGoogleLogging(argv[0]); FLAGS_minloglevel = 0; FLAGS_stderrthreshold = 0; - fs::path outputPath = "a.o"; - std::vector configFilePaths; - fs::path sourceFileDumpPath = ""; - bool exitCode = false; - bool pic = false; - - int c; - while ((c = getopt_long( - argc, argv, opts.shortOpts(), opts.longOpts(), nullptr)) != -1) { - switch (c) { - case 'h': - usage(); - return EXIT_SUCCESS; - case 'o': - outputPath = optarg; - break; - case 'c': - configFilePaths.emplace_back(optarg); - break; - case 'd': - google::LogToStderr(); - google::SetStderrLogging(google::INFO); - google::SetVLOGLevel("*", atoi(optarg)); - // Upstream glog defines `GLOG_INFO` as 0 https://fburl.com/ydjajhz0, - // but internally it's defined as 1 https://fburl.com/code/9fwams75 - gflags::SetCommandLineOption("minloglevel", "0"); - break; - case 'j': - sourceFileDumpPath = optarg != nullptr ? optarg : "jit.cpp"; - break; - case 'e': - exitCode = true; - break; - case 'p': - pic = true; - break; - } + auto expectedParser = + clang::tooling::CommonOptionsParser::create(argc, argv, OilgenCategory); + if (!expectedParser) { + llvm::errs() << expectedParser.takeError(); + return -1; } + clang::tooling::CommonOptionsParser& options = expectedParser.get(); + auto& compilations = options.getCompilations(); - if (optind >= argc) { - usage(); - return EXIT_FAILURE; - } - fs::path primaryObject = argv[optind]; - - if ((setenv("DRGN_ENABLE_TYPE_ITERATOR", "1", 1)) < 0) { - LOG(ERROR) << "Failed to set environment variable\ - DRGN_ENABLE_TYPE_ITERATOR\n"; - exit(EXIT_FAILURE); + if (DebugLevel.getNumOccurrences()) { + google::LogToStderr(); + google::SetStderrLogging(google::INFO); + google::SetVLOGLevel("*", DebugLevel); + // Upstream glog defines `GLOG_INFO` as 0 https://fburl.com/ydjajhz0, + // but internally it's defined as 1 https://fburl.com/code/9fwams75 + gflags::SetCommandLineOption("minloglevel", "0"); } OIGenerator oigen; - oigen.setOutputPath(std::move(outputPath)); - oigen.setConfigFilePaths(std::move(configFilePaths)); - oigen.setSourceFileDumpPath(sourceFileDumpPath); - oigen.setFailIfNothingGenerated(exitCode); - oigen.setUsePIC(pic); + oigen.setConfigFilePaths(ConfigFiles | + ranges::views::transform([](const auto& p) { + return std::filesystem::path(p); + }) | + ranges::to()); + if (DumpJit.getNumOccurrences()) + oigen.setSourceFileDumpPath(DumpJit.getValue()); - SymbolService symbols(primaryObject); + oigen.setOutputPath("a.o"); // TODO: make this an opt - return oigen.generate(primaryObject, symbols); + // TODO: can we extract this from the original arguments? + // oigen.setUsePIC(args["pic"].as()); + + oigen.setFailIfNothingGenerated(true); + // compilations.getAllFiles(); + // return oigen.generate(compilations, + // {"/data/users/jakehillion/fbsource/fbcode/object-introspection/oil/examples/compile-time/VectorOfStrings-Introspect.cpp"}); + return oigen.generate(compilations, options.getSourcePathList()); } diff --git a/types/string_type.toml b/types/string_type.toml index fa4a2ae..0d2ab0d 100644 --- a/types/string_type.toml +++ b/types/string_type.toml @@ -35,3 +35,34 @@ void getSizeType(const %1% &container, size_t& returnArg) ); } """ + +traversal_func = """ + bool sso = ((uintptr_t)container.data() < + (uintptr_t)(&container + sizeof(std::__cxx11::basic_string))) && + ((uintptr_t)container.data() >= (uintptr_t)&container); + + return returnArg.write(container.capacity()) + .write(sso) + .write(container.size()); +""" + +[[codegen.processor]] +type = "types::st::VarInt" +func = """ +uint64_t capacity = std::get(d.val).value; +el.container_stats.emplace(result::Element::ContainerStats { .capacity = capacity }); +""" + +[[codegen.processor]] +type = "types::st::VarInt" +func = """ +bool sso = std::get(d.val).value; +if (!sso) + el.exclusive_size += el.container_stats->capacity * sizeof(T0); +""" + +[[codegen.processor]] +type = "types::st::VarInt" +func = """ +el.container_stats->length = std::get(d.val).value; +"""