mirror of
https://github.com/revng/revng
synced 2026-06-21 14:07:57 +00:00
Introduce FixPointerSize
This commit introduces `FixPointerSize`, a pipe changing the `DataLayout` of module in order to have a pointer size identical to the one of `targetABI()`. The pipe also performs safety checks to ensure this does not corrupt semantics.
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
|
||||
//
|
||||
// This file is distributed under the MIT License. See LICENSE.md for details.
|
||||
//
|
||||
|
||||
#include "revng/PipeboxCommon/Common.h"
|
||||
#include "revng/PipeboxCommon/LLVMContainer.h"
|
||||
#include "revng/PipeboxCommon/Model.h"
|
||||
|
||||
namespace revng::pypeline::pipes {
|
||||
|
||||
// For target binaries with 32-bit pointers, rewrite the LLVM module's
|
||||
// DataLayout so the default-address-space pointer is 32 bits wide before the
|
||||
// IR is handed off to `clifter`.
|
||||
//
|
||||
// Two preconditions must hold for the rewrite to be valid:
|
||||
//
|
||||
// 1. No scalar `alloca`'s allocated type transitively contains an
|
||||
// `llvm::PointerType`. Pointer-typed allocas would silently shrink
|
||||
// from 8 to 4 bytes when the DataLayout changes; pointer values must
|
||||
// instead live in integer-typed allocas and be bitcast (via
|
||||
// `ptrtoint`/`inttoptr`) at load/store boundaries.
|
||||
//
|
||||
// 2. No `getelementptr` has a source type that transitively contains an
|
||||
// `llvm::PointerType`. Otherwise the GEP's byte offsets would shift
|
||||
// under the new DataLayout.
|
||||
//
|
||||
// Both preconditions are checked with hard assertions; the pipe aborts if
|
||||
// either fails. The DataLayout rewrite is a no-op for targets whose pointer
|
||||
// is not 32 bits wide.
|
||||
class FixPointerSize {
|
||||
public:
|
||||
static constexpr llvm::StringRef Name = "fix-pointer-size";
|
||||
using Arguments = TypeList<PipeArgument<"Module",
|
||||
"LLVM Modules whose pointer size "
|
||||
"needs to match the "
|
||||
"target architecture">>;
|
||||
|
||||
const std::string StaticConfiguration;
|
||||
FixPointerSize(llvm::StringRef StaticConfiguration) :
|
||||
StaticConfiguration(StaticConfiguration) {}
|
||||
|
||||
PipeOutput run(const Model &TheModel,
|
||||
const revng::pypeline::Request &Incoming,
|
||||
const revng::pypeline::Request &Outgoing,
|
||||
llvm::StringRef Configuration,
|
||||
LLVMFunctionContainer &Container);
|
||||
};
|
||||
|
||||
} // namespace revng::pypeline::pipes
|
||||
@@ -10,6 +10,7 @@ revng_add_analyses_library_internal(
|
||||
EmbedStatementComments.cpp
|
||||
ExitSSAPass.cpp
|
||||
ExtractValueToGEP.cpp
|
||||
FixPointerSize.cpp
|
||||
FoldModelGEP.cpp
|
||||
SplitStructPhis.cpp
|
||||
ImplicitModelCastPass.cpp
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
//
|
||||
// This file is distributed under the MIT License. See LICENSE.md for details.
|
||||
//
|
||||
|
||||
#include "llvm/ADT/SmallPtrSet.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/IR/DerivedTypes.h"
|
||||
#include "llvm/IR/Function.h"
|
||||
#include "llvm/IR/GlobalVariable.h"
|
||||
#include "llvm/IR/InstIterator.h"
|
||||
#include "llvm/IR/Instructions.h"
|
||||
#include "llvm/IR/Module.h"
|
||||
|
||||
#include "revng/ADT/Queue.h"
|
||||
#include "revng/Canonicalize/FixPointerSize.h"
|
||||
#include "revng/Model/Architecture.h"
|
||||
#include "revng/PipeboxCommon/ObjectID.h"
|
||||
#include "revng/Support/Assert.h"
|
||||
#include "revng/Support/IRHelpers.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
static bool containsPointer(Type *T) {
|
||||
OnceQueue<Type *> Queue;
|
||||
Queue.insert(T);
|
||||
while (not Queue.empty()) {
|
||||
Type *Current = Queue.pop();
|
||||
if (Current->isPointerTy())
|
||||
return true;
|
||||
auto ContainedTypesCount = Current->getNumContainedTypes();
|
||||
for (unsigned I = 0; I < ContainedTypesCount; ++I)
|
||||
Queue.insert(Current->getContainedType(I));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Rewrite the default-address-space pointer specification in a DataLayout
|
||||
// string to the given pointer size in bits.
|
||||
static std::string rewriteDefaultPointerSize(StringRef Original,
|
||||
unsigned Bits) {
|
||||
std::string Width = std::to_string(Bits);
|
||||
std::string NewSpec = "p:" + Width + ":" + Width;
|
||||
|
||||
SmallVector<StringRef, 16> Parts;
|
||||
Original.split(Parts, '-');
|
||||
std::string Result;
|
||||
bool Replaced = false;
|
||||
for (StringRef Part : Parts) {
|
||||
if (not Result.empty())
|
||||
Result += '-';
|
||||
if (Part.starts_with("p:") or Part.starts_with("p0:")) {
|
||||
Result += NewSpec;
|
||||
Replaced = true;
|
||||
} else {
|
||||
Result += Part.str();
|
||||
}
|
||||
}
|
||||
if (not Replaced) {
|
||||
if (not Result.empty())
|
||||
Result += '-';
|
||||
Result += NewSpec;
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static void checkAndFixModule(llvm::Module &M, unsigned TargetPointerBits) {
|
||||
for (llvm::GlobalVariable &GV : M.globals()) {
|
||||
if (GV.use_empty())
|
||||
continue;
|
||||
revng_assert(not containsPointer(GV.getValueType()),
|
||||
("used global variable's value type transitively contains a "
|
||||
"pointer: "
|
||||
+ dumpToString(&GV))
|
||||
.c_str());
|
||||
}
|
||||
|
||||
for (llvm::Function &F : M) {
|
||||
if (F.isDeclaration())
|
||||
continue;
|
||||
|
||||
for (llvm::Instruction &I : llvm::instructions(F)) {
|
||||
if (auto *Alloca = dyn_cast<llvm::AllocaInst>(&I)) {
|
||||
Type *AllocatedTy = Alloca->getAllocatedType();
|
||||
revng_assert(not containsPointer(AllocatedTy),
|
||||
("scalar alloca containing a pointer in @" + F.getName()
|
||||
+ ": " + dumpToString(Alloca))
|
||||
.str()
|
||||
.c_str());
|
||||
} else if (auto *GEP = dyn_cast<llvm::GetElementPtrInst>(&I)) {
|
||||
revng_assert(not containsPointer(GEP->getSourceElementType()),
|
||||
("GEP source type transitively contains a pointer in @"
|
||||
+ F.getName() + ": " + dumpToString(GEP))
|
||||
.str()
|
||||
.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto &DL = M.getDataLayout();
|
||||
if (DL.getPointerSizeInBits(0) == TargetPointerBits)
|
||||
return;
|
||||
|
||||
auto Original = DL.getStringRepresentation();
|
||||
M.setDataLayout(rewriteDefaultPointerSize(Original, TargetPointerBits));
|
||||
}
|
||||
|
||||
namespace revng::pypeline::pipes {
|
||||
|
||||
PipeOutput FixPointerSize::run(const Model &TheModel,
|
||||
const revng::pypeline::Request &Incoming,
|
||||
const revng::pypeline::Request &Outgoing,
|
||||
llvm::StringRef Configuration,
|
||||
LLVMFunctionContainer &Container) {
|
||||
using namespace model::ABI;
|
||||
auto TargetPointerBits = getPointerSize(TheModel.get()->targetABI()) * 8;
|
||||
|
||||
for (const ObjectID *Object : Outgoing.at(0)) {
|
||||
revng_assert(Object->kind() == Kinds::Function);
|
||||
checkAndFixModule(Container.getModule(*Object), TargetPointerBits);
|
||||
}
|
||||
|
||||
return { {}, {} };
|
||||
}
|
||||
|
||||
} // namespace revng::pypeline::pipes
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "revng/ABI/Analyses/ConvertFunctionsToCABI.h"
|
||||
#include "revng/ABI/Analyses/ConvertFunctionsToRaw.h"
|
||||
#include "revng/Canonicalize/FixPointerSize.h"
|
||||
#include "revng/Canonicalize/SimplifySwitch.h"
|
||||
#include "revng/Canonicalize/SwitchToStatements.h"
|
||||
#include "revng/CliftPipes/Clifter.h"
|
||||
@@ -93,6 +94,7 @@ REGISTER(Container, TranslatedContainer);
|
||||
|
||||
using namespace revng::pypeline::pipes;
|
||||
|
||||
REGISTER(Pipe, FixPointerSize);
|
||||
REGISTER(Pipe, PureLLVMPassesPipe);
|
||||
REGISTER(Pipe, PureLLVMPassesRootPipe);
|
||||
REGISTER(Pipe, PureMLIRPassesPipe);
|
||||
|
||||
@@ -396,6 +396,8 @@ branches:
|
||||
container: llvm-functions
|
||||
category: debug
|
||||
filename: clifter_input.bc
|
||||
- pipe: fix-pointer-size
|
||||
arguments: [llvm-functions]
|
||||
- pipe: clifter
|
||||
arguments: [llvm-functions, clift-functions]
|
||||
artifacts:
|
||||
|
||||
Reference in New Issue
Block a user