aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/Transforms/NaCl/ReplacePtrsWithInts.cpp142
-rw-r--r--test/Transforms/NaCl/replace-ptrs-with-ints.ll88
2 files changed, 182 insertions, 48 deletions
diff --git a/lib/Transforms/NaCl/ReplacePtrsWithInts.cpp b/lib/Transforms/NaCl/ReplacePtrsWithInts.cpp
index 4ca5b59e00..619ae04826 100644
--- a/lib/Transforms/NaCl/ReplacePtrsWithInts.cpp
+++ b/lib/Transforms/NaCl/ReplacePtrsWithInts.cpp
@@ -43,6 +43,7 @@
//===----------------------------------------------------------------------===//
#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/Function.h"
@@ -77,21 +78,16 @@ namespace {
Type *IntPtrType;
struct RewrittenVal {
- RewrittenVal(): IsPlaceholder(true), NewIntVal(NULL) {}
- bool IsPlaceholder;
+ RewrittenVal(): Placeholder(NULL), NewIntVal(NULL) {}
+ Value *Placeholder;
Value *NewIntVal;
};
// Maps from old values (of pointer type) to converted values (of
// IntPtrType type).
DenseMap<Value *, RewrittenVal> RewriteMap;
- // Number of placeholders; used for detecting unhandled cases.
- int PlaceholderCount;
- // List of instructions whose deletion has been deferred.
- SmallVector<Instruction *, 20> ToErase;
public:
- FunctionConverter(Type *IntPtrType)
- : IntPtrType(IntPtrType), PlaceholderCount(0) {}
+ FunctionConverter(Type *IntPtrType) : IntPtrType(IntPtrType) {}
// Returns the normalized version of the given type, converting
// pointer types to IntPtrType.
@@ -106,8 +102,19 @@ namespace {
void recordConverted(Value *From, Value *To);
void recordConvertedAndErase(Instruction *From, Value *To);
+ // Returns Val with no-op casts (those that convert between
+ // IntPtrType and pointer types) stripped off.
+ Value *stripNoopCasts(Value *Val);
+
// Returns the normalized version of the given value.
- Value *convert(Value *Val);
+ //
+ // If the conversion of Val has been deferred, this returns a
+ // placeholder object, which will later be replaceAllUsesWith'd to
+ // the final value. Since replaceAllUsesWith does not work on
+ // references by metadata nodes, this can be bypassed using
+ // BypassPlaceholder to get the real converted value, assuming it
+ // is available.
+ Value *convert(Value *Val, bool BypassPlaceholder = false);
// Returns the NormalizedPtr form of the given pointer value.
// Inserts conversion instructions at InsertPt.
Value *convertBackToPtr(Value *Val, Instruction *InsertPt);
@@ -119,6 +126,9 @@ namespace {
void convertInPlace(Instruction *Inst);
void eraseReplacedInstructions();
+
+ // List of instructions whose deletion has been deferred.
+ SmallVector<Instruction *, 20> ToErase;
};
}
@@ -144,14 +154,7 @@ void FunctionConverter::recordConverted(Value *From, Value *To) {
return;
}
RewrittenVal *RV = &RewriteMap[From];
- if (RV->NewIntVal) {
- assert(RV->IsPlaceholder);
- // Resolve placeholder.
- RV->NewIntVal->replaceAllUsesWith(To);
- delete RV->NewIntVal;
- RV->IsPlaceholder = false;
- --PlaceholderCount;
- }
+ assert(!RV->NewIntVal);
RV->NewIntVal = To;
}
@@ -161,35 +164,56 @@ void FunctionConverter::recordConvertedAndErase(Instruction *From, Value *To) {
ToErase.push_back(From);
}
-Value *FunctionConverter::convert(Value *Val) {
+Value *FunctionConverter::stripNoopCasts(Value *Val) {
+ SmallPtrSet<Value *, 4> Visited;
+ for (;;) {
+ if (!Visited.insert(Val)) {
+ // It is possible to get a circular reference in unreachable
+ // basic blocks. Handle this case for completeness.
+ return UndefValue::get(Val->getType());
+ }
+ if (CastInst *Cast = dyn_cast<CastInst>(Val)) {
+ Value *Src = Cast->getOperand(0);
+ if ((isa<BitCastInst>(Cast) && Cast->getType()->isPointerTy()) ||
+ (isa<PtrToIntInst>(Cast) && Cast->getType() == IntPtrType) ||
+ (isa<IntToPtrInst>(Cast) && Src->getType() == IntPtrType)) {
+ Val = Src;
+ continue;
+ }
+ }
+ return Val;
+ }
+}
+
+Value *FunctionConverter::convert(Value *Val, bool BypassPlaceholder) {
+ Val = stripNoopCasts(Val);
if (!Val->getType()->isPointerTy())
return Val;
if (Constant *C = dyn_cast<Constant>(Val))
return ConstantExpr::getPtrToInt(C, IntPtrType);
RewrittenVal *RV = &RewriteMap[Val];
- if (!RV->NewIntVal) {
- // No converted value available yet, so create a placeholder.
- Argument *Placeholder = new Argument(convertType(Val->getType()));
- RV->NewIntVal = Placeholder;
- ++PlaceholderCount;
+ if (BypassPlaceholder) {
+ assert(RV->NewIntVal);
+ return RV->NewIntVal;
}
- return RV->NewIntVal;
+ if (!RV->Placeholder)
+ RV->Placeholder = new Argument(convertType(Val->getType()));
+ return RV->Placeholder;
}
Value *FunctionConverter::convertBackToPtr(Value *Val, Instruction *InsertPt) {
Type *NewTy =
convertType(Val->getType()->getPointerElementType())->getPointerTo();
- Value *Conv = convert(Val);
- return new IntToPtrInst(Conv, NewTy, Conv->getName() + ".asptr", InsertPt);
+ return new IntToPtrInst(convert(Val), NewTy, "", InsertPt);
}
Value *FunctionConverter::convertFunctionPtr(Value *Callee,
Instruction *InsertPt) {
FunctionType *FuncType = cast<FunctionType>(
Callee->getType()->getPointerElementType());
- Value *Conv = convert(Callee);
- return new IntToPtrInst(Conv, convertFuncType(FuncType)->getPointerTo(),
- Conv->getName() + ".asfuncptr", InsertPt);
+ return new IntToPtrInst(convert(Callee),
+ convertFuncType(FuncType)->getPointerTo(),
+ "", InsertPt);
}
static bool ShouldLeaveAlone(Value *V) {
@@ -206,8 +230,7 @@ void FunctionConverter::convertInPlace(Instruction *Inst) {
Value *Arg = Inst->getOperand(I);
if (Arg->getType()->isPointerTy() && !ShouldLeaveAlone(Arg)) {
Value *Conv = convert(Arg);
- Inst->setOperand(I, new IntToPtrInst(convert(Arg), Arg->getType(),
- Conv->getName() + ".asptr", Inst));
+ Inst->setOperand(I, new IntToPtrInst(Conv, Arg->getType(), "", Inst));
}
}
// Convert result.
@@ -220,14 +243,29 @@ void FunctionConverter::convertInPlace(Instruction *Inst) {
}
void FunctionConverter::eraseReplacedInstructions() {
- if (PlaceholderCount) {
- for (DenseMap<Value *, RewrittenVal>::iterator I = RewriteMap.begin(),
- E = RewriteMap.end(); I != E; ++I) {
- if (I->second.IsPlaceholder)
+ bool Error = false;
+ for (DenseMap<Value *, RewrittenVal>::iterator I = RewriteMap.begin(),
+ E = RewriteMap.end(); I != E; ++I) {
+ if (I->second.Placeholder) {
+ if (I->second.NewIntVal) {
+ I->second.Placeholder->replaceAllUsesWith(I->second.NewIntVal);
+ } else {
errs() << "Not converted: " << *I->first << "\n";
+ Error = true;
+ }
}
+ }
+ if (Error)
report_fatal_error("Case not handled in ReplacePtrsWithInts");
+
+ // Delete the placeholders in a separate pass. This means that if
+ // one placeholder is accidentally rewritten to another, we will get
+ // a useful error message rather than accessing a dangling pointer.
+ for (DenseMap<Value *, RewrittenVal>::iterator I = RewriteMap.begin(),
+ E = RewriteMap.end(); I != E; ++I) {
+ delete I->second.Placeholder;
}
+
// We must do dropAllReferences() before doing eraseFromParent(),
// otherwise we will try to erase instructions that are still
// referenced.
@@ -250,7 +288,7 @@ static void ConvertMetadataOperand(FunctionConverter *FC,
return;
Value *MDArg = MD->getOperand(0);
if (MDArg && (isa<Argument>(MDArg) || isa<Instruction>(MDArg))) {
- MDArg = FC->convert(MDArg);
+ MDArg = FC->convert(MDArg, /* BypassPlaceholder= */ true);
if (PtrToIntInst *Cast = dyn_cast<PtrToIntInst>(MDArg)) {
// Unwrapping this is necessary for llvm.dbg.declare to work.
MDArg = Cast->getPointerOperand();
@@ -325,9 +363,9 @@ static void ConvertInstruction(DataLayout *DL, Type *IntPtrType,
FC->recordConvertedAndErase(Phi, Phi2);
} else if (SelectInst *Op = dyn_cast<SelectInst>(Inst)) {
Instruction *Op2 = SelectInst::Create(Op->getCondition(),
- FC->convert(Op->getTrueValue()),
- FC->convert(Op->getFalseValue()),
- "", Op);
+ FC->convert(Op->getTrueValue()),
+ FC->convert(Op->getFalseValue()),
+ "", Op);
CopyDebug(Op2, Op);
Op2->takeName(Op);
FC->recordConvertedAndErase(Op, Op2);
@@ -342,7 +380,7 @@ static void ConvertInstruction(DataLayout *DL, Type *IntPtrType,
FC->recordConvertedAndErase(Inst, Result);
} else if (isa<BitCastInst>(Inst)) {
if (Inst->getType()->isPointerTy()) {
- FC->recordConvertedAndErase(Inst, FC->convert(Inst->getOperand(0)));
+ FC->ToErase.push_back(Inst);
}
} else if (ICmpInst *Cmp = dyn_cast<ICmpInst>(Inst)) {
Value *Cmp2 = CopyDebug(new ICmpInst(Inst, Cmp->getPredicate(),
@@ -380,9 +418,6 @@ static void ConvertInstruction(DataLayout *DL, Type *IntPtrType,
// https://code.google.com/p/nativeclient/issues/detail?id=3443
Inst->eraseFromParent();
} else {
- if (ICall->getIntrinsicID() == Intrinsic::dbg_declare) {
- ConvertMetadataOperand(FC, ICall, 0);
- }
FC->convertInPlace(Inst);
}
} else if (isa<InlineAsm>(Call->getCalledValue())) {
@@ -482,12 +517,18 @@ static void CleanUpFunction(Function *Func, Type *IntPtrType) {
SimplifyCasts(Iter++, IntPtrType);
}
}
- // Cleanup: Remove ptrtoints that were introduced for allocas but not used.
+ // Cleanup pass.
for (Function::iterator BB = Func->begin(), E = Func->end();
BB != E; ++BB) {
for (BasicBlock::iterator Iter = BB->begin(), E = BB->end();
Iter != E; ) {
Instruction *Inst = Iter++;
+ // Add names to inttoptrs to make the output more readable. The
+ // placeholder values get in the way of doing this earlier when
+ // the inttoptrs are created.
+ if (isa<IntToPtrInst>(Inst))
+ Inst->setName(Inst->getOperand(0)->getName() + ".asptr");
+ // Remove ptrtoints that were introduced for allocas but not used.
if (isa<PtrToIntInst>(Inst) && Inst->use_empty())
Inst->eraseFromParent();
}
@@ -539,6 +580,19 @@ bool ReplacePtrsWithInts::runOnModule(Module &M) {
ConvertInstruction(&DL, IntPtrType, &FC, Iter++);
}
}
+ // Now that all the replacement instructions have been created, we
+ // can update the debug intrinsic calls.
+ for (Function::iterator BB = NewFunc->begin(), E = NewFunc->end();
+ BB != E; ++BB) {
+ for (BasicBlock::iterator Inst = BB->begin(), E = BB->end();
+ Inst != E; ++Inst) {
+ if (IntrinsicInst *Call = dyn_cast<IntrinsicInst>(Inst)) {
+ if (Call->getIntrinsicID() == Intrinsic::dbg_declare) {
+ ConvertMetadataOperand(&FC, Call, 0);
+ }
+ }
+ }
+ }
FC.eraseReplacedInstructions();
OldFunc->replaceAllUsesWith(ConstantExpr::getBitCast(NewFunc,
OldFunc->getType()));
diff --git a/test/Transforms/NaCl/replace-ptrs-with-ints.ll b/test/Transforms/NaCl/replace-ptrs-with-ints.ll
index c45ca322cd..bab6101848 100644
--- a/test/Transforms/NaCl/replace-ptrs-with-ints.ll
+++ b/test/Transforms/NaCl/replace-ptrs-with-ints.ll
@@ -26,16 +26,79 @@ declare i8* @declared_func(i8*, i64)
; CHECK: declare i32 @declared_func(i32, i64)
-define void @self_reference(i8* %ptr) {
+define void @self_reference_phi(i8* %ptr) {
entry:
br label %loop
loop:
%x = phi i8* [ %x, %loop ], [ %ptr, %entry ]
br label %loop
}
-; CHECK: define void @self_reference(i32 %ptr) {
+; CHECK: define void @self_reference_phi(i32 %ptr) {
; CHECK: %x = phi i32 [ %x, %loop ], [ %ptr, %entry ]
+; Self-referencing bitcasts are possible in unreachable basic blocks.
+; It is not very likely that we will encounter this, but we handle it
+; for completeness.
+define void @self_reference_bitcast(i8** %dest) {
+ ret void
+unreachable_loop:
+ store i8* %self_ref, i8** %dest
+ %self_ref = bitcast i8* %self_ref to i8*
+ store i8* %self_ref, i8** %dest
+ br label %unreachable_loop
+}
+; CHECK: define void @self_reference_bitcast(i32 %dest) {
+; CHECK: store i32 undef, i32* %dest.asptr
+; CHECK: store i32 undef, i32* %dest.asptr
+
+define void @circular_reference_bitcasts(i8** %dest) {
+ ret void
+unreachable_loop:
+ store i8* %cycle1, i8** %dest
+ %cycle1 = bitcast i8* %cycle2 to i8*
+ %cycle2 = bitcast i8* %cycle1 to i8*
+ br label %unreachable_loop
+}
+; CHECK: define void @circular_reference_bitcasts(i32 %dest) {
+; CHECK: store i32 undef, i32* %dest.asptr
+
+define void @circular_reference_inttoptr(i8** %dest) {
+ ret void
+unreachable_loop:
+ %ptr = inttoptr i32 %int to i8*
+ %int = ptrtoint i8* %ptr to i32
+ store i8* %ptr, i8** %dest
+ br label %unreachable_loop
+}
+; CHECK: define void @circular_reference_inttoptr(i32 %dest) {
+; CHECK: store i32 undef, i32* %dest.asptr
+
+define i8* @forwards_reference(%struct** %ptr) {
+ br label %block1
+block2:
+ ; Forwards reference to %val.
+ %cast = bitcast %struct* %val to i8*
+ br label %block3
+block1:
+ %val = load %struct** %ptr
+ br label %block2
+block3:
+ ; Backwards reference to a forwards reference that has already been
+ ; resolved.
+ ret i8* %cast
+}
+; CHECK: define i32 @forwards_reference(i32 %ptr) {
+; CHECK-NEXT: br label %block1
+; CHECK: block2:
+; CHECK-NEXT: br label %block3
+; CHECK: block1:
+; CHECK-NEXT: %ptr.asptr = inttoptr i32 %ptr to i32*
+; CHECK-NEXT: %val = load i32* %ptr.asptr
+; CHECK-NEXT: br label %block2
+; CHECK: block3:
+; CHECK-NEXT: ret i32 %val
+
+
define i8* @phi_multiple_entry(i1 %arg, i8* %ptr) {
entry:
br i1 %arg, label %done, label %done
@@ -198,8 +261,8 @@ define i8* @indirect_call(i8* (i8*)* %func, i8* %arg) {
ret i8* %result
}
; CHECK: define i32 @indirect_call(i32 %func, i32 %arg) {
-; CHECK-NEXT: %func.asfuncptr = inttoptr i32 %func to i32 (i32)*
-; CHECK-NEXT: %result = call i32 %func.asfuncptr(i32 %arg)
+; CHECK-NEXT: %func.asptr = inttoptr i32 %func to i32 (i32)*
+; CHECK-NEXT: %result = call i32 %func.asptr(i32 %arg)
; CHECK-NEXT: ret i32 %result
@@ -302,6 +365,23 @@ define void @alloca_lower_alignment() {
; CHECK-NEXT: alloca [4 x i8], align 1
+; This tests for a bug in which, when processing the store's %buf2
+; operand, ReplacePtrsWithInts accidentally strips off the ptrtoint
+; cast that it previously introduced for the 'alloca', causing an
+; internal sanity check to fail.
+define void @alloca_cast_stripping() {
+ %buf = alloca i32
+ %buf1 = ptrtoint i32* %buf to i32
+ %buf2 = inttoptr i32 %buf1 to i32*
+ store i32 0, i32* %buf2
+ ret void
+}
+; CHECK: define void @alloca_cast_stripping() {
+; CHECK-NEXT: %buf = alloca [4 x i8]
+; CHECK-NEXT: %buf.bc = bitcast [4 x i8]* %buf to i32*
+; CHECK-NEXT: store i32 0, i32* %buf.bc
+
+
define i1 @compare(i8* %ptr1, i8* %ptr2) {
%cmp = icmp ult i8* %ptr1, %ptr2
ret i1 %cmp