Canonicalize: ban pointers

In order to ensure there are no `alloca`s containing pointers, we change
the passes that emit them to emit instead integer scalars of the same
type.
This commit is contained in:
Alessandro Di Federico
2026-06-18 16:44:34 +02:00
parent c4760fa0c4
commit e1fc2d346e
7 changed files with 81 additions and 20 deletions
+63
View File
@@ -4,11 +4,26 @@
// This file is distributed under the MIT License. See LICENSE.md for details.
//
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/DebugInfoMetadata.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/GlobalVariable.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/Module.h"
#include "revng/Support/Assert.h"
#include "revng/Support/Debug.h"
inline llvm::Type *getVariableType(const llvm::Value *Variable) {
if (auto *Alloca = llvm::dyn_cast<llvm::AllocaInst>(Variable))
return Alloca->getAllocatedType();
else if (auto *GV = llvm::dyn_cast<llvm::GlobalVariable>(Variable))
return GV->getValueType();
else
revng_abort("Either GlobalVariable or AllocaInst expected");
}
namespace revng {
class IRBuilder;
@@ -44,6 +59,13 @@ using LLVMBuilderBase = llvm::IRBuilder<llvm::ConstantFolder,
} // namespace detail
inline bool
sameSize(llvm::Type *LHS, llvm::Type *RHS, const llvm::DataLayout &DL) {
auto LHSSize = DL.getTypeStoreSize(LHS).getFixedValue();
auto RHSSize = DL.getTypeStoreSize(RHS).getFixedValue();
return LHSSize == RHSSize;
}
/// This is a wrapper over llvm's IR builder that force-sets a debug location
/// even when its insertion point is a basic block.
///
@@ -120,6 +142,47 @@ public:
getInserter().checkImpl();
}
public:
/// Create an `alloca` of integer type or byte array capable to hold \p T
llvm::AllocaInst *createSimpleAlloca(llvm::Type *T) {
using namespace llvm;
const DataLayout &DL = GetInsertBlock()->getModule()->getDataLayout();
if (T->isPointerTy()) {
auto Size = DL.getPointerSizeInBits(T->getPointerAddressSpace());
return CreateAlloca(this->getIntNTy(Size));
}
if (T->isIntegerTy())
return CreateAlloca(T);
auto Size = DL.getTypeStoreSize(T).getFixedValue();
return CreateAlloca(ArrayType::get(this->getInt8Ty(), Size));
}
/// Load a value of type \p DesiredType from \p Variable (an `AllocaInst` or
/// `GlobalVariable`). The variable's allocated type and \p DesiredType must
/// have the same store size.
llvm::LoadInst *createLoadFromVariable(llvm::Value *Variable,
llvm::Type *DesiredType) {
using namespace llvm;
Type *AllocatedType = getVariableType(Variable);
const DataLayout &DL = GetInsertBlock()->getModule()->getDataLayout();
revng_assert(sameSize(DesiredType, AllocatedType, DL));
return this->CreateLoad(DesiredType, Variable);
}
/// Store \p V into \p Variable (an `AllocaInst` or `GlobalVariable`). The
/// variable's allocated type and \p V's type must have the same store size.
llvm::StoreInst *createStoreToVariable(llvm::Value *V,
llvm::Value *Variable) {
using namespace llvm;
Type *AllocatedType = getVariableType(Variable);
const DataLayout &DL = GetInsertBlock()->getModule()->getDataLayout();
revng_assert(sameSize(V->getType(), AllocatedType, DL));
return CreateStore(V, Variable);
}
protected:
// NOLINTNEXTLINE
explicit IRBuilder(bool EnableDebugInformationChecks, llvm::LLVMContext &C) :
-9
View File
@@ -1531,15 +1531,6 @@ inline llvm::Instruction *createLoadVariable(revng::IRBuilder &Builder,
revng_abort("Either GlobalVariable or AllocaInst expected");
}
inline llvm::Type *getVariableType(const llvm::Value *Variable) {
if (auto *Alloca = llvm::dyn_cast<llvm::AllocaInst>(Variable))
return Alloca->getAllocatedType();
else if (auto *GV = llvm::dyn_cast<llvm::GlobalVariable>(Variable))
return GV->getValueType();
else
revng_abort("Either GlobalVariable or AllocaInst expected");
}
void pruneDICompileUnits(llvm::Module &M);
llvm::SmallSet<llvm::Value *, 2> findPhiTreeLeaves(llvm::Value *Root);
+6 -4
View File
@@ -273,7 +273,7 @@ buildStore(BasicBlock *StoreBlock, Value *Incoming, AllocaInst *Alloca) {
Builder.SetInsertPoint(StoreBlock->getTerminator(), Alloca->getDebugLoc());
}
auto *S = Builder.CreateStore(Incoming, Alloca);
auto *S = Builder.createStoreToVariable(Incoming, Alloca);
revng_log(Log,
"Created StoreInst " << dumpToString(S)
<< " in Block: " << StoreBlock->getName());
@@ -285,7 +285,9 @@ buildStore(BasicBlock *StoreBlock, Value *Incoming, AllocaInst *Alloca) {
for (Use &Operand : NextInBlock.operands()) {
if (Operand.get() == IncomingInst) {
if (not LoadFromStore) {
LoadFromStore = Builder.CreateLoad(IncomingInst->getType(), Alloca);
LoadFromStore = Builder
.createLoadFromVariable(Alloca,
IncomingInst->getType());
if (auto *IncomingInst = dyn_cast<Instruction>(Incoming))
LoadFromStore->setDebugLoc(IncomingInst->getDebugLoc());
}
@@ -308,7 +310,7 @@ static void replacePHIEquivalenceClass(const SetVector<PHINode *> &PHIs,
const DebugLoc &PHIDebugLoc = (*PHIs.begin())->getDebugLoc();
Builder.SetInsertPointPastAllocas(&F, PHIDebugLoc);
AllocaInst *Alloca = Builder.CreateAlloca((*PHIs.begin())->getType());
AllocaInst *Alloca = Builder.createSimpleAlloca((*PHIs.begin())->getType());
revng_log(Log, "Created Alloca: " << dumpToString(Alloca));
{
@@ -423,7 +425,7 @@ static void replacePHIEquivalenceClass(const SetVector<PHINode *> &PHIs,
LoggerIndent IndentPHI{ Log };
Builder.SetInsertPoint(PHI->getParent()->getFirstNonPHI());
auto *NewLoad = createLoad(Builder, Alloca);
auto *NewLoad = Builder.createLoadFromVariable(Alloca, PHI->getType());
NewLoad->setDebugLoc(PHIDebugLoc);
revng_log(Log, "Create new load: " << dumpToString(NewLoad));
+2
View File
@@ -67,8 +67,10 @@ static std::string rewriteDefaultPointerSize(StringRef Original,
static void checkAndFixModule(llvm::Module &M, unsigned TargetPointerBits) {
for (llvm::GlobalVariable &GV : M.globals()) {
// Ignore unused globals
if (GV.use_empty())
continue;
revng_assert(not containsPointer(GV.getValueType()),
("used global variable's value type transitively contains a "
"pointer: "
@@ -9,6 +9,7 @@
#include "llvm/Pass.h"
#include "revng/Support/IRBuilder.h"
#include "revng/Support/IRHelpers.h"
using namespace llvm;
@@ -67,12 +68,12 @@ bool SplitExponentialDataflow::runOnFunction(llvm::Function &F) {
Changed = true;
auto Location = I.getDebugLoc();
B.SetInsertPointPastAllocas(&F, Location);
auto *Alloca = B.CreateAlloca(I.getType());
auto *Alloca = B.createSimpleAlloca(I.getType());
B.SetInsertPoint(&*std::next(I.getIterator()), Location);
auto *Load = B.CreateLoad(I.getType(), Alloca);
auto *Load = B.createLoadFromVariable(Alloca, I.getType());
I.replaceAllUsesWith(Load);
B.SetInsertPoint(Load, Location);
B.CreateStore(&I, Alloca);
B.createStoreToVariable(&I, Alloca);
}
}
return Changed;
+4 -2
View File
@@ -1528,7 +1528,7 @@ private:
if constexpr (not IsLegacy) {
revng::NonDebugInfoCheckingIRBuilder B(F.getContext());
B.SetInsertPointPastAllocas(&F, DL);
return B.CreateAlloca(I->getType());
return B.createSimpleAlloca(I->getType());
} else {
revng_assert(I->getType()->isIntOrPtrTy());
const model::UpcastableType &VariableType = TheTypeMap.at(I);
@@ -1547,6 +1547,8 @@ private:
if (auto *I = dyn_cast<Instruction>(ToCopy))
DL = I->getDebugLoc();
revng::NonDebugInfoCheckingIRBuilder B(InsertBefore, DL);
if (auto *Alloca = dyn_cast<AllocaInst>(ToCopy))
return B.createLoadFromVariable(Alloca, U->getType());
return B.CreateLoad(U->getType(), ToCopy);
} else {
// TODO: remove when we drop legacy mode.
@@ -1580,7 +1582,7 @@ private:
if constexpr (not IsLegacy) {
revng::NonDebugInfoCheckingIRBuilder B(NextInstruction,
ValueToAssign->getDebugLoc());
return B.CreateStore(ValueToAssign, LocalVariable);
return B.createStoreToVariable(ValueToAssign, LocalVariable);
} else {
// TODO: drop when we drop legacy mode.
return VariableBuilder.createAssignmentBefore(LocalVariable,
@@ -175,7 +175,7 @@ define i32 @d_argument(ptr %arg) {
; The the ret instruction should load from there again.
;
; CHECK-LABEL: define %s @s
; CHECK: [[ALLOCA:%[a-zA-Z0-9_]+]] = alloca %s
; CHECK: [[ALLOCA:%[a-zA-Z0-9_]+]] = alloca [8 x i8]
; CHECK-NEXT: [[LOADED_VALUE:%[a-zA-Z0-9_]+]] = load %s, ptr %arg
; CHECK-NEXT: store %s [[LOADED_VALUE]], ptr [[ALLOCA]]
; CHECK-NEXT: store i64 7, ptr @segment
@@ -196,7 +196,7 @@ define %s @s(ptr %arg) {
; This is the same as the previous test, but with segment and argument swapped.
;
; CHECK-LABEL: define %s @s_swapped
; CHECK: [[ALLOCA:%[a-zA-Z0-9_]+]] = alloca %s
; CHECK: [[ALLOCA:%[a-zA-Z0-9_]+]] = alloca [8 x i8]
; CHECK-NEXT: [[LOADED_VALUE:%[a-zA-Z0-9_]+]] = load %s, ptr @segment
; CHECK-NEXT: store %s [[LOADED_VALUE]], ptr [[ALLOCA]]
; CHECK-NEXT: store i64 7, ptr %arg