Files
revng-revng/lib/CliftTransforms/EmitFieldAccesses.cpp
Andrea Gussoni 02cc6923d0 EmitFieldAccesses: implement pass
The `EmitFieldAccesses` pass transforms `clift` by taking pointer-typed
expressions computed via integerr arithmetic with type-safe field
accesses and array accesses.

The transformation is split in three main phases:
1) `PointerArithmetic` computation.
2) `BestTraversal` computation.
3) `FieldAccess` `clift` rewrite.

The high level driver is implemented in the `EmitFieldAccesses` header
and cpp, while the nested 3 phases are implemented respectively in
`PointerArithmetic`, `BestTraversal` and `FieldAccessReplacement`.

The `computerPointerArithmetic` phase is concerned with taking a
pointer-typed `ExpressionOp`, called `PointerToReplace`, and expressing
it in a `BasePointer+Offset` form.

The `computeBestTraversal` phase is concerned with computing the best
traversal of the type pointed to by `BasePointer`, that can be used to
rewrite the pointer arithmetic in `clift` with just field accesses and
array subscripts.

The `replaceFieldAccess` phase takes the `Traversal` computed at the
previous step, and actually rewrites in `clift` the `PointerToReplace`
in terms of field accesses and array accesses w.r.t. the `BasePointer`.
2026-03-18 17:25:13 +01:00

99 lines
2.9 KiB
C++

//
// This file is distributed under the MIT License. See LICENSE.md for details.
//
#include <compare>
#include <cstdint>
#include <memory>
#include "mlir/IR/Builders.h"
#include "mlir/IR/BuiltinTypes.h"
#include "revng/Clift/CliftEnums.h"
#include "revng/CliftTransforms/Passes.h"
#include "revng/Support/CTarget.h"
#include "BestTraversal.h"
#include "EmitFieldAccesses.h"
#include "FieldAccessReplacement.h"
#include "PointerArithmetic.h"
namespace mlir {
namespace clift {
#define GEN_PASS_DEF_CLIFTEMITFIELDACCESSES
#include "revng/CliftTransforms/Passes.h.inc"
} // namespace clift
} // namespace mlir
namespace clift = mlir::clift;
using namespace clift;
namespace {
struct EmitFieldAccessesPass
: clift::impl::CliftEmitFieldAccessesBase<EmitFieldAccessesPass> {
void runOnOperation() override {
if (emitFieldAccesses(getOperation()).failed()) {
signalPassFailure();
}
}
};
/// Holds the planned `Replacement` for a single expression
struct PlannedReplacement {
clift::ExpressionOpInterface Op;
PointerArithmetic PA;
Traversal BestTraversal;
};
/// Implementation of the high level `emitFieldAccesses` phases inside the
/// anonymous namespace in this translation unit
mlir::LogicalResult emitFieldAccessesImpl(clift::FunctionOp Function) {
// `TraversalInfoMap` cache. Even if not elegant, we store the data computed
// at the pass level so that we can cache it instead of recomputing it every
// time
TraversalInfoMap TraversalMap;
// Phase 1-2: Collect all planned `Replacement`s without modifying the IR
llvm::SmallVector<PlannedReplacement> Replacements;
Function->walk([&TraversalMap,
&Replacements](clift::ExpressionOpInterface Op) {
// 1. We inspect all the `ExpressionOp`s in the current `Function`
std::optional<PointerArithmetic> PA = computePointerArithmetic(Op);
// The `PointerArithmetic` returned object could be empty at the moment of
// return, and this is a signal that we could not compute a
// `PointerArithmetic` for the current `ExpressionOpInterface`
// 2. We proceed with the computation of the `BestTraversal` for the current
// `PointerArithmetic`
if (PA) {
std::optional<Traversal> BT = computeBestTraversal(Op, *PA, TraversalMap);
if (BT) {
Replacements.push_back({ Op, std::move(*PA), std::move(*BT) });
}
}
});
// Phase 3: Apply all replacements
for (const auto &R : Replacements) {
replaceFieldAccess(R.Op, R.PA, R.BestTraversal);
}
// The IR is always in a valid state, regardless of whether we performed an
// operation rewrite or not.
return mlir::success();
}
} // namespace
/// `emitFieldAccesses` driver that can be called by importing the header
mlir::LogicalResult emitFieldAccesses(clift::FunctionOp Function) {
return emitFieldAccessesImpl(Function);
}
clift::PassPtr<clift::FunctionOp> clift::createEmitFieldAccessesPass() {
return std::make_unique<EmitFieldAccessesPass>();
}