Now that we need the llvm::DataLayout for e.g. computing pointer
sizes, caching pointer types becomes risky: different DataLayouts
coming from different LLVM Modules could disagree on the pointer
size, and securing against this would require clearing the caches
at every call to import.
Given that the caches in practice were not caching operations that
are supposed to be slow, just drop all the type caches to simplify
the codebase.
Replace the standalone PointerSize field with a pointer to the
function's llvm::DataLayout, and read the pointer size off the
DataLayout when needed. This keeps the importer aligned with what
LLVM thinks the pointer size is, instead of relying on an external
source of truth that may go stale.
The Clifter public header only needs llvm::Function, not the full
llvm::Module definition, so drop the Module.h include. This
shaves a non-trivial amount of compile time off the translation
units that depend on Clifter.h without pulling in Module.h
themselves.
Use llvm::DataLayout to compute helper struct layouts in the
Clifter, mirroring what HelpersToHeader does. The layout query
replaces a couple of hand-rolled offset/size computations and
keeps the importer in sync with what LLVM and the helpers header
believe about each helper struct.
When emitting the header for QEMU/revng helper struct types, use
the LLVM DataLayout to walk the StructType, so that field offsets
and the overall struct size match what LLVM actually produces. The
old code printed fields in declaration order without consulting
the layout, which broke whenever the struct included padding.
Run -strip-dead-prototypes as part of the LLVM-pass list invoked
just before the Clifter, so that all unused helpers are stripped
away. In particular, this prevents some LLVM intrinsics that
return structs containing i1 (e.g. the overflow intrinsics) from
ending up in the C output and tripping later passes.
Teach CliftToCEmitter how to emit assignments and bitcasts whose
target type is an array, by emitting calls to the new
assign_array and bit_cast_to_array macros. The shape of the call
encodes the element type and the number of elements, which is what
the macros need to expand correctly.
assign_array is necessary as a fallback for when the decompiled C
code may need assignments among array types. bit_cast_to_array is
necessary because when the target type of a bitcast is an array
the regular bit_cast macro is not enough.
Rework primitive-types.h so that the various bit_cast macros share
a single implementation based on `typeof`/`__typeof__`. As a
consequence both keywords are added to the model's reserved-name
set so that user identifiers cannot collide with them.
When importing an llvm::ArrayType, emit a Clift StructType with the
OpaqueType rank in its handle and a single i8 array field of the
right byte size, instead of a real array type. This avoids
exposing C array types as rvalues in the generated code, which is
a foot-gun because array rvalues immediately decay to pointers,
and provides a single name (via the existing OpaqueType infra) for
arrays of equal byte size.
Rework the GEP-importing logic to handle the i8-typed,
byte-strided GEPs produced by ArithmeticToGEP (and natively used
elsewhere) in addition to the structured ones it already supported.
Indices are now interpreted as byte offsets and the importer walks
the model type tree at byte granularity to materialise the right
sequence of field/element accesses.
Drop the !revng.variable_type metadata and the matching helpers,
along with the only place in the Clifter that still consumed them.
After the previous LVB cleanup nothing emits this metadata, and the
model variable type can be reconstructed from the alloca's array
type alone.
Emit forward declarations and definitions of the opaque-array
artificial structs from the C model header (and from the helpers
header). Definitions are gated on a new DefineOpaqueTypes flag
threaded through PTMLHeaderBuilder::printModelHeader so that the
helpers header keeps emitting only the declarations.
Teach Clift's verify-against-model and ImportDescriptiveInfo
visitors about types whose handle has the OpaqueType rank: the
verifier rejects non-struct types with such handles, and the name
importer fills in the type's mutable name with
NameBuilder::opaqueTypeName based on the wrapped byte size.
Teach NameBuilder and ModelCBuilder how to produce names and
references for opaque-array types: NameBuilder gains
opaqueTypeName(ByteSize), ModelCBuilder gains the matching
reference and definition tags. Both rely on the new
Configuration::OpaqueTypePrefix.
Add an OpaqueTypePrefix string to the model configuration with
default `opaque_type_`. This is used to build the C name of the
artificial structs that wrap model array types and keeps the prefix
configurable alongside the other artificial-name prefixes.
Define a new "opaque-type" rank in revng/Pipes/Ranks.h, keyed by
the byte size of the wrapped type. This rank is used by upcoming
commits to identify the artificial structs that wrap model array
types in Clift.
Add a canonicalisation pass that rewrites extractvalue from a
pointer-returning struct as an i8 GEP plus a load. Together with
ArithmeticToGEP this lets later passes consume the result through
the same GEP-based interface they use for ordinary memory accesses.
Introduce the ArithmeticToGEP canonicalisation pass. It detects
pointers (LLVM-typed pointer values, model pointers flagged by the
"revng.pointers" metadata, and pointer-tagged extract values) and
rewrites integer arithmetic computed on top of them as i8 GEPs,
followed by a ptrtoint when the original use needs an integer.
For each obvious pointer the pass also walks backwards through Add
chains and disambiguates the pointer operand of each Add when the
other operand cannot be a pointer, so the rewrite extends to integer
arithmetic that only later flows into a pointer use.
Add an llvm-lit test suite for the new-PassManager
SwitchToStatements pass, exercised via
`revng opt -passes=switch-to-statements-test` (registered by the
pass's registerCallbacks hook). The tests cover load/store
serialisation, side-effecting calls and a few representative
patterns where an instruction must be picked for serialisation
because its memory read is no longer available at one of its uses.
Drop the special case in serializeToLocalVariable that, for
struct-typed values, allocated `[N x i8]` of the struct's data-layout
size instead of an alloca of the struct type itself. The Clifter now
handles arbitrary alloca element types directly, so we can just use
the value's LLVM type.
In non-legacy mode, replace the hand-written aliasing checks in
AEMFP::mayClobber with the answer of LLVM's AAManager, queried
through an AliasAnalysis * threaded into the framework. Legacy mode
keeps its previous logic, since it operates on calls to opaque
copy/assign functions that LLVM AA cannot reason about.
The pass now registers and consumes AAManager via the
FunctionAnalysisManager, with the usual GlobalsAA / module-proxy
cross-registration so that local AA can fall back to GlobalsAA
without crashing.
Make AEMFP::mayClobber and AEMFP::applyTransferFunctionImpl proper
members instead of free functions taking the framework state as a
parameter. This removes a fair amount of boilerplate around passing
the AliasAnalysis pointer along, and keeps all the logic that
operates on the lattice in one place.
No behavioural change.
Convert SwitchToStatements to a new-PassManager FunctionPass and
introduce two function-level analyses,
AvailableExpressionsAnalysis and InstructionToSerializePicker,
backed by AnalysisInfoMixin. The non-legacy production entry point
now sets up an FAM, registers GlobalsAA proxies and runs the pass
through a FunctionPassManager; the legacy entry point keeps using
the legacy FunctionPass wrapper.
The pass also exposes a registerCallbacks hook so the upcoming unit
tests can load it from `revng opt -passes=switch-to-statements-test`
without dragging the model along.
Replace the loose ResultMap typedef with the
MFP::MFIResultMap<AEMFP<IsLegacy>> alias and keep the maximal fixed
point result inside AvailableExpressionsResult, so callers of
AEResult can fetch availability-at-a-point without reaching into a
separate map.
This is a structural step toward turning available-expressions
discovery into a real new-PassManager analysis.
Rename the AvailableExpressionsAnalysis class to
AvailableExpressionsMonotoneFramework (with the AEMFP alias)
because it does not satisfy the LLVM AnalysisInfoMixin contract;
keeping the "Analysis" name would clash with the actual
new-PassManager analysis introduced in a follow-up commit.
Replace the home-grown hasSideEffects, mayHaveSideEffects,
mayWriteToMemory and mayReadMemory helpers (in
DecompilationHelpers.h) with the equivalent predicates from
llvm::Instruction. The legacy code paths still need a few extra
checks, so the templated wrappers in SwitchToStatements.cpp
specialise on IsLegacy and fall back to the LLVM predicates in
non-legacy mode.
This removes the now-unused helpers from DecompilationHelpers.h.
Add a set of templated helpers for getting the pointer/value
operands and operand-uses of copy and assign instructions. The
helpers exist because the legacy mode represents copies and
assignments as calls to opaque functions while the non-legacy mode
uses LoadInst and StoreInst directly: with these helpers the rest
of the pass can be written once and parameterised on IsLegacy.
The helpers are pure additions; nothing uses them yet.
Rename the legacy FunctionPass wrapper class from
SwitchToStatements to SwitchToStatementsPass to free the
SwitchToStatements name for the upcoming new-PassManager pass that
will replace it.
Reorganise LocalVariableBuilder so that the legacy and non-legacy
code paths are separated into two specialisations, and so that the
non-legacy specialisation does not need a model::Binary to operate.
This is a prerequisite for using LocalVariableBuilder from passes
that do not know about the model, e.g. unit tests for the new-PM
SwitchToStatements and the upcoming non-legacy decompilation
pipeline.
Extend SegregateStackAccessesPass to attach the "revng.pointers"
metadata on every llvm::Function it rewrites, and on every CallInst
it produces.
The metadata represents, in a model-agnostic way, which arguments
and return values of a call or function (both formal and actual)
are pointer-typed in the model. Later passes consume this so that
they can stay model-agnostic.
Introduce templated readers and writers for the "revng.pointers"
metadata in revng/Support/IRHelpers.h. The metadata is a pair of
boolean tuples describing, for an llvm::Function or llvm::CallInst,
which of its return values and which of its argument operands are
pointer-typed in the model. This is the model-agnostic
representation later passes (e.g. ArithmeticToGEP) consume to find
pointers without having to look at the model directly.
createLocalVariable no longer attaches the !revng.variable_type
metadata to the alloca. The model variable type can be reconstructed
from the array-typed alloca alone, so the metadata is redundant. The
metadata itself will be dropped from the codebase in a follow-up
commit.
Switch the IRBuilder used by createCallStackArgumentVariable and
createStackFrameVariable to revng::NonDebugInfoCheckingIRBuilder, in
line with the rest of the LocalVariableBuilder code paths. There is
no functional change for builds that already attach debug info, just
a stricter check on builds where it is unexpectedly missing.
When emitting the synthesized integer index for an access whose
LinearCombination contains a variable, use the variable's own type
for the constant `[0]` immediate (and for the rest of the index
arithmetic) instead of always using a generic pointer-sized integer.
With model-sized pointers in the picture the two integer widths can
differ, and using the wrong one trips Clift's type checking
downstream.