Drop type inlining

Type inlining was a feature that allowed type definitions of
structs/unions/enums to be printed in C directly inside the definition
of another parent struct/union, if the inner type was only used once in
the parent type.

This kind of reasoning is inherently global: a type definition of the
subtype can be inlined in the parent type one only if *globally* the
subtype it isn't referred anywhere else.

This caused issues with type inlining inside definitions of stack types
in the body of functions. Indeed, for a given function, due to type
inlining, it was necessary to do global reasoning about what other types
could be inlined in the definition of the function's stack frame type.
This, in turn, had heavy consequences on invalidation, because any
change to any type (even if it wasn't referred in a given function's
body) was causing invalidation of all functions' bodies.

For this reason it was decided to drop the type inlining feature.
This commit is contained in:
Pietro Fezzardi
2025-03-27 10:13:17 +01:00
parent 82e57e1513
commit ea3cbe4c1b
29 changed files with 62 additions and 641 deletions
+1 -4
View File
@@ -8,10 +8,7 @@
namespace revng::options {
// Disabled by default.
extern llvm::cl::opt<bool> EnableTypeInlining;
// Enabled by default.
extern llvm::cl::opt<bool> DisableStackFrameInlining;
extern llvm::cl::opt<bool> EnableStackFrameInlining;
} // namespace revng::options
+9 -52
View File
@@ -26,14 +26,15 @@ protected:
public:
struct ConfigurationOptions {
/// When set to true, the types that are only ever depended on by a single
/// other type (for example, a nested struct), are printed inside the parent
/// type definition instead of separately.
bool EnableTypeInlining = true;
/// When set to true, function stack frame types are printed inside the
/// struct body as opposed to the type header.
bool EnableStackFrameInlining = true;
/// When set to true, function stack frame types are printed also inside the
/// struct body, and not just in the header.
/// This may break the property that we emit syntactically valid C code,
/// because including the header will cause the definition of the stack
/// frame type to be duplicated in the header and inside the function body.
/// For this reason it's disabled by default. And it should only be turned
/// on when printing artifacts that only show the body of the function, for
/// which emitting recompilable C code is not a strict requirement.
bool EnableStackFrameInlining = false;
/// Because we are emitting C11, we cannot specify underlying enum type
/// (the feature was only backported from C++ in C23), which means that
@@ -61,54 +62,10 @@ public:
const ConfigurationOptions Configuration;
private:
/// This is the cache containing the names of artificial types (like array
/// wrappers).
std::map<model::UpcastableType, std::string> ArtificialNameCache = {};
/// This is the cache containing the dependency data for the types.
/// It is here so that we don't have to recompute it with multiple invocations
std::optional<DependencyGraph> DependencyCache = std::nullopt;
/// This is the cache containing the keys of the types that should be inlined
/// into their only user.
std::set<model::TypeDefinition::Key> TypesToInlineCache = {};
/// This is the cache containing the keys of the stack frame types that
/// should be inlined into the functions they belong to.
std::set<model::TypeDefinition::Key> StackFrameTypeCache = {};
/// Is only set to true if \ref collectInlinableTypes was invoked.
bool InlinableCacheIsReady = false;
public:
/// Gather (and store internally) the list of types that can (and should)
/// be inlined. This list is then later used by the invocations of
/// \ref printDefinition.
void collectInlinableTypes();
bool shouldInline(model::TypeDefinition::Key Key) const {
revng_assert(InlinableCacheIsReady,
"`shouldInline` must not be called before "
"`collectInlinableTypes`.");
if (not TypesToInlineCache.contains(Key)) {
// This type is not allowed be inlined.
return false;
}
if (StackFrameTypeCache.contains(Key)) {
// This is a stack frame.
return Configuration.EnableStackFrameInlining;
} else {
return Configuration.EnableTypeInlining;
}
}
bool shouldInline(const model::TypeDefinition &Type) const {
return shouldInline(Type.key());
}
public:
CTypeBuilder(llvm::raw_ostream &OutputStream,
const model::Binary &Binary,
+7 -2
View File
@@ -1928,14 +1928,19 @@ void CCodeGenerator::emitFunction(bool NeedsLocalStateVar) {
revng_assert(not IsStackDefined, "Multiple stack variables?");
const model::StructDefinition &Struct = *ModelFunction.stackFrameType();
if (B.shouldInline(Struct.key())) {
// In the artifacts generated by this LLVM-based backend we've given up
// the requirement of emitting syntactically valid C code. Hence, we can
// always emit the stack frame type definition inside the body of the
// function itself, because we don't care anymore if it clashes with a
// global definition that we have emitted in a header outside the
// function's body.
if (B.Configuration.EnableStackFrameInlining) {
B.printDefinition(Struct, " " + std::move(VarName));
} else {
auto Named = B.getNamedCInstance(*ModelFunction.StackFrameType(),
std::move(VarName));
B.append(Named + ";\n");
}
IsStackDefined = true;
} else {
+1 -3
View File
@@ -136,9 +136,7 @@ void Decompile::run(pipeline::ExecutionContext &EC,
B(llvm::nulls(),
Model,
/* EnableTaglessMode = */ false,
{ .EnableTypeInlining = options::EnableTypeInlining,
.EnableStackFrameInlining = !options::DisableStackFrameInlining });
B.collectInlinableTypes();
{ .EnableStackFrameInlining = options::EnableStackFrameInlining });
for (const model::Function &Function :
getFunctionsAndCommit(EC, DecompiledFunctions.name())) {
+11 -7
View File
@@ -38,13 +38,17 @@ void DecompileToDirectory::run(pipeline::ExecutionContext &EC,
const model::Binary &Model = *getModelFromContext(EC);
namespace options = revng::options;
ptml::CTypeBuilder
B(llvm::nulls(),
Model,
/* EnableTaglessMode = */ true,
{ .EnableTypeInlining = options::EnableTypeInlining,
.EnableStackFrameInlining = !options::DisableStackFrameInlining });
B.collectInlinableTypes();
ptml::CTypeBuilder B(llvm::nulls(),
Model,
/* EnableTaglessMode = */ true,
// Disable stack frame inlining because enabling it could
// break the property that we emit syntactically valid C
// code, due to the stack frame type definition being
// duplicated in the global header and in the function's
// body. In this artifact, where all the decompiled
// functions are put in a single .c file, recompilability
// is still important.
{ .EnableStackFrameInlining = false });
{
ControlFlowGraphCache Cache{ CFGMap };
+10 -7
View File
@@ -26,13 +26,16 @@ void DecompileToSingleFile::run(pipeline::ExecutionContext &EC,
llvm::raw_string_ostream Out = OutCFile.asStream();
namespace options = revng::options;
ptml::CTypeBuilder
B(Out,
*getModelFromContext(EC),
/* EnableTaglessMode = */ false,
{ .EnableTypeInlining = options::EnableTypeInlining,
.EnableStackFrameInlining = !options::DisableStackFrameInlining });
B.collectInlinableTypes();
ptml::CTypeBuilder B(Out,
*getModelFromContext(EC),
/* EnableTaglessMode = */ false,
// Disable stack frame inlining because enabling it could
// break the property that we emit syntactically valid C
// code, due to the stack frame type definition being
// duplicated in the global header and in the function's
// body. In the single file artifact recompilability is
// still important.
{ .EnableStackFrameInlining = false });
// Make a single C file with an empty set of targets, which means all the
// functions in DecompiledFunctions
-1
View File
@@ -28,7 +28,6 @@
static Logger<> Log{ "model-to-header" };
bool ptml::HeaderBuilder::printModelHeader() {
B.collectInlinableTypes();
auto Scope = B.getScopeTag(ptml::tags::Div);
+1 -13
View File
@@ -10,17 +10,6 @@
#include "revng/Pipes/Kinds.h"
#include "revng/Pipes/ModelGlobal.h"
static llvm::cl::opt<bool> InlineTypes("inline-types",
llvm::cl::desc("Enable printing struct, "
"union and enum types "
"inline in their parent "
"types. This also "
"enables printing stack "
"types definitions "
"inline in the function "
"body."),
llvm::cl::init(false));
namespace revng::pipes {
inline constexpr char ModelHeaderFileContainerMIMEType[] = "text/x.c+ptml";
@@ -64,8 +53,7 @@ public:
B(Header,
*getModelFromContext(EC),
/* EnableTaglessMode = */ false,
{ .EnableTypeInlining = options::EnableTypeInlining,
.EnableStackFrameInlining = !options::DisableStackFrameInlining,
{ .EnableStackFrameInlining = options::EnableStackFrameInlining,
.EnablePrintingOfTheMaximumEnumValue = true });
ptml::HeaderBuilder(B).printModelHeader();
@@ -50,7 +50,6 @@ public:
{ .EnablePrintingOfTheMaximumEnumValue = true,
.EnableExplicitPaddingMode = false,
.EnableStructSizeAnnotation = true });
B.collectInlinableTypes();
B.printDefinition(Type);
}
+5 -18
View File
@@ -8,23 +8,10 @@ using namespace llvm::cl;
namespace revng::options {
/// TODO: type inlining is currently broken in rare cases involving recursive
/// array struct fields. That's why it's disabled by default.
/// When the bugs a fixed, turn this option into `disable-type-inlining`
/// and reverse it's using.
opt<bool> EnableTypeInlining("enable-type-inlining",
desc("Enable printing struct, union and enum "
"types inline in their parent types if "
"they're only used once. This also enables "
"printing stack types definitions inline "
"in "
"the function body."),
init(false));
opt<bool> DisableStackFrameInlining("disable-stack-frame-inlining",
desc("Disable printing the definition "
"of the function stack type inside "
"its body."),
init(false));
opt<bool> EnableStackFrameInlining("enable-stack-frame-inlining",
desc("Enable printing the definition "
"of a function's stack type inside "
"the function's body."),
init(false));
} // namespace revng::options
+1 -1
View File
@@ -143,7 +143,7 @@ struct ImportFromCAnalysis {
}
ptml::CTypeBuilder::ConfigurationOptions Configuration = {
.EnableTypeInlining = false, .EnableStackFrameInlining = false
.EnableStackFrameInlining = false
};
ptml::HeaderBuilder::ConfigurationOptions HeaderConfiguration = {};
if (TheOption == ImportFromCOption::EditType) {
+9 -103
View File
@@ -106,16 +106,10 @@ void ptml::CTypeBuilder::printDefinition(const model::StructDefinition &S,
for (const auto &Field : S.Fields()) {
printPadding(PreviousOffset, Field.Offset());
auto *Definition = Field.Type()->skipToDefinition();
if (not Definition or not shouldInline(*Definition)) {
auto F = getDefinitionTag(S, Field);
std::string Result = getModelComment(Field)
+ getNamedCInstance(*Field.Type(), F) + ';';
*Out << getCommentableTag(std::move(Result), S, Field) << '\n';
} else {
printInlineDefinition(NameBuilder.name(S, Field), *Field.Type());
}
auto F = getDefinitionTag(S, Field);
std::string Result = getModelComment(Field)
+ getNamedCInstance(*Field.Type(), F) + ';';
*Out << getCommentableTag(std::move(Result), S, Field) << '\n';
PreviousOffset = Field.Offset() + Field.Type()->size().value();
}
@@ -137,16 +131,10 @@ void ptml::CTypeBuilder::printDefinition(const model::UnionDefinition &U,
{
Scope Scope(*Out, ptml::c::scopes::UnionBody);
for (const auto &Field : U.Fields()) {
auto *Definition = Field.Type()->skipToDefinition();
if (not Definition or not shouldInline(*Definition)) {
auto F = getDefinitionTag(U, Field);
std::string Result = getModelComment(Field)
+ getNamedCInstance(*Field.Type(), F) + ';';
*Out << getCommentableTag(std::move(Result), U, Field) << '\n';
} else {
printInlineDefinition(NameBuilder.name(U, Field), *Field.Type());
}
auto F = getDefinitionTag(U, Field);
std::string Result = getModelComment(Field)
+ getNamedCInstance(*Field.Type(), F) + ';';
*Out << getCommentableTag(std::move(Result), U, Field) << '\n';
}
}
@@ -296,88 +284,6 @@ void ptml::CTypeBuilder::printInlineDefinition(llvm::StringRef Name,
}
}
static Logger<> InlineTypeLog{ "inline-type-selection" };
void ptml::CTypeBuilder::collectInlinableTypes() {
if (not DependencyCache.has_value())
DependencyCache = DependencyGraph::make(Binary.TypeDefinitions());
StackFrameTypeCache = {};
for (const model::Function &Function : Binary.Functions())
if (auto *StackFrame = Function.stackFrameType())
StackFrameTypeCache.insert(StackFrame->key());
if (Configuration.EnableTypeInlining
|| Configuration.EnableStackFrameInlining) {
std::map<model::TypeDefinition::Key, uint64_t> DependentTypeCount;
for (const auto *Node : DependencyCache->nodes()) {
if (isDeclarationTheSameAsDefinition(*Node->T)) {
// Skip types that never produce a definition since there's no point
// inlining them.
continue;
}
auto &&[Iterator, _] = DependentTypeCount.try_emplace(Node->T->key(), 0);
Iterator->second += Node->predecessorCount();
if (Node->K == TypeNode::Kind::Declaration) {
// Ignore a reference from a type definition to its own declaration.
// But only do so if there is exactly one. If there are more, keep it in
// order to ensure it is never marked for inlining.
auto SelfEdgeCounter = [Key = Node->T->key()](auto *N) {
return N->T->key() == Key;
};
if (llvm::count_if(Node->predecessors(), SelfEdgeCounter) == 1)
--Iterator->second;
// Since dependency graph does not take functions into account,
// explicitly add one "use" to each struct that appears as a function
// stack frame.
if (StackFrameTypeCache.contains(Node->T->key()))
++Iterator->second;
}
if (InlineTypeLog.isEnabled()) {
InlineTypeLog << getNodeLabel(Node) << "' is depended on by: {\n";
for (auto *Predecessor : Node->predecessors())
InlineTypeLog << "- " << getNodeLabel(Predecessor) << '\n';
InlineTypeLog << "}\n" << DoLog;
}
}
auto SingleDependencyFilter = std::views::filter([](const auto &Pair) {
return Pair.second == 1;
});
TypesToInlineCache = DependentTypeCount | SingleDependencyFilter
| std::views::keys
| revng::to<std::set<model::TypeDefinition::Key>>();
if (InlineTypeLog.isEnabled()) {
revng_log(InlineTypeLog, "Final list of types that can be inlined: {");
{
LoggerIndent Indent{ InlineTypeLog };
for (const model::TypeDefinition::Key &T : TypesToInlineCache)
revng_log(InlineTypeLog, ::toString(T));
}
revng_log(InlineTypeLog, "}");
}
}
if (Configuration.EnableStackFrameInlining && InlineTypeLog.isEnabled()) {
revng_log(InlineTypeLog, "Which also includes stack frames: {");
{
LoggerIndent Indent{ InlineTypeLog };
for (const model::TypeDefinition::Key &T : StackFrameTypeCache)
if (TypesToInlineCache.contains(T))
revng_log(InlineTypeLog, ::toString(T));
}
revng_log(InlineTypeLog, "}");
}
InlinableCacheIsReady = true;
}
static Logger<> TypePrinterLog{ "type-definition-printer" };
void ptml::CTypeBuilder::printTypeDefinitions() {
@@ -415,7 +321,7 @@ void ptml::CTypeBuilder::printTypeDefinitions() {
revng_log(TypePrinterLog, "Definition");
revng_assert(Defined.contains(DependencyCache->getDeclaration(NodeT)));
if (isDeclarationTheSameAsDefinition(*NodeT) or shouldInline(*NodeT)) {
if (isDeclarationTheSameAsDefinition(*NodeT)) {
continue;
}
@@ -90,8 +90,11 @@ struct EmitCPass : clift::impl::CliftEmitCBase<EmitCPass> {
return;
llvm::raw_null_ostream NullStream;
ptml::CTypeBuilder B(NullStream, *Model, /* EnableTaglessMode = */ Tagless);
B.collectInlinableTypes();
ptml::CTypeBuilder B(NullStream,
*Model,
Tagless,
ptml::CTypeBuilder::ConfigurationOptions{
.EnableStackFrameInlining = true });
getOperation()->walk([&](clift::FunctionOp Function) {
if (not Function.isExternal())
+1 -1
View File
@@ -1308,7 +1308,7 @@ public:
if (const model::Type *T = ModelFunction.StackFrameType().get()) {
const auto *D = llvm::cast<model::DefinedType>(T)->Definition().get();
if (C.shouldInline(D->key()))
if (C.Configuration.EnableStackFrameInlining)
C.printDefinition(*D);
}
@@ -12,12 +12,6 @@ sources:
- primitive-types.h.model.yml
- union.h.model.yml
- pointer-to-struct.h.model.yml
- inline-struct.h.model.yml
- do-not-inline-types-pointing-to-itself.h.model.yml
- do-not-generate-struct-for-stack-type.h.model.yml
- do-not-inline-used-stack-type.h.model.yml
- inline-enum.h.model.yml
- inline-union.h.model.yml
- annotate-attibutes.model.yml
commands:
- type: revng.model-to-header-unit-test
@@ -26,7 +20,7 @@ commands:
filter: model-to-header
suffix: .h
command: |-
revng artifact --enable-type-inlining emit-model-header /dev/null --model "$INPUT"
revng artifact emit-model-header /dev/null --model "$INPUT"
| revng ptml > "$OUTPUT";
revng check-decompiled-c "$OUTPUT";
FileCheck --input-file="$OUTPUT" "${SOURCE}.filecheck";
@@ -1,45 +0,0 @@
---
#
# This file is distributed under the MIT License. See LICENSE.md for details.
#
Architecture: x86_64
Functions:
- Entry: "0x401129:Code_x86_64"
Name: fn
StackFrameType:
Kind: DefinedType
Definition: "/TypeDefinitions/3001-StructDefinition"
Prototype:
Kind: DefinedType
Definition: "/TypeDefinitions/3002-CABIFunctionDefinition"
ExportedNames:
- fn
TypeDefinitions:
- Kind: CABIFunctionDefinition
ID: 3002
ABI: SystemV_x86_64
ReturnType:
Kind: PrimitiveType
PrimitiveKind: Generic
Size: 8
Arguments: []
- Kind: StructDefinition
ID: 3001
Fields:
- Offset: 0
Type:
Kind: PrimitiveType
PrimitiveKind: Generic
Size: 8
Size: 16
Segments:
- StartAddress: "0x400000:Generic64"
VirtualSize: 40960
StartOffset: 0
FileSize: 40960
IsReadable: true
IsWriteable: false
IsExecutable: true
@@ -1,7 +0,0 @@
#
# This file is distributed under the MIT License. See LICENSE.md for details.
#
# The definition will be generated inline in the decompiled functions.
CHECK: typedef struct _PACKED struct_3001 struct_3001;
CHECK-NOT: struct_3001
@@ -1,23 +0,0 @@
---
#
# This file is distributed under the MIT License. See LICENSE.md for details.
#
Architecture: x86_64
TypeDefinitions:
- Kind: StructDefinition
ID: 3001
Name: B
Fields:
- Offset: 0
Name: next
Type:
Kind: PointerType
PointerSize: 8
PointeeType:
Kind: DefinedType
Definition: "/TypeDefinitions/3001-StructDefinition"
Size: 8
---
@@ -1,8 +0,0 @@
#
# This file is distributed under the MIT License. See LICENSE.md for details.
#
CHECK: typedef struct _PACKED B B;
CHECK: struct _PACKED B {
CHECK: B *next;
CHECK: };
@@ -1,55 +0,0 @@
---
#
# This file is distributed under the MIT License. See LICENSE.md for details.
#
Architecture: x86_64
Functions:
- Entry: "0x401129:Code_x86_64"
Name: fn
StackFrameType:
Kind: DefinedType
Definition: "/TypeDefinitions/3001-StructDefinition"
Prototype:
Kind: DefinedType
Definition: "/TypeDefinitions/3002-CABIFunctionDefinition"
ExportedNames:
- fn
TypeDefinitions:
- Kind: CABIFunctionDefinition
ID: 3002
ABI: SystemV_x86_64
ReturnType:
Kind: PrimitiveType
PrimitiveKind: Generic
Size: 8
Arguments: []
- Kind: StructDefinition
ID: 3001
Fields:
- Offset: 0
Type:
Kind: PrimitiveType
PrimitiveKind: Generic
Size: 8
Size: 16
- Kind: StructDefinition
ID: 3003
Name: S
Fields:
- Offset: 0
Name: s1
Type:
Kind: DefinedType
Definition: "/TypeDefinitions/3001-StructDefinition"
Size: 16
Segments:
- StartAddress: "0x400000:Generic64"
VirtualSize: 40960
StartOffset: 0
FileSize: 40960
IsReadable: true
IsWriteable: false
IsExecutable: true
@@ -1,13 +0,0 @@
#
# This file is distributed under the MIT License. See LICENSE.md for details.
#
CHECK: typedef struct _PACKED struct_3001 struct_3001;
CHECK: struct _PACKED struct_3001 {
CHECK: generic64_t offset_0;
CHECK: uint8_t padding_at_8[8];
CHECK: };
CHECK: typedef struct _PACKED S S;
CHECK: struct _PACKED S {
CHECK: struct_3001 s1;
CHECK: };
@@ -1,105 +0,0 @@
---
#
# This file is distributed under the MIT License. See LICENSE.md for details.
#
Architecture: x86_64
TypeDefinitions:
- Kind: StructDefinition
ID: 3001
Name: X
Fields:
- Offset: 0
Name: the_struct_y
Type:
Kind: DefinedType
Definition: "/TypeDefinitions/3002-StructDefinition"
Size: 56
- Kind: StructDefinition
ID: 3002
Name: Y
Fields:
- Offset: 0
Name: the_union_w
Type:
Kind: DefinedType
Definition: "/TypeDefinitions/3003-UnionDefinition"
- Offset: 4
Name: the_union_z
Type:
Kind: DefinedType
Definition: "/TypeDefinitions/3004-UnionDefinition"
- Offset: 48
Name: ptr_to_E
Type:
Kind: PointerType
PointerSize: 8
PointeeType:
Kind: DefinedType
Definition: "/TypeDefinitions/3007-EnumDefinition"
Size: 56
- Kind: UnionDefinition
ID: 3003
Name: W
Fields:
- Index: 0
Name: field
Type:
Kind: PrimitiveType
PrimitiveKind: Signed
Size: 4
- Kind: UnionDefinition
ID: 3004
Name: Z
Fields:
- Index: 0
Name: the_enum_e
Type:
Kind: DefinedType
Definition: "/TypeDefinitions/3005-EnumDefinition"
- Index: 1
Name: array_of_a
Type:
Kind: PointerType
PointerSize: 8
PointeeType:
Kind: DefinedType
Definition: "/TypeDefinitions/3006-StructDefinition"
- Kind: EnumDefinition
ID: 3005
Name: E
UnderlyingType:
Kind: PrimitiveType
PrimitiveKind: Unsigned
Size: 4
Entries:
- Value: 2
Name: VALUE
- Value: 7
Name: OTHER
- Kind: StructDefinition
ID: 3006
Name: A
Fields:
- Offset: 0
Name: field
Type:
Kind: PrimitiveType
PrimitiveKind: Signed
Size: 4
Size: 4
- Kind: EnumDefinition
ID: 3007
Name: E2
UnderlyingType:
Kind: PrimitiveType
PrimitiveKind: Unsigned
Size: 4
Entries:
- Value: 1
Name: FIRST
- Value: 2
Name: SECOND
---
@@ -1,35 +0,0 @@
#
# This file is distributed under the MIT License. See LICENSE.md for details.
#
CHECK: typedef enum _PACKED E2 E2;
CHECK: enum _PACKED E2 {
CHECK: FIRST = 0x1U,
CHECK: SECOND = 0x2U,
CHECK: enum_max_value_E2 = 0xffffffffU,
CHECK: };
CHECK: typedef struct _PACKED X X;
CHECK: typedef struct _PACKED Y Y;
CHECK: typedef union _PACKED W W;
CHECK: typedef union _PACKED Z Z;
CHECK: typedef enum _PACKED E E;
CHECK: typedef struct _PACKED A A;
CHECK: struct _PACKED X {
CHECK: struct _PACKED Y {
CHECK: union _PACKED W {
CHECK: int32_t field;
CHECK: } the_union_w;
CHECK: union _PACKED Z {
CHECK: enum _PACKED E {
CHECK: VALUE = 0x2U,
CHECK: OTHER = 0x7U,
CHECK: enum_max_value_E = 0xffffffffU,
CHECK: } the_enum_e;
CHECK: struct _PACKED A {
CHECK: int32_t field;
CHECK: } array_of_a[10];
CHECK: } the_union_z;
CHECK: uint8_t padding_at_44[4];
CHECK: E2 *ptr_to_E;
CHECK: } the_struct_y;
CHECK: };
@@ -1,32 +0,0 @@
---
#
# This file is distributed under the MIT License. See LICENSE.md for details.
#
Architecture: x86_64
TypeDefinitions:
- Kind: StructDefinition
ID: 1
Name: B
Fields:
- Offset: 0
Name: the_a
Type:
Kind: DefinedType
Definition: "/TypeDefinitions/3002-EnumDefinition"
Size: 4
- Kind: EnumDefinition
ID: 3002
Name: A
UnderlyingType:
Kind: PrimitiveType
PrimitiveKind: Unsigned
Size: 4
Entries:
- Value: 0
Name: FIRST
- Value: 1
Name: SECOND
---
@@ -1,13 +0,0 @@
#
# This file is distributed under the MIT License. See LICENSE.md for details.
#
CHECK: typedef struct _PACKED B B;
CHECK: typedef enum _PACKED A A;
CHECK: struct _PACKED B {
CHECK: enum _ENUM_UNDERLYING(uint32_t) _PACKED A {
CHECK: FIRST = 0x0U,
CHECK: SECOND = 0x1U,
CHECK: enum_max_value_A = 0xffffffffU,
CHECK: } the_a;
CHECK: };
@@ -1,31 +0,0 @@
---
#
# This file is distributed under the MIT License. See LICENSE.md for details.
#
Architecture: x86_64
TypeDefinitions:
- Kind: StructDefinition
ID: 3001
Name: B
Fields:
- Offset: 0
Name: a1
Type:
Kind: DefinedType
Definition: "/TypeDefinitions/3002-StructDefinition"
Size: 4
- Kind: StructDefinition
ID: 3002
Name: A
Fields:
- Offset: 0
Name: x
Type:
Kind: PrimitiveType
PrimitiveKind: Signed
Size: 4
Size: 4
---
@@ -1,11 +0,0 @@
#
# This file is distributed under the MIT License. See LICENSE.md for details.
#
CHECK: typedef struct _PACKED B B;
CHECK: typedef struct _PACKED A A;
CHECK: struct _PACKED B {
CHECK: struct _PACKED A {
CHECK: int32_t x;
CHECK: } a1;
CHECK: };
@@ -1,30 +0,0 @@
---
#
# This file is distributed under the MIT License. See LICENSE.md for details.
#
Architecture: x86_64
TypeDefinitions:
- Kind: StructDefinition
ID: 3001
Name: B
Fields:
- Offset: 0
Name: a1
Type:
Kind: DefinedType
Definition: "/TypeDefinitions/3002-UnionDefinition"
Size: 4
- Kind: UnionDefinition
ID: 3002
Name: A
Fields:
- Index: 0
Name: x
Type:
Kind: PrimitiveType
PrimitiveKind: Signed
Size: 4
---
@@ -1,11 +0,0 @@
#
# This file is distributed under the MIT License. See LICENSE.md for details.
#
CHECK: typedef struct _PACKED B B;
CHECK: typedef union _PACKED A A;
CHECK: struct _PACKED B {
CHECK: union _PACKED A {
CHECK: int32_t x;
CHECK: } a1;
CHECK: };