mirror of
https://github.com/revng/revng
synced 2026-06-21 14:07:57 +00:00
02cc6923d0
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`.
99 lines
2.9 KiB
C++
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>();
|
|
}
|