From 55989a9156fa073c8f73f79b55426f35d87f8799 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Tue, 19 Dec 2023 11:57:44 -0800 Subject: [PATCH] oilgen: migrate to source parsing (#421) Summary: oilgen: migrate to source parsing 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: hope `buck2 run fbcode//mode/opt fbcode//object-introspection/oil/examples/compile-time:compile-time` Reviewed By: tyroguru Differential Revision: D51854477 Pulled By: JakeHillion --- CMakeLists.txt | 3 +- oi/CodeGen.cpp | 31 +- oi/CodeGen.h | 6 +- oi/OIGenerator.cpp | 369 ++++++++++++--------- oi/OIGenerator.h | 29 +- oi/type_graph/CMakeLists.txt | 2 + oi/type_graph/ClangTypeParser.cpp | 418 ++++++++++++++++++++++++ oi/type_graph/ClangTypeParser.h | 131 ++++++++ oi/type_graph/DrgnExporter.cpp | 8 + oi/type_graph/DrgnExporter.h | 1 + oi/type_graph/NameGen.cpp | 9 + 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 | 61 ++++ oi/type_graph/Visitor.h | 7 + tools/OILGen.cpp | 147 +++------ types/string_type.toml | 31 ++ 20 files changed, 983 insertions(+), 288 deletions(-) create mode 100644 oi/type_graph/ClangTypeParser.cpp create mode 100644 oi/type_graph/ClangTypeParser.h 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..9f41c12 100644 --- a/oi/CodeGen.cpp +++ b/oi/CodeGen.cpp @@ -299,6 +299,11 @@ 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 +547,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 +1120,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 +1146,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 +1200,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..8fc5a81 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_ = nullptr; std::vector> containerInfos_; std::unordered_set definedContainers_; std::unordered_map diff --git a/oi/OIGenerator.cpp b/oi/OIGenerator.cpp index 1cb2a49..ba06069 100644 --- a/oi/OIGenerator.cpp +++ b/oi/OIGenerator.cpp @@ -16,130 +16,149 @@ #include "oi/OIGenerator.h" +#include +#include +#include +#include +#include +#include #include -#include #include -#include -#include +#include +#include +#include +#include +#include +#include +#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/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 site to generate for but we can't currently " + "handle this case"); + + if (ctx.nameToTypeMap.empty()) { + LOG(ERROR) << "Nothing to generate!"; + return failIfNothingGenerated ? -1 : 0; + } + 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 +171,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 = + introspectImpl | 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; + auto els = nameToClangTypeMap | + ranges::views::transform( + [&parser, &Context, &Sema]( + auto& p) -> std::pair { + return {p.first, &parser.parse(Context, Sema, *p.second)}; + }); + ctx.nameToTypeMap.insert(els.begin(), els.end()); + + 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( + [[maybe_unused]] clang::CompilerInstance& CI, + [[maybe_unused]] 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..5190478 --- /dev/null +++ b/oi/type_graph/ClangTypeParser.cpp @@ -0,0 +1,418 @@ +/* + * 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 +#include + +#include "oi/type_graph/Types.h" + +namespace oi::detail::type_graph { +namespace { +bool requireCompleteType(clang::Sema& sema, const clang::Type& ty); +} + +Type& ClangTypeParser::parse(clang::ASTContext& ast_, + clang::Sema& sema_, + const clang::Type& ty) { + ast = &ast_; + sema = &sema_; + + depth_ = 0; + return enumerateType(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; + } + + // TODO: This check basically doesn't work. The action has failed before it + // returns false. Fix this. + if (!requireCompleteType(*sema, ty)) { + std::string fqName = clang::TypeName::getFullyQualifiedName( + clang::QualType(&ty, 0), *ast, {ast->getLangOpts()}); + return makeType(ty, std::move(fqName)); + } + + 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); +} + +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 pointers + 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..9b27d64 --- /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 clang::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..725ab59 100644 --- a/oi/type_graph/NameGen.cpp +++ b/oi/type_graph/NameGen.cpp @@ -200,6 +200,15 @@ void NameGen::visit(Pointer& p) { p.setInputName(inputName); } +void NameGen::visit(Reference& r) { + RecursiveVisitor::visit(r); + + r.regenerateName(); + std::string inputName{r.pointeeType().inputName()}; + inputName += '&'; + r.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..4f1c258 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& r) 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..f598161 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& r) { + if (prefix(r)) + return; + + out_ << "Reference"; + if (auto inp = r.inputName(); !inp.empty()) + out_ << " [" << inp << "]"; + out_ << std::endl; + print(r.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..e68f562 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& r) 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..c2fc91d 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& r) { + topLevelType_ = &r.pointeeType(); +} + } // namespace oi::detail::type_graph diff --git a/oi/type_graph/RemoveTopLevelPointer.h b/oi/type_graph/RemoveTopLevelPointer.h index 611429f..3ed2c15 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& r) override; private: Type* topLevelType_ = nullptr; diff --git a/oi/type_graph/Types.h b/oi/type_graph/Types.h index f0b3fe9..1d5dd79 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,66 @@ 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() { + // Following a reference wouldn't trigger cycle checking, as it would look + // like anything else we're sure is there. Generate as a pointer. It will be + // followed regardless of `ChaseRawPointers` because that affects whether a + // type becomes a `StubbedPointer` and not whether pointers are followed in + // the generated code. + 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/tools/OILGen.cpp b/tools/OILGen.cpp index d1e345d..77c3569 100644 --- a/tools/OILGen.cpp +++ b/tools/OILGen.cpp @@ -14,131 +14,80 @@ * limitations under the License. */ +#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"()"), + llvm::cl::cat(OilgenCategory)); +static llvm::cl::opt OutputFile( + "output", + llvm::cl::desc(R"(Write output to this file.)"), + llvm::cl::init("a.o"), + llvm::cl::cat(OilgenCategory)); +static llvm::cl::opt DebugLevel( + "debug-level", + llvm::cl::desc(R"(Verbose level for logging.)"), + llvm::cl::init(-1), + llvm::cl::cat(OilgenCategory)); +static llvm::cl::opt DumpJit( + "dump-jit", + llvm::cl::desc(R"(Write the generated code to a file.)"), + 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(OutputFile.getValue()); - return oigen.generate(primaryObject, symbols); + oigen.setFailIfNothingGenerated(true); + 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; +"""