This commits drop an old BeautifyGHAST's dependency on
MarkForSerialization.
This dependency was required because the beautification process makes
some choices (e.g. on short-circuiting ifs) based on knowledge of how
many instructions in a given basic block have side effects.
Informally speaking, if a basic block contains an instructions with side
effects, it cannot be moved before around "crossing" other stuff with
side effects. This is why it was necessary for BeautifyGHAST to know
about side effects.
Information about side effects was computed by BeautifyGHAST using
MarkForSerialization until now, but it was not properly used.
It was more like wishful thinking about what we wanted to do, but we
never fixed it.
So this commit takes a step in fixing this:
- drops dependency in BeautifyGHAST from MarkForSerialization, which was
there but not used;
- starts taking the same information from calls to SerializationMarkers,
if they are available in the IR.
Now, in all the decompilation pipelines we have in place, we don't
inject SerializationMarkers before BeautifyGHAST yet, so the behavior
in those pipelines is totally unmodified for what concerns side
effects.
However, for all new decompilation pipelines that we will build, we will
run IRCanonicalization passes before BeautifyGHAST. In this way, the IR
reaching BeautifyGHAST will have SerializationMarkers in place, and the
beautification process will work as intended.
This commit adds a second argument to functions in the family of
SerializationMarker. The second argument is a boolean.
If it's true, it represents the information that the marked
llvm::Instruction was marked because it had side effects.
This information is necessary for various beautification tasks on the
GHAST, and we want it on the IR in order to be able to switch away from
the old MarkForSerializationPass.
The new name is more sensible because the tests in that directory now
don't always use `FileCheck` anymore, but they are still all
detected using `lit`.
Model classes are now described by a YAML document, which is used to
generate C++ headers containing classes and all the boilerplate
required for YAML serialization/deserialization, usage in
SortedVectors, etc. See the README in include/revng/Model for more
info.
Remove the logic for detecting Instructions with duplicated uses
introduced by control-flow restructuring (the use is duplicated, but the
instruction is not).
By dropping this detection, we'll end up not marking for serialization
some Instructions. Hence, when emitting C code, such Instructions will
just be emitted as inline expressions, without declaring a dedicated
local variable to hold their value. This is somehow suboptimal w.r.t the
fact that the expression will be emitted many times, one for each
duplicated use. However, this is not semantically incorrect, just
verbose.
On the other hand, the logic for detecting Instructions with duplicated
uses has always been subtly broken, because it only looked at the number
of duplicates for a given basic block introduced by control-flow
restructuring.
This information is not enough to detect Instructions with duplicated
uses. Proper detection should actually be based on GHAST.
`main` is not in all cases a dynamically exported symbol, therefore,
it's not safe to rely on it.
This commit switches to use `PathList`'s `getCurrentExecutableFullPath`,
which reads `/proc/self/exe`.
StringRef::data() does not ensure that the string is zero terminated,
thus when printed it can contain more data than expected.
Specifically, this triggered the reported name of the registers to be
incorrect, and this manifested itself as wrong inline assembly emitted.
The constructor now accepts a llvm::object::Binary directly rather than
a path. Will be used by the revng-pipeline which will retain ownership
of the binary.
A dereference iterator is used to map a pointer-like object to the
pointee, as an example given a `vector<unique_ptr<int>> Vector`, using
`dereferenceRange(Vector)` will present a range of `int &` rather than a
range of `unique_ptr<int> &`.
mapToValueIterator can be used on a map range
to access the underlying object directly rather
than the pair<key, object>.
This allows e.g. the following programming pattern:
```
RecursiveCoroutine<std::optional<SomeType>> f();
int g() {
return *f();
}
```
Without operator* defined for `RecursiveCoroutine` this would fail,
requiring an explicit cast such as:
```
int g() {
return *static_cast<std::optional<SomeType>>(f());
}
```