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 <oi/oi.h>
template <typename T>
struct Foo {
  T t;
};
template <typename T>
struct Bar {
  Foo<T>& f;
};
void foo(const Bar<int>& b) {
  oi::introspect(b);
}
```

The pointer/reference to `Foo<int>` 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
This commit is contained in:
Jake Hillion 2023-12-19 15:36:17 +00:00
parent 37b89d789d
commit dcc46f7b45
24 changed files with 1004 additions and 289 deletions

View File

@ -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)

View File

@ -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<const Class*>(&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<ContainerInfo> 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_));

View File

@ -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<drgn_type>& drgnTypes,
drgn_type** rootType) const;
bool registerContainers();
void registerContainer(std::unique_ptr<ContainerInfo> 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<std::unique_ptr<ContainerInfo>> containerInfos_;
std::unordered_set<const ContainerInfo*> definedContainers_;
std::unordered_map<const type_graph::Class*, const type_graph::Member*>

View File

@ -22,6 +22,8 @@
#include <boost/property_tree/ini_parser.hpp>
#include <boost/property_tree/ptree.hpp>
#include <filesystem>
#include <range/v3/action/sort.hpp>
#include <range/v3/action/unique.hpp>
#include "oi/support/Toml.h"
@ -54,6 +56,9 @@ std::optional<FeatureSet> 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<FeatureSet> 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());
}
});
}

View File

@ -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

View File

@ -66,7 +66,7 @@ class OICodeGen {
};
FeatureSet features;
std::set<std::filesystem::path> containerConfigPaths;
std::vector<std::filesystem::path> containerConfigPaths;
std::set<std::string> defaultHeaders;
std::set<std::string> defaultNamespaces;
std::vector<std::pair<std::string, std::string>> membersToStub;

View File

@ -16,130 +16,156 @@
#include "oi/OIGenerator.h"
#include <clang/AST/Mangle.h>
#include <clang/Frontend/CompilerInstance.h>
#include <clang/Frontend/CompilerInvocation.h>
#include <clang/Frontend/FrontendAction.h>
#include <clang/Sema/Sema.h>
#include <clang/Tooling/Tooling.h>
#include <glog/logging.h>
#include <boost/core/demangle.hpp>
#include <fstream>
#include <iostream>
#include <range/v3/action/transform.hpp>
#include <range/v3/core.hpp>
#include <range/v3/view/cache1.hpp>
#include <range/v3/view/drop.hpp>
#include <range/v3/view/drop_while.hpp>
#include <range/v3/view/filter.hpp>
#include <range/v3/view/for_each.hpp>
#include <range/v3/view/take.hpp>
#include <range/v3/view/transform.hpp>
#include <stdexcept> // TODO: remove
#include <string_view>
#include <unordered_map>
#include <variant>
#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<std::string, std::string>
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<std::string, std::pair<std::string, std::string>>
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<std::string, std::string> 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<clang::ASTConsumer> 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<clang::FrontendAction> create() override {
return std::make_unique<CreateTypeGraphAction>(ctx);
}
std::unordered_map<std::string, drgn_qualified_type>
OIGenerator::findOilTypesAndNames(drgnplusplus::program& prog) {
auto strongToWeakSymbols = oilStrongToWeakSymbolsMap(prog);
private:
ConsumerContext& ctx;
};
std::unordered_map<std::string, drgn_qualified_type> out;
class ConsumerContext {
public:
ConsumerContext(const std::vector<std::unique_ptr<ContainerInfo>>& 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<std::string, type_graph::Type*> nameToTypeMap;
std::optional<bool> pic;
const std::vector<std::unique_ptr<ContainerInfo>>& containerInfos;
private:
clang::Sema* sema = nullptr;
friend CreateTypeGraphConsumer;
friend CreateTypeGraphAction;
};
} // namespace
int OIGenerator::generate(clang::tooling::CompilationDatabase& db,
const std::vector<std::string>& sourcePaths) {
std::map<Feature, bool> 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<std::unique_ptr<ContainerInfo>> containerInfos;
containerInfos.reserve(generatorConfig.containerConfigPaths.size());
try {
for (const auto& path : generatorConfig.containerConfigPaths) {
auto info = std::make_unique<ContainerInfo>(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(&params[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<std::string>{}(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<const char*, 1> 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<clang::NamespaceDecl>(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 <oi/oi.h>?";
return;
}
}
auto oilTypes = findOilTypesAndNames(prog);
std::map<Feature, bool> 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<clang::FunctionTemplateDecl>(p);
}) |
ranges::views::filter([](auto* td) {
return td != nullptr && td->getName() == "introspectImpl";
}) |
ranges::views::take(1) | ranges::to<std::vector>();
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<clang::FunctionDecl>(p); }) |
ranges::views::filter([](auto* p) { return p != nullptr; }) |
ranges::views::transform(
[](auto* fd) -> std::pair<std::string, const clang::Type*> {
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<std::unordered_map>();
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<std::string, type_graph::Type*> {
return {p.first, &parser.parse(Context, Sema, *p.second)};
}) |
ranges::to<std::unordered_map>();
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<clang::ASTConsumer> CreateTypeGraphAction::CreateASTConsumer(
clang::CompilerInstance& CI, clang::StringRef file) {
return std::make_unique<CreateTypeGraphConsumer>(ctx);
}
} // namespace
} // namespace oi::detail

View File

@ -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<std::string>&);
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<std::string> args_) {
clangArgs = std::move(args_);
}
private:
@ -50,20 +58,7 @@ class OIGenerator {
std::vector<std::filesystem::path> configFilePaths;
std::filesystem::path sourceFileDumpPath;
bool failIfNothingGenerated = false;
bool pic = false;
std::unordered_map<std::string, std::string> oilStrongToWeakSymbolsMap(
drgnplusplus::program& prog);
std::unordered_map<std::string, drgn_qualified_type> 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<std::string> clangArgs;
};
} // namespace oi::detail

View File

@ -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})

View File

@ -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 <clang/AST/ASTContext.h>
#include <clang/AST/Decl.h>
#include <clang/AST/DeclTemplate.h>
#include <clang/AST/QualTypeNames.h>
#include <clang/AST/Type.h>
#include <clang/Basic/DiagnosticSema.h>
#include <clang/Sema/Sema.h>
#include <glog/logging.h>
#include <iostream> // TODO: maybe remove
#include <regex>
#include <stdexcept> // 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<Incomplete>(ty, "incomplete (TODO naming)");
switch (ty.getTypeClass()) {
case clang::Type::Record:
return enumerateClass(llvm::cast<const clang::RecordType>(ty));
case clang::Type::LValueReference:
return enumerateReference(
llvm::cast<const clang::LValueReferenceType>(ty));
case clang::Type::Pointer:
return enumeratePointer(llvm::cast<const clang::PointerType>(ty));
case clang::Type::SubstTemplateTypeParm:
return enumerateSubstTemplateTypeParm(
llvm::cast<const clang::SubstTemplateTypeParmType>(ty));
case clang::Type::Builtin:
return enumeratePrimitive(llvm::cast<const clang::BuiltinType>(ty));
case clang::Type::Elaborated:
return enumerateElaboratedType(
llvm::cast<const clang::ElaboratedType>(ty));
case clang::Type::TemplateSpecialization:
return enumerateTemplateSpecialization(
llvm::cast<const clang::TemplateSpecializationType>(ty));
case clang::Type::UnaryTransform:
return enumerateUnaryTransformType(
llvm::cast<const clang::UnaryTransformType>(ty));
case clang::Type::Decltype:
return enumerateDecltypeType(llvm::cast<const clang::DecltypeType>(ty));
case clang::Type::Typedef:
return enumerateTypedef(llvm::cast<const clang::TypedefType>(ty));
case clang::Type::Using:
return enumerateUsing(llvm::cast<const clang::UsingType>(ty));
case clang::Type::ConstantArray:
return enumerateArray(llvm::cast<const clang::ConstantArrayType>(ty));
case clang::Type::Enum:
return enumerateEnum(llvm::cast<const clang::EnumType>(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<Typedef>(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<Typedef>(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<int64_t, std::string> enumeratorMap;
if (options_.readEnumValues) {
for (const auto* enumerator : ty.getDecl()->enumerators()) {
enumeratorMap.emplace(enumerator->getInitVal().getExtValue(),
enumerator->getNameAsString());
}
}
return makeType<Enum>(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<Array>(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<Primitive>(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<Container>(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<Class>(
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<TemplateParam>& params) {
assert(params.empty());
auto* decl = dyn_cast<clang::ClassTemplateSpecializationDecl>(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<TemplateParam> 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<TemplateParam> 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<Member>& 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<Incomplete*>(&t))
return makeType<Pointer>(ty, t);
return makeType<Reference>(ty, t);
}
Type& ClangTypeParser::enumeratePointer(const clang::PointerType& ty) {
// TODO: function references
if (!chasePointer())
return makeType<Primitive>(ty, Primitive::Kind::StubbedPointer);
Type& t = enumerateType(*ty.getPointeeType());
return makeType<Reference>(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<Primitive>(ty, Primitive::Kind::Void);
case clang::BuiltinType::Bool:
return makeType<Primitive>(ty, Primitive::Kind::Bool);
case clang::BuiltinType::Char_U:
case clang::BuiltinType::UChar:
return makeType<Primitive>(ty, Primitive::Kind::UInt8);
case clang::BuiltinType::WChar_U:
return makeType<Primitive>(ty, Primitive::Kind::UInt32);
case clang::BuiltinType::Char_S:
case clang::BuiltinType::SChar:
return makeType<Primitive>(ty, Primitive::Kind::Int8);
case clang::BuiltinType::WChar_S:
return makeType<Primitive>(ty, Primitive::Kind::Int32);
case clang::BuiltinType::Char16:
return makeType<Primitive>(ty, Primitive::Kind::Int16);
case clang::BuiltinType::Char32:
return makeType<Primitive>(ty, Primitive::Kind::Int32);
case clang::BuiltinType::UShort:
return makeType<Primitive>(ty, Primitive::Kind::UInt16);
case clang::BuiltinType::UInt:
return makeType<Primitive>(ty, Primitive::Kind::UInt32);
case clang::BuiltinType::ULong:
return makeType<Primitive>(ty, Primitive::Kind::UInt64);
case clang::BuiltinType::ULongLong:
return makeType<Primitive>(ty, Primitive::Kind::Int64);
case clang::BuiltinType::Short:
return makeType<Primitive>(ty, Primitive::Kind::Int16);
case clang::BuiltinType::Int:
return makeType<Primitive>(ty, Primitive::Kind::Int32);
case clang::BuiltinType::Long:
case clang::BuiltinType::LongLong:
return makeType<Primitive>(ty, Primitive::Kind::Int64);
case clang::BuiltinType::Float:
return makeType<Primitive>(ty, Primitive::Kind::Float32);
case clang::BuiltinType::Double:
case clang::BuiltinType::LongDouble:
return makeType<Primitive>(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

View File

@ -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 <cstdint>
#include <functional>
#include <unordered_map>
#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<std::unique_ptr<ContainerInfo>>& 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<std::unique_ptr<ContainerInfo>>& containers_;
ClangTypeParserOptions options_;
clang::ASTContext* ast;
clang::Sema* sema;
uint_fast32_t depth_;
std::unordered_map<const clang::Type*, std::reference_wrapper<Type>>
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<TemplateParam>&);
std::optional<TemplateParam> enumerateTemplateParam(
const clang::TemplateArgument&);
std::optional<TemplateParam> enumerateTemplateTemplateParam(
const clang::TemplateName&);
void enumerateClassMembers(const clang::RecordType&, std::vector<Member>&);
ContainerInfo* getContainerInfo(const std::string& fqName) const;
template <typename T, typename... Args>
T& makeType(const clang::Type& clangType, Args&&... args) {
auto& newType = typeGraph_.makeType<T>(std::forward<Args>(args)...);
clang_types_.insert({&clangType, newType});
return newType;
}
bool chasePointer() const;
};
} // namespace oi::detail::type_graph

View File

@ -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);
}

View File

@ -54,6 +54,7 @@ class DrgnExporter : public Visitor<drgn_type*> {
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;

View File

@ -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();

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -36,6 +36,7 @@ class RemoveTopLevelPointer : public LazyVisitor {
void removeTopLevelPointers(std::vector<std::reference_wrapper<Type>>& types);
void visit(Pointer& p) override;
void visit(Reference& p) override;
private:
Type* topLevelType_ = nullptr;

View File

@ -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<Type> pointeeType_;
std::string inputName_;
NodeId id_ = -1;
std::string name_;
};
/*
* Dummy
*

View File

@ -108,6 +108,9 @@ class RecursiveVisitor : public Visitor<void> {
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<Type&> {
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;
}

View File

@ -0,0 +1,16 @@
includes = ["vector", "utility", "string"]
definitions = '''
template <typename K, typename V, template <typename, typename> typename Pair>
struct bad_map {
std::vector<Pair<K, K>> keys;
std::vector<Pair<V, V>> values;
};
'''
[cases]
[cases.int_int_empty]
param_types = ["const bad_map<int, std::string, std::pair>&"]
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":[]}]'

View File

@ -14,131 +14,81 @@
* limitations under the License.
*/
#include <clang/Tooling/CommonOptionsParser.h>
#include <gflags/gflags.h>
#include <glog/logging.h>
#include <cstdlib>
#include <filesystem>
#include <iostream>
#include <range/v3/action/drop_while.hpp>
#include <range/v3/algorithm/find.hpp>
#include <range/v3/range/conversion.hpp>
#include <range/v3/view/all.hpp>
#include <range/v3/view/transform.hpp>
#include <vector>
#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,
"<file>",
"Write output(s) to file(s) with this prefix."},
OIOpt{'c',
"config-file",
required_argument,
"<oid.toml>",
"Path to OI configuration file."},
OIOpt{'d',
"debug-level",
required_argument,
"<level>",
"Verbose level for logging"},
OIOpt{'j',
"dump-jit",
optional_argument,
"<jit.cpp>",
"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<std::string> ConfigFiles("config-file",
llvm::cl::desc(R"(TODO HELP)"),
llvm::cl::cat(OilgenCategory));
static llvm::cl::opt<int> DebugLevel("debug-level",
llvm::cl::desc(R"(TODO HELP)"),
llvm::cl::init(-1),
llvm::cl::cat(OilgenCategory));
static llvm::cl::opt<std::string> 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<fs::path> 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<std::vector>());
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<bool>());
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());
}

View File

@ -35,3 +35,34 @@ void getSizeType(const %1%<T, Traits, Allocator> &container, size_t& returnArg)
);
}
"""
traversal_func = """
bool sso = ((uintptr_t)container.data() <
(uintptr_t)(&container + sizeof(std::__cxx11::basic_string<T0>))) &&
((uintptr_t)container.data() >= (uintptr_t)&container);
return returnArg.write(container.capacity())
.write(sso)
.write(container.size());
"""
[[codegen.processor]]
type = "types::st::VarInt<DB>"
func = """
uint64_t capacity = std::get<ParsedData::VarInt>(d.val).value;
el.container_stats.emplace(result::Element::ContainerStats { .capacity = capacity });
"""
[[codegen.processor]]
type = "types::st::VarInt<DB>"
func = """
bool sso = std::get<ParsedData::VarInt>(d.val).value;
if (!sso)
el.exclusive_size += el.container_stats->capacity * sizeof(T0);
"""
[[codegen.processor]]
type = "types::st::VarInt<DB>"
func = """
el.container_stats->length = std::get<ParsedData::VarInt>(d.val).value;
"""