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 <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:
hope

`buck2 run fbcode//mode/opt fbcode//object-introspection/oil/examples/compile-time:compile-time`

Reviewed By: tyroguru

Differential Revision: D51854477

Pulled By: JakeHillion
This commit is contained in:
Jake Hillion 2023-12-19 11:57:44 -08:00 committed by Facebook Community Bot
parent 37b89d789d
commit 55989a9156
20 changed files with 983 additions and 288 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,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<const Class*>(&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<ContainerInfo> 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_));

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_ = nullptr;
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

@ -16,130 +16,149 @@
#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 <string_view>
#include <range/v3/core.hpp>
#include <range/v3/view/drop.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>
#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/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 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<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 =
introspectImpl | 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;
auto els = 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)};
});
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<clang::ASTConsumer> CreateTypeGraphAction::CreateASTConsumer(
[[maybe_unused]] clang::CompilerInstance& CI,
[[maybe_unused]] 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,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 <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 <regex>
#include <stdexcept>
#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<Incomplete>(ty, std::move(fqName));
}
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);
}
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 pointers
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 clang::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,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();

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& r) 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& 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;

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& r) 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& r) {
topLevelType_ = &r.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& r) 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,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<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

@ -14,131 +14,80 @@
* 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/range/conversion.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"(</path/to/oid.toml>)"),
llvm::cl::cat(OilgenCategory));
static llvm::cl::opt<std::string> OutputFile(
"output",
llvm::cl::desc(R"(Write output to this file.)"),
llvm::cl::init("a.o"),
llvm::cl::cat(OilgenCategory));
static llvm::cl::opt<int> DebugLevel(
"debug-level",
llvm::cl::desc(R"(Verbose level for logging.)"),
llvm::cl::init(-1),
llvm::cl::cat(OilgenCategory));
static llvm::cl::opt<std::string> 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<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(OutputFile.getValue());
return oigen.generate(primaryObject, symbols);
oigen.setFailIfNothingGenerated(true);
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;
"""