mirror of
https://github.com/JakeHillion/object-introspection.git
synced 2024-09-20 03:29:05 +01:00
TypeGraph: Add Prune pass
This lets us remove fields from types when they are no longer needed, speeding up later passes. A secondary benefit of pruning unused types means that we sometimes remove types for which we can't generate correct C++ code. This can allow us to CodeGen for complex types which reference these broken types without actually requiring them (e.g. as template parameters). Add a new feature flag "prune-type-graph" to control this pass. It makes sense to prune most of the time, but for testing CodeGen functionality on a wider range of types, it will be useful to have the option to not prune.
This commit is contained in:
parent
bc895dc9cc
commit
45c3697f6b
@ -31,6 +31,7 @@
|
|||||||
#include "type_graph/DrgnParser.h"
|
#include "type_graph/DrgnParser.h"
|
||||||
#include "type_graph/Flattener.h"
|
#include "type_graph/Flattener.h"
|
||||||
#include "type_graph/NameGen.h"
|
#include "type_graph/NameGen.h"
|
||||||
|
#include "type_graph/Prune.h"
|
||||||
#include "type_graph/RemoveMembers.h"
|
#include "type_graph/RemoveMembers.h"
|
||||||
#include "type_graph/RemoveTopLevelPointer.h"
|
#include "type_graph/RemoveTopLevelPointer.h"
|
||||||
#include "type_graph/TopoSorter.h"
|
#include "type_graph/TopoSorter.h"
|
||||||
@ -38,13 +39,24 @@
|
|||||||
#include "type_graph/TypeIdentifier.h"
|
#include "type_graph/TypeIdentifier.h"
|
||||||
#include "type_graph/Types.h"
|
#include "type_graph/Types.h"
|
||||||
|
|
||||||
|
using type_graph::AddChildren;
|
||||||
|
using type_graph::AddPadding;
|
||||||
|
using type_graph::AlignmentCalc;
|
||||||
using type_graph::Class;
|
using type_graph::Class;
|
||||||
using type_graph::Container;
|
using type_graph::Container;
|
||||||
|
using type_graph::DrgnParser;
|
||||||
using type_graph::Enum;
|
using type_graph::Enum;
|
||||||
|
using type_graph::Flattener;
|
||||||
using type_graph::Member;
|
using type_graph::Member;
|
||||||
|
using type_graph::NameGen;
|
||||||
|
using type_graph::Prune;
|
||||||
|
using type_graph::RemoveMembers;
|
||||||
|
using type_graph::RemoveTopLevelPointer;
|
||||||
|
using type_graph::TopoSorter;
|
||||||
using type_graph::Type;
|
using type_graph::Type;
|
||||||
using type_graph::Typedef;
|
using type_graph::Typedef;
|
||||||
using type_graph::TypeGraph;
|
using type_graph::TypeGraph;
|
||||||
|
using type_graph::TypeIdentifier;
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
using ref = std::reference_wrapper<T>;
|
using ref = std::reference_wrapper<T>;
|
||||||
@ -407,7 +419,7 @@ void CodeGen::getClassSizeFuncConcrete(std::string_view funcName,
|
|||||||
|
|
||||||
for (size_t i = 0; i < c.members.size(); i++) {
|
for (size_t i = 0; i < c.members.size(); i++) {
|
||||||
const auto& member = c.members[i];
|
const auto& member = c.members[i];
|
||||||
if (member.name.starts_with(type_graph::AddPadding::MemberPrefix))
|
if (member.name.starts_with(AddPadding::MemberPrefix))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (thriftIssetMember && thriftIssetMember != &member) {
|
if (thriftIssetMember && thriftIssetMember != &member) {
|
||||||
@ -610,7 +622,7 @@ void CodeGen::getClassTypeHandler(const Class& c, std::string& code) {
|
|||||||
size_t lastNonPaddingElement = -1;
|
size_t lastNonPaddingElement = -1;
|
||||||
for (size_t i = 0; i < c.members.size(); i++) {
|
for (size_t i = 0; i < c.members.size(); i++) {
|
||||||
const auto& el = c.members[i];
|
const auto& el = c.members[i];
|
||||||
if (!el.name.starts_with(type_graph::AddPadding::MemberPrefix)) {
|
if (!el.name.starts_with(AddPadding::MemberPrefix)) {
|
||||||
lastNonPaddingElement = i;
|
lastNonPaddingElement = i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -627,7 +639,7 @@ void CodeGen::getClassTypeHandler(const Class& c, std::string& code) {
|
|||||||
|
|
||||||
for (size_t i = 0; i < lastNonPaddingElement + 1; i++) {
|
for (size_t i = 0; i < lastNonPaddingElement + 1; i++) {
|
||||||
const auto& member = c.members[i];
|
const auto& member = c.members[i];
|
||||||
if (member.name.starts_with(type_graph::AddPadding::MemberPrefix)) {
|
if (member.name.starts_with(AddPadding::MemberPrefix)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -671,7 +683,7 @@ void CodeGen::getClassTypeHandler(const Class& c, std::string& code) {
|
|||||||
{
|
{
|
||||||
for (size_t i = 0; i < lastNonPaddingElement + 1; i++) {
|
for (size_t i = 0; i < lastNonPaddingElement + 1; i++) {
|
||||||
const auto& member = c.members[i];
|
const auto& member = c.members[i];
|
||||||
if (member.name.starts_with(type_graph::AddPadding::MemberPrefix)) {
|
if (member.name.starts_with(AddPadding::MemberPrefix)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -761,7 +773,7 @@ bool CodeGen::codegenFromDrgn(struct drgn_type* drgnType, std::string& code) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
type_graph::TypeGraph typeGraph;
|
TypeGraph typeGraph;
|
||||||
try {
|
try {
|
||||||
addDrgnRoot(drgnType, typeGraph);
|
addDrgnRoot(drgnType, typeGraph);
|
||||||
} catch (const type_graph::DrgnParserError& err) {
|
} catch (const type_graph::DrgnParserError& err) {
|
||||||
@ -779,46 +791,47 @@ void CodeGen::registerContainer(const fs::path& path) {
|
|||||||
VLOG(1) << "Registered container: " << info.typeName;
|
VLOG(1) << "Registered container: " << info.typeName;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CodeGen::addDrgnRoot(struct drgn_type* drgnType,
|
void CodeGen::addDrgnRoot(struct drgn_type* drgnType, TypeGraph& typeGraph) {
|
||||||
type_graph::TypeGraph& typeGraph) {
|
DrgnParser drgnParser{typeGraph, containerInfos_,
|
||||||
type_graph::DrgnParser drgnParser{
|
config_.features[Feature::ChaseRawPointers]};
|
||||||
typeGraph, containerInfos_, config_.features[Feature::ChaseRawPointers]};
|
|
||||||
Type& parsedRoot = drgnParser.parse(drgnType);
|
Type& parsedRoot = drgnParser.parse(drgnType);
|
||||||
typeGraph.addRoot(parsedRoot);
|
typeGraph.addRoot(parsedRoot);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CodeGen::transform(type_graph::TypeGraph& typeGraph) {
|
void CodeGen::transform(TypeGraph& typeGraph) {
|
||||||
type_graph::PassManager pm;
|
type_graph::PassManager pm;
|
||||||
|
|
||||||
// Simplify the type graph first so there is less work for later passes
|
// Simplify the type graph first so there is less work for later passes
|
||||||
pm.addPass(type_graph::RemoveTopLevelPointer::createPass());
|
pm.addPass(RemoveTopLevelPointer::createPass());
|
||||||
pm.addPass(type_graph::Flattener::createPass());
|
pm.addPass(Flattener::createPass());
|
||||||
pm.addPass(type_graph::TypeIdentifier::createPass(config_.passThroughTypes));
|
pm.addPass(TypeIdentifier::createPass(config_.passThroughTypes));
|
||||||
|
if (config_.features[Feature::PruneTypeGraph])
|
||||||
|
pm.addPass(Prune::createPass());
|
||||||
|
|
||||||
if (config_.features[Feature::PolymorphicInheritance]) {
|
if (config_.features[Feature::PolymorphicInheritance]) {
|
||||||
// Parse new children nodes
|
// Parse new children nodes
|
||||||
type_graph::DrgnParser drgnParser{
|
DrgnParser drgnParser{typeGraph, containerInfos_,
|
||||||
typeGraph, containerInfos_,
|
config_.features[Feature::ChaseRawPointers]};
|
||||||
config_.features[Feature::ChaseRawPointers]};
|
pm.addPass(AddChildren::createPass(drgnParser, symbols_));
|
||||||
pm.addPass(type_graph::AddChildren::createPass(drgnParser, symbols_));
|
|
||||||
|
|
||||||
// Re-run passes over newly added children
|
// Re-run passes over newly added children
|
||||||
pm.addPass(type_graph::Flattener::createPass());
|
pm.addPass(Flattener::createPass());
|
||||||
pm.addPass(
|
pm.addPass(TypeIdentifier::createPass(config_.passThroughTypes));
|
||||||
type_graph::TypeIdentifier::createPass(config_.passThroughTypes));
|
if (config_.features[Feature::PruneTypeGraph])
|
||||||
|
pm.addPass(Prune::createPass());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate alignment before removing members, as those members may have an
|
// Calculate alignment before removing members, as those members may have an
|
||||||
// influence on the class' overall alignment.
|
// influence on the class' overall alignment.
|
||||||
pm.addPass(type_graph::AlignmentCalc::createPass());
|
pm.addPass(AlignmentCalc::createPass());
|
||||||
pm.addPass(type_graph::RemoveMembers::createPass(config_.membersToStub));
|
pm.addPass(RemoveMembers::createPass(config_.membersToStub));
|
||||||
|
|
||||||
// Add padding to fill in the gaps of removed members and ensure their
|
// Add padding to fill in the gaps of removed members and ensure their
|
||||||
// alignments
|
// alignments
|
||||||
pm.addPass(type_graph::AddPadding::createPass(config_.features));
|
pm.addPass(AddPadding::createPass(config_.features));
|
||||||
|
|
||||||
pm.addPass(type_graph::NameGen::createPass());
|
pm.addPass(NameGen::createPass());
|
||||||
pm.addPass(type_graph::TopoSorter::createPass());
|
pm.addPass(TopoSorter::createPass());
|
||||||
|
|
||||||
pm.run(typeGraph);
|
pm.run(typeGraph);
|
||||||
|
|
||||||
@ -829,7 +842,7 @@ void CodeGen::transform(type_graph::TypeGraph& typeGraph) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CodeGen::generate(
|
void CodeGen::generate(
|
||||||
type_graph::TypeGraph& typeGraph,
|
TypeGraph& typeGraph,
|
||||||
std::string& code,
|
std::string& code,
|
||||||
struct drgn_type* drgnType /* TODO: this argument should not be required */
|
struct drgn_type* drgnType /* TODO: this argument should not be required */
|
||||||
) {
|
) {
|
||||||
|
@ -34,6 +34,8 @@ std::string_view featureHelp(Feature f) {
|
|||||||
return "Capture isset data for Thrift object.";
|
return "Capture isset data for Thrift object.";
|
||||||
case Feature::TypeGraph:
|
case Feature::TypeGraph:
|
||||||
return "Use Type Graph for code generation (CodeGen v2).";
|
return "Use Type Graph for code generation (CodeGen v2).";
|
||||||
|
case Feature::PruneTypeGraph:
|
||||||
|
return "Prune unreachable nodes from the type graph";
|
||||||
case Feature::TypedDataSegment:
|
case Feature::TypedDataSegment:
|
||||||
return "Use Typed Data Segment in generated code.";
|
return "Use Typed Data Segment in generated code.";
|
||||||
case Feature::TreeBuilderTypeChecking:
|
case Feature::TreeBuilderTypeChecking:
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
X(GenPaddingStats, "gen-padding-stats") \
|
X(GenPaddingStats, "gen-padding-stats") \
|
||||||
X(CaptureThriftIsset, "capture-thrift-isset") \
|
X(CaptureThriftIsset, "capture-thrift-isset") \
|
||||||
X(TypeGraph, "type-graph") \
|
X(TypeGraph, "type-graph") \
|
||||||
|
X(PruneTypeGraph, "prune-type-graph") \
|
||||||
X(TypedDataSegment, "typed-data-segment") \
|
X(TypedDataSegment, "typed-data-segment") \
|
||||||
X(TreeBuilderTypeChecking, "tree-builder-type-checking") \
|
X(TreeBuilderTypeChecking, "tree-builder-type-checking") \
|
||||||
X(TreeBuilderV2, "tree-builder-v2") \
|
X(TreeBuilderV2, "tree-builder-v2") \
|
||||||
|
@ -468,6 +468,7 @@ int main(int argc, char* argv[]) {
|
|||||||
std::map<Feature, bool> features = {
|
std::map<Feature, bool> features = {
|
||||||
{Feature::PackStructs, true},
|
{Feature::PackStructs, true},
|
||||||
{Feature::GenPaddingStats, true},
|
{Feature::GenPaddingStats, true},
|
||||||
|
{Feature::PruneTypeGraph, true},
|
||||||
};
|
};
|
||||||
|
|
||||||
bool logAllStructs = true;
|
bool logAllStructs = true;
|
||||||
|
@ -185,6 +185,7 @@ int OIGenerator::generate(fs::path& primaryObject, SymbolService& symbols) {
|
|||||||
|
|
||||||
std::map<Feature, bool> featuresMap = {
|
std::map<Feature, bool> featuresMap = {
|
||||||
{Feature::PackStructs, true},
|
{Feature::PackStructs, true},
|
||||||
|
{Feature::PruneTypeGraph, true},
|
||||||
};
|
};
|
||||||
|
|
||||||
OICodeGen::Config generatorConfig{};
|
OICodeGen::Config generatorConfig{};
|
||||||
|
@ -89,6 +89,7 @@ bool OILibraryImpl::processConfigFile() {
|
|||||||
{
|
{
|
||||||
{Feature::ChaseRawPointers, _self->opts.chaseRawPointers},
|
{Feature::ChaseRawPointers, _self->opts.chaseRawPointers},
|
||||||
{Feature::PackStructs, true},
|
{Feature::PackStructs, true},
|
||||||
|
{Feature::PruneTypeGraph, true},
|
||||||
{Feature::GenJitDebug, _self->opts.generateJitDebugInfo},
|
{Feature::GenJitDebug, _self->opts.generateJitDebugInfo},
|
||||||
},
|
},
|
||||||
compilerConfig, generatorConfig);
|
compilerConfig, generatorConfig);
|
||||||
|
@ -7,6 +7,7 @@ add_library(type_graph
|
|||||||
NameGen.cpp
|
NameGen.cpp
|
||||||
PassManager.cpp
|
PassManager.cpp
|
||||||
Printer.cpp
|
Printer.cpp
|
||||||
|
Prune.cpp
|
||||||
RemoveMembers.cpp
|
RemoveMembers.cpp
|
||||||
RemoveTopLevelPointer.cpp
|
RemoveTopLevelPointer.cpp
|
||||||
TopoSorter.cpp
|
TopoSorter.cpp
|
||||||
|
65
oi/type_graph/Prune.cpp
Normal file
65
oi/type_graph/Prune.cpp
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* 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 "Prune.h"
|
||||||
|
|
||||||
|
#include "TypeGraph.h"
|
||||||
|
#include "TypeIdentifier.h"
|
||||||
|
|
||||||
|
namespace type_graph {
|
||||||
|
|
||||||
|
Pass Prune::createPass() {
|
||||||
|
auto fn = [](TypeGraph& typeGraph) {
|
||||||
|
Prune pass{typeGraph.resetTracker()};
|
||||||
|
for (auto& type : typeGraph.rootTypes()) {
|
||||||
|
pass.accept(type);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return Pass("Prune", fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Prune::accept(Type& type) {
|
||||||
|
if (tracker_.visit(type))
|
||||||
|
return;
|
||||||
|
|
||||||
|
type.accept(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Prune::visit(Class& c) {
|
||||||
|
for (const auto& param : c.templateParams) {
|
||||||
|
accept(param.type());
|
||||||
|
}
|
||||||
|
for (const auto& parent : c.parents) {
|
||||||
|
accept(parent.type());
|
||||||
|
}
|
||||||
|
for (const auto& member : c.members) {
|
||||||
|
accept(member.type());
|
||||||
|
}
|
||||||
|
for (const auto& child : c.children) {
|
||||||
|
accept(child);
|
||||||
|
}
|
||||||
|
|
||||||
|
c.templateParams.clear();
|
||||||
|
c.parents.clear();
|
||||||
|
c.functions.clear();
|
||||||
|
|
||||||
|
// Should we bother with this shrinking? It saves memory but costs CPU
|
||||||
|
c.templateParams.shrink_to_fit();
|
||||||
|
c.parents.shrink_to_fit();
|
||||||
|
c.functions.shrink_to_fit();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace type_graph
|
51
oi/type_graph/Prune.h
Normal file
51
oi/type_graph/Prune.h
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* 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 <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "NodeTracker.h"
|
||||||
|
#include "PassManager.h"
|
||||||
|
#include "Types.h"
|
||||||
|
#include "Visitor.h"
|
||||||
|
|
||||||
|
namespace type_graph {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prune
|
||||||
|
*
|
||||||
|
* Removes unnecessary information from a type graph and releases memory where
|
||||||
|
* possible.
|
||||||
|
*/
|
||||||
|
class Prune : public RecursiveVisitor {
|
||||||
|
public:
|
||||||
|
static Pass createPass();
|
||||||
|
|
||||||
|
Prune(NodeTracker& tracker) : tracker_(tracker) {
|
||||||
|
}
|
||||||
|
|
||||||
|
using RecursiveVisitor::accept;
|
||||||
|
using RecursiveVisitor::visit;
|
||||||
|
|
||||||
|
void accept(Type& type) override;
|
||||||
|
void visit(Class& c) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
NodeTracker& tracker_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace type_graph
|
@ -44,6 +44,7 @@ add_executable(test_type_graph
|
|||||||
test_flattener.cpp
|
test_flattener.cpp
|
||||||
test_name_gen.cpp
|
test_name_gen.cpp
|
||||||
test_node_tracker.cpp
|
test_node_tracker.cpp
|
||||||
|
test_prune.cpp
|
||||||
test_remove_members.cpp
|
test_remove_members.cpp
|
||||||
test_remove_top_level_pointer.cpp
|
test_remove_top_level_pointer.cpp
|
||||||
test_topo_sorter.cpp
|
test_topo_sorter.cpp
|
||||||
|
61
test/test_prune.cpp
Normal file
61
test/test_prune.cpp
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include "oi/type_graph/Prune.h"
|
||||||
|
#include "test/type_graph_utils.h"
|
||||||
|
|
||||||
|
using type_graph::Prune;
|
||||||
|
|
||||||
|
TEST(PruneTest, PruneClass) {
|
||||||
|
test(Prune::createPass(), R"(
|
||||||
|
[0] Class: MyClass (size: 8)
|
||||||
|
Param
|
||||||
|
Primitive: int32_t
|
||||||
|
Param
|
||||||
|
Value: "123"
|
||||||
|
Parent (offset: 0)
|
||||||
|
[1] Class: MyParent (size: 4)
|
||||||
|
Member: a (offset: 0)
|
||||||
|
Primitive: int32_t
|
||||||
|
Member: a (offset: 0)
|
||||||
|
Primitive: int32_t
|
||||||
|
Member: b (offset: 4)
|
||||||
|
Primitive: int32_t
|
||||||
|
Function: foo
|
||||||
|
Function: bar
|
||||||
|
)",
|
||||||
|
R"(
|
||||||
|
[0] Class: MyClass (size: 8)
|
||||||
|
Member: a (offset: 0)
|
||||||
|
Primitive: int32_t
|
||||||
|
Member: b (offset: 4)
|
||||||
|
Primitive: int32_t
|
||||||
|
)");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(PruneTest, RecurseClassMember) {
|
||||||
|
test(Prune::createPass(), R"(
|
||||||
|
[0] Class: MyClass (size: 0)
|
||||||
|
Member: xxx (offset: 0)
|
||||||
|
[1] Class: ClassA (size: 12)
|
||||||
|
Function: foo
|
||||||
|
)",
|
||||||
|
R"(
|
||||||
|
[0] Class: MyClass (size: 0)
|
||||||
|
Member: xxx (offset: 0)
|
||||||
|
[1] Class: ClassA (size: 12)
|
||||||
|
)");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(PruneTest, RecurseClassChild) {
|
||||||
|
test(Prune::createPass(), R"(
|
||||||
|
[0] Class: MyClass (size: 0)
|
||||||
|
Child
|
||||||
|
[1] Class: ClassA (size: 12)
|
||||||
|
Function: foo
|
||||||
|
)",
|
||||||
|
R"(
|
||||||
|
[0] Class: MyClass (size: 0)
|
||||||
|
Child
|
||||||
|
[1] Class: ClassA (size: 12)
|
||||||
|
)");
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user