Add `MakeModelTypes` and `UpdateFuncPrototypes` to the DLA backend:
* `MakeModelTypes` adds to the model all the types recovered by the DLA
* `UpdateFuncPrototypes` updates function prototypes in the model that
have generic return values or arguments to use the types recovered by
the DLA
* Changes to the `LayoutTypeSystem` graph
Pointers are identified in the TypeSystem graph as leaf nodes which
have a new type of edge (PointerEdge) that connects them to another
node of the graph. The destination of the edge represents the layout of
the pointed type.
* Changes to the Front-end
Pointer edges, and their destination nodes, are created by the DLA
front-end (`DLACreateIntraProceduralTypes`) whenever an access node has
a size that is compatible with the size of a pointer in the current
Architecture.
Successors might then be added to the newly generated node, if any,
by looking up the llvm::Value it is attached to.
* Changes to the Middle-end
Most of the DLA passes should ignore Pointer Edges, so they are modified
accordingly. Most notably, nodes that represent pointed layouts should
never be merged/pruned-off.
* Changes to the Back-end
The `TypeDeclCreationAction` of the decompiler and the `DLAMakeLayouts`
step of the DLA back-end are modified to take into account the new
information about pointers.
⚠️ There is a known issue with this version of the decompiler,
namely the fact that type loops are not detected and can cause the
emitter to enter an infinite loop.
We introduce a new tile, for matching if node with a single and inlined
then/else. In these case, the not inlined successor should be connected
with the tile node.
Handle switch nodes with all but one case edges inlined, by electing the
not inlined edge as the path conducting to the postdominator, and
avoiding parts of the graph disconnecting during the tiling procedure.
Add a step that recognizes if two subtrees of a union node are
topologically equivalent and merges them. This corresponds to removing
duplicate fields in unions.
This deduplication was prevously done while emitting layouts.
A check is inserted into DLAMakeLayouts to assert that, after
constructing unions, no union has only one child, which could be the
case if we didn't deduplicate union fields in the graph.
`moveEdgeTarget` and `moveEdgeSource` now directly modify the
successor/predecessor field in place in the edge data structure, instead
of extracting and reinserting later the edge.
This avoid a subtle bug, found in the `createTile` helper function, that
caused the swap of a `then` and `else` branch during a tile creation
step, that consequently broke the semantics in the recovered GHAST.
Break node creation is now improved to avoid edge index swapping that
can cause inversion of conditions later on in the decompilation
pipeline.
To do this, the creation and connection of the break nodes is done
during the bulk insertion of nodes in a collapsed region.
* Inheritance straight lines are now collapsed by CollapseSingleChild
* Don't collapse if parent has more than one parent or if the child has
more than one parent
* Allow the possibility to construct single-member struct: if this
struct is inherited by many other structs, "flattening" the struct is not
the most sensible thing to do, since inheritance information is something
we want to preserve in these cases.
During combing, GCBI's BlockType was used to detect unexpectedPC and
anyPC. This method is does not work anymore (since revng started
dropping such info from isolated functions and replacing that with
llvm::unreachable instructions).
This commit drops the dependency on GCBI and BlockType, and starts
detecting blocks to inline by simply looking at their terminator. If
it's an llvm::unreachable, the block is detected as inlineable.
While doing thie, also drop `printAccessNode()` from DLA DebugPrinter.
The information about which LLVM instruction originated a given access
node is already available in the csv generated by the `dla-accesses-log`
logger.
- Remove `CreateInterProceduralTypes` and `CreateIntraProceduralTypes`
from the StepManager and put them in a separate
`DLATypeSystemLLVMBuilder` object that is in charge of initializing
the DLATypeSystem graph.
- Remove `MakeLayouts` from the StepManager and split into two free
functions: `makeLayouts` and `makeLayoutMap()`
- Remove all LLVM-related stuff (Module, LayoutTypePtrs and mappings
between these and DLATypeSystemNodes) from DLATypeSystem
- Add an IntEqClasses member to DLATypeSystem, to use to map between
LayoutTypePtrs and Layouts
- Add a TSDebugPrinter object inside the DLATypeSystem, which by default
prints only IDs
- Define an LLVMTSDebugPrinter inside DLATypeSystemBuilder which
overrides the default printer's behavior, printing LLVM-related info.
- Add the possibility to dump into a .csv file the bindings between
Nodes in the TypeSystem and `llvm::Value`s
Before this commit DLATypeSystem used a
std::set<std::unique_ptr<LayoutTypeSystemNode> to hold nodes, allowing
heterogenous lookup with bare pointers, fast removal being a set,
and providing address stability without requiring an ordering between
LayoutTypeSystemNodes.
The same requirements is now obtained more cleanly using with a
BumpPtrAllocator for nodes, and a set of naked pointers to them to
enable fast lookup and removal.
With this quarantine, when nodes are removed from RegionCFG, they are
not really freed, but they are held here until the RegionCFG itself goes
out of scope.
This is unfortunately necessary now, since the CFG restructuring
algorithm uses maps and sets (e.g. Backedges.) that are indexed using
a BasicBlockNodeT *.
If we don't hold the removed nodes in quarantine, the system allocator
can reuse the blocks, allocating new nodes at the same address, and
causing false-positive hits in some of the mentioned maps. This was the
most straightforward solution for now.
Other solutions we have considered:
- use a special monotonic allocator for BasicBlockNodes
- this should work, but in principle it gives the same results as the
current solution, with more boilerplate. Also, at the moment
std::unique_ptr is not allocator aware, so we would need to change
BlockNodes to not use them, and this would require even more
boilerplate.
- change the API for RegionCFG::removeNode, to take as arguments the
reference to the data structure and maps that must be updated, so that
when we remove the node from RegionCFG we also clear it from the maps.
However, this is very invasive, it requires changing the public facing
API, it requirese coupling the RegionCFG API with internal details,
and in the future it would need to be updated for every new map that
must be updated on removal of a node.
This commit fixes a bug causing a failing assertion on Backedges that
jump from an inner MetaRegion to an outer MetaRegion after region
collapsing.
Constructor declarations for `BasicBlockNode` were not consistent.
We had default-constructor, and move constructor explicitly deleted, but
the copy constructor was not explicitly deleted, even though it was
implicitly deleted. Make deletion explicit, in accordance to the fact
that all other special member functions for construction and assignment
are deleted.
Implement a beautify phase which does the following:
- Compute, for every scope in the AST, if that scope is `fallthrough`
or `nofallthrough` scope. Basically, the `nofallthrough` scopes are
scope which ends with a `return`, `continue', or `break`.
- Using the information computed before, we can promote the scope of an
`IfNode` using the following criterion: if one of the two branches of
the `IfNode` is a `nofallthrough` scope, we are sure that the other
branch is not reachable from the former one. We can therefore, promote
the latter as `fallthrough` block of the `IfNode` (of course taking care
of inverting the condition statement if we are promoting to
`fallthrough` the `then` branch.
If both the `then` and the `else` branches can be promoted as
`nofallthrough`, we have a function that evaluates the weight of the two
branches, and promotes the heavier one. This helps reducing the
Cognitive Complexity of the generated code
This library provides a thin locking wrapper around clang::tooling
invocations.
It should be used instead of performing direct clang::tooling
invocations by all programs that use revng-c and may run more than one
ClangTool concurrently.
This is necessary because clang::tooling internally uses llvm's cl::opt
for parsing command line options.
cl::opt uses a global variable for the parser under the hood so parsing
two command lines concurrently is not safe.
Similarly, cl::opt typically uses global variables to hold options, so
it is not safe to execute a ClangTool concurrently to another tool
that is parsing a new set of options, because there might be race
conditions between threads reading and writing the same options at the
same time.
The new library introduces a thin locking layer so that the end-user
does not need to know or worry about these details.
Actually remove dummy nodes that are purged during the `purgeDummies`
normalization phase (which is in charge of removing dummy nodes that are
not superfluos for our AST representation).
These dummies where laying around untouched, and caused errors when
iterating over all the AST while collecting the weight after the
combing.