Add model drop-types tool

This commit is contained in:
Alessandro Di Federico
2026-05-21 17:04:33 +02:00
parent c1d8d1fef1
commit e8fc9628ba
9 changed files with 368 additions and 1 deletions
+6 -1
View File
@@ -411,7 +411,12 @@ set(FILECHECK_TESTS
llvm-passes/select-scope.ll
llvm-passes/switch-to-statements.ll
llvm-passes/ternary-reduction.ll
llvm-passes/twoscomplement-normalization.ll)
llvm-passes/twoscomplement-normalization.ll
model/drop-types/ArrayPropagation.yml
model/drop-types/DropStructField.yml
model/drop-types/DropViaPointer.yml
model/drop-types/UnionCascade.yml
model/drop-types/UnionPartialDrop.yml)
foreach(F IN LISTS FILECHECK_TESTS)
add_filecheck_test(${F})
@@ -0,0 +1,51 @@
#
# This file is distributed under the MIT License. See LICENSE.md for details.
#
# Arrays do not break the dependency chain: a field of type `Array<victim>`
# is treated as a direct reference to `victim`, so dropping `victim` removes
# the field from the containing struct.
#
# RUN: %root/bin/revng model drop-types %s 1001-StructDefinition | FileCheck %s
Architecture: x86_64
TypeDefinitions:
# This type is dropped.
# CHECK-NOT: 1001-StructDefinition
- ID: 1001
Kind: StructDefinition
Name: "victim"
Size: 4
Fields:
- Offset: 0
Type:
Kind: PrimitiveType
PrimitiveKind: Generic
Size: 4
# Container survives; the array field (offset 0) is removed, only the
# primitive field at offset 16 stays.
# CHECK: ID: 1002
# CHECK: Name: container_with_array
# CHECK: Fields:
# CHECK-NEXT: - Offset: 16
# CHECK-NEXT: Type:
# CHECK-NEXT: Kind: PrimitiveType
# CHECK-NOT: - Offset: 0
- ID: 1002
Kind: StructDefinition
Name: "container_with_array"
Size: 20
Fields:
- Offset: 0
Type:
Kind: ArrayType
ElementCount: 4
ElementType:
Kind: DefinedType
Definition: "/TypeDefinitions/1001-StructDefinition"
- Offset: 16
Type:
Kind: PrimitiveType
PrimitiveKind: Generic
Size: 4
@@ -0,0 +1,47 @@
#
# This file is distributed under the MIT License. See LICENSE.md for details.
#
# Dropping a struct that is used as a direct (non-pointer) field type should
# remove that field from the containing struct, but the containing struct
# itself must survive (its other fields are unaffected).
#
# RUN: %root/bin/revng model drop-types %s 1001-StructDefinition | FileCheck %s
Architecture: x86_64
TypeDefinitions:
# This type is dropped — it must not appear in the output.
# CHECK-NOT: 1001-StructDefinition
# CHECK-NOT: Name: victim
- ID: 1001
Kind: StructDefinition
Name: "victim"
Size: 4
Fields:
- Offset: 0
Type:
Kind: PrimitiveType
PrimitiveKind: Generic
Size: 4
# The container survives; only the primitive field at offset 4 remains.
# CHECK: ID: 1002
# CHECK: Name: container
# CHECK: Fields:
# CHECK-NEXT: Offset: 4
# CHECK-NEXT: Type:
# CHECK-NEXT: Kind: PrimitiveType
- ID: 1002
Kind: StructDefinition
Name: "container"
Size: 8
Fields:
- Offset: 0
Type:
Kind: DefinedType
Definition: "/TypeDefinitions/1001-StructDefinition"
- Offset: 4
Type:
Kind: PrimitiveType
PrimitiveKind: Generic
Size: 4
@@ -0,0 +1,47 @@
#
# This file is distributed under the MIT License. See LICENSE.md for details.
#
# Dropping a struct that is only reached via a pointer should NOT remove the
# containing struct's field. Instead, the pointer's pointee is replaced with
# void.
#
# RUN: %root/bin/revng model drop-types %s 1001-StructDefinition | FileCheck %s
Architecture: x86_64
TypeDefinitions:
# This type is dropped — it must not appear in the output.
# CHECK-NOT: 1001-StructDefinition
- ID: 1001
Kind: StructDefinition
Name: "victim"
Size: 4
Fields:
- Offset: 0
Type:
Kind: PrimitiveType
PrimitiveKind: Generic
Size: 4
# The container survives with its pointer field; the pointee is now void.
# CHECK: ID: 1002
# CHECK: Name: container_with_ptr
# CHECK: Fields:
# CHECK-NEXT: Offset: 0
# CHECK-NEXT: Type:
# CHECK-NEXT: Kind: PointerType
# CHECK: PointeeType:
# CHECK-NEXT: Kind: PrimitiveType
# CHECK-NEXT: PrimitiveKind: Void
- ID: 1002
Kind: StructDefinition
Name: "container_with_ptr"
Size: 8
Fields:
- Offset: 0
Type:
Kind: PointerType
PointerSize: 8
PointeeType:
Kind: DefinedType
Definition: "/TypeDefinitions/1001-StructDefinition"
@@ -0,0 +1,67 @@
#
# This file is distributed under the MIT License. See LICENSE.md for details.
#
# When ALL fields of a union reference deleted types, the union itself must
# also be dropped and deletion cascades to its dependents (here, the field
# in `uses_u` referencing the union).
#
# RUN: %root/bin/revng model drop-types %s 1001-StructDefinition 1002-StructDefinition | FileCheck %s
Architecture: x86_64
TypeDefinitions:
# Dropped directly.
# CHECK-NOT: 1001-StructDefinition
- ID: 1001
Kind: StructDefinition
Name: "v1"
Size: 4
Fields:
- Offset: 0
Type:
Kind: PrimitiveType
PrimitiveKind: Generic
Size: 4
# Dropped directly.
# CHECK-NOT: 1002-StructDefinition
- ID: 1002
Kind: StructDefinition
Name: "v2"
Size: 4
Fields:
- Offset: 0
Type:
Kind: PrimitiveType
PrimitiveKind: Generic
Size: 4
# Both entries reference deleted types, so the union itself is cascaded.
# CHECK-NOT: 1003-UnionDefinition
# CHECK-NOT: UnionDefinition
- ID: 1003
Kind: UnionDefinition
Name: "u"
Fields:
- Index: 0
Type:
Kind: DefinedType
Definition: "/TypeDefinitions/1001-StructDefinition"
- Index: 1
Type:
Kind: DefinedType
Definition: "/TypeDefinitions/1002-StructDefinition"
# Survives but loses its only field (which referenced the cascaded union).
# CHECK: ID: 1004
# CHECK: Name: uses_u
# CHECK-NOT: Fields:
- ID: 1004
Kind: StructDefinition
Name: "uses_u"
Size: 4
Fields:
- Offset: 0
Type:
Kind: DefinedType
Definition: "/TypeDefinitions/1003-UnionDefinition"
@@ -0,0 +1,46 @@
#
# This file is distributed under the MIT License. See LICENSE.md for details.
#
# When only SOME of a union's fields reference deleted types, the union
# itself survives. The deleted entries are removed and the remaining ones
# are reindexed starting from zero.
#
# RUN: %root/bin/revng model drop-types %s 1001-StructDefinition | FileCheck %s
Architecture: x86_64
TypeDefinitions:
# This type is dropped.
# CHECK-NOT: 1001-StructDefinition
- ID: 1001
Kind: StructDefinition
Name: "victim"
Size: 4
Fields:
- Offset: 0
Type:
Kind: PrimitiveType
PrimitiveKind: Generic
Size: 4
# The union survives with only the primitive entry, reindexed to 0.
# CHECK: ID: 1002
# CHECK: Kind: UnionDefinition
# CHECK: Fields:
# CHECK-NEXT: - Index: 0
# CHECK-NEXT: Type:
# CHECK-NEXT: Kind: PrimitiveType
# CHECK-NOT: - Index: 1
- ID: 1002
Kind: UnionDefinition
Name: "u"
Fields:
- Index: 0
Type:
Kind: DefinedType
Definition: "/TypeDefinitions/1001-StructDefinition"
- Index: 1
Type:
Kind: PrimitiveType
PrimitiveKind: Generic
Size: 4
+1
View File
@@ -4,6 +4,7 @@
add_subdirectory(apply)
add_subdirectory(diff)
add_subdirectory(drop-types)
add_subdirectory(export)
add_subdirectory(import)
add_subdirectory(merge)
+7
View File
@@ -0,0 +1,7 @@
#
# This file is distributed under the MIT License. See LICENSE.md for details.
#
revng_add_executable(model-drop-types Main.cpp)
target_link_libraries(model-drop-types revngModel)
+96
View File
@@ -0,0 +1,96 @@
//
// This file is distributed under the MIT License. See LICENSE.md for details.
//
#include <string>
#include "llvm/Support/CommandLine.h"
#include "revng/Model/Binary.h"
#include "revng/Model/Processing.h"
#include "revng/Support/Debug.h"
#include "revng/Support/InitRevng.h"
#include "revng/Support/YAMLTraits.h"
namespace cl = llvm::cl;
static cl::OptionCategory ThisToolCategory("model-drop-types options", "");
static cl::opt<std::string> InputFilename(cl::Positional,
cl::cat(ThisToolCategory),
cl::desc("<input model>"),
cl::init("-"),
cl::value_desc("model"));
static cl::opt<std::string> OutputFilename("o",
cl::cat(ThisToolCategory),
cl::init("-"),
cl::desc("Override output "
"filename"),
cl::value_desc("filename"));
static cl::list<std::string> TypeKeys(cl::Positional,
cl::cat(ThisToolCategory),
cl::desc("<type keys to drop...>"),
cl::OneOrMore);
int main(int Argc, char *Argv[]) {
revng::InitRevng X(Argc, Argv, "", { &ThisToolCategory });
llvm::ExitOnError ExitOnError;
auto MaybeModel = TupleTree<model::Binary>::fromFileOrSTDIN(InputFilename);
if (not MaybeModel)
ExitOnError(MaybeModel.takeError());
auto &Model = *MaybeModel;
// Resolve the type keys into TypeDefinition pointers
std::set<const model::TypeDefinition *> TypesToDrop;
for (const std::string &KeyString : TypeKeys) {
// Keys have the form "ID-Kind", e.g. "42-StructDefinition"
auto SplitPoint = KeyString.find('-');
if (SplitPoint == std::string::npos) {
dbg << "Invalid type key (expected ID-Kind): " << KeyString << "\n";
return EXIT_FAILURE;
}
uint64_t ID = 0;
if (llvm::StringRef(KeyString).substr(0, SplitPoint).getAsInteger(10, ID)) {
dbg << "Invalid ID in type key: " << KeyString << "\n";
return EXIT_FAILURE;
}
llvm::StringRef KindName = llvm::StringRef(KeyString).substr(SplitPoint
+ 1);
auto Kind = model::TypeDefinitionKind::fromName(KindName);
if (Kind == model::TypeDefinitionKind::Invalid) {
dbg << "Unknown type kind: " << KindName.str() << "\n";
return EXIT_FAILURE;
}
// Look up the type in the model
bool Found = false;
for (const model::UpcastableTypeDefinition &Type :
Model->TypeDefinitions()) {
if (Type->ID() == ID && Type->Kind() == Kind) {
TypesToDrop.insert(Type.get());
Found = true;
break;
}
}
if (not Found) {
dbg << "Type not found in model: " << KeyString << "\n";
return EXIT_FAILURE;
}
}
unsigned Dropped = model::dropTypesDependingOnDefinitions(Model, TypesToDrop);
dbg << "Dropped " << Dropped << " type(s)\n";
ExitOnError(Model.toFile(OutputFilename));
return EXIT_SUCCESS;
}