aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/llvm/Transforms/NaCl.h6
-rw-r--r--lib/Transforms/NaCl/CMakeLists.txt1
-rw-r--r--lib/Transforms/NaCl/ExpandConstantExpr.cpp23
-rw-r--r--lib/Transforms/NaCl/ExpandTls.cpp13
-rw-r--r--lib/Transforms/NaCl/ExpandTlsConstantExpr.cpp13
-rw-r--r--lib/Transforms/NaCl/ExpandUtils.cpp40
-rw-r--r--test/Transforms/NaCl/expand-tls-constexpr.ll36
-rw-r--r--test/Transforms/NaCl/expand-tls-phi.ll37
8 files changed, 128 insertions, 41 deletions
diff --git a/include/llvm/Transforms/NaCl.h b/include/llvm/Transforms/NaCl.h
index 58166bbf22..0a857653ba 100644
--- a/include/llvm/Transforms/NaCl.h
+++ b/include/llvm/Transforms/NaCl.h
@@ -14,7 +14,10 @@ namespace llvm {
class BasicBlockPass;
class FunctionPass;
+class Instruction;
class ModulePass;
+class Use;
+class Value;
FunctionPass *createExpandConstantExprPass();
ModulePass *createExpandCtorsPass();
@@ -26,6 +29,9 @@ ModulePass *createGlobalCleanupPass();
ModulePass *createResolveAliasesPass();
ModulePass *createStripMetadataPass();
+Instruction *PhiSafeInsertPt(Use *U);
+void PhiSafeReplaceUses(Use *U, Value *NewVal);
+
}
#endif
diff --git a/lib/Transforms/NaCl/CMakeLists.txt b/lib/Transforms/NaCl/CMakeLists.txt
index 1ca8e3d59e..5d630bb111 100644
--- a/lib/Transforms/NaCl/CMakeLists.txt
+++ b/lib/Transforms/NaCl/CMakeLists.txt
@@ -4,6 +4,7 @@ add_llvm_library(LLVMTransformsNaCl
ExpandGetElementPtr.cpp
ExpandTls.cpp
ExpandTlsConstantExpr.cpp
+ ExpandUtils.cpp
ExpandVarArgs.cpp
GlobalCleanup.cpp
StripMetadata.cpp
diff --git a/lib/Transforms/NaCl/ExpandConstantExpr.cpp b/lib/Transforms/NaCl/ExpandConstantExpr.cpp
index 5ff47cb00c..2856a9d7e4 100644
--- a/lib/Transforms/NaCl/ExpandConstantExpr.cpp
+++ b/lib/Transforms/NaCl/ExpandConstantExpr.cpp
@@ -63,31 +63,12 @@ static bool expandInstruction(Instruction *Inst) {
return false;
bool Modified = false;
- if (PHINode *PN = dyn_cast<PHINode>(Inst)) {
- // PHI nodes require special treatment. A incoming block may be
- // listed twice, but its incoming values must match so they must
- // be converted only once.
- std::map<BasicBlock*,Value*> Converted;
- for (unsigned OpNum = 0; OpNum < Inst->getNumOperands(); OpNum++) {
- if (ConstantExpr *Expr =
- dyn_cast<ConstantExpr>(Inst->getOperand(OpNum))) {
- Modified = true;
- BasicBlock *Incoming = PN->getIncomingBlock(OpNum);
- if (Converted.count(Incoming) == 0) {
- Converted[Incoming] = expandConstantExpr(Incoming->getTerminator(),
- Expr);
- }
- Inst->setOperand(OpNum, Converted[Incoming]);
- }
- }
- return Modified;
- }
-
for (unsigned OpNum = 0; OpNum < Inst->getNumOperands(); OpNum++) {
if (ConstantExpr *Expr =
dyn_cast<ConstantExpr>(Inst->getOperand(OpNum))) {
Modified = true;
- Inst->setOperand(OpNum, expandConstantExpr(Inst, Expr));
+ Use *U = &Inst->getOperandUse(OpNum);
+ PhiSafeReplaceUses(U, expandConstantExpr(PhiSafeInsertPt(U), Expr));
}
}
return Modified;
diff --git a/lib/Transforms/NaCl/ExpandTls.cpp b/lib/Transforms/NaCl/ExpandTls.cpp
index 929b2e0a15..53e1c92a96 100644
--- a/lib/Transforms/NaCl/ExpandTls.cpp
+++ b/lib/Transforms/NaCl/ExpandTls.cpp
@@ -250,15 +250,8 @@ static void rewriteTlsVars(Module &M, std::vector<VarInfo> *TlsVars,
++VarInfo) {
GlobalVariable *Var = VarInfo->TlsVar;
while (!Var->use_empty()) {
- Instruction *U = cast<Instruction>(*Var->use_begin());
- Instruction *InsertPt = U;
- if (PHINode *PN = dyn_cast<PHINode>(InsertPt)) {
- // We cannot insert instructions before a PHI node, so insert
- // before the incoming block's terminator. Note that if the
- // terminator is conditional, this could be suboptimal,
- // because we might be calling ReadTpFunc unnecessarily.
- InsertPt = PN->getIncomingBlock(Var->use_begin())->getTerminator();
- }
+ Use *U = &Var->use_begin().getUse();
+ Instruction *InsertPt = PhiSafeInsertPt(U);
Value *RawThreadPtr = CallInst::Create(ReadTpFunc, "tls_raw", InsertPt);
Value *TypedThreadPtr = new BitCastInst(RawThreadPtr, TemplatePtrType,
"tls_struct", InsertPt);
@@ -279,7 +272,7 @@ static void rewriteTlsVars(Module &M, std::vector<VarInfo> *TlsVars,
M.getContext(), APInt(32, VarInfo->TemplateIndex)));
Value *TlsField = GetElementPtrInst::Create(TypedThreadPtr, Indexes,
"field", InsertPt);
- U->replaceUsesOfWith(Var, TlsField);
+ PhiSafeReplaceUses(U, TlsField);
}
VarInfo->TlsVar->eraseFromParent();
}
diff --git a/lib/Transforms/NaCl/ExpandTlsConstantExpr.cpp b/lib/Transforms/NaCl/ExpandTlsConstantExpr.cpp
index 51b21cbf2a..328e5e08e6 100644
--- a/lib/Transforms/NaCl/ExpandTlsConstantExpr.cpp
+++ b/lib/Transforms/NaCl/ExpandTlsConstantExpr.cpp
@@ -78,18 +78,11 @@ static void expandConstExpr(Constant *Expr) {
if (ConstantExpr *CE = dyn_cast<ConstantExpr>(Expr)) {
while (!Expr->use_empty()) {
- Instruction *U = cast<Instruction>(*Expr->use_begin());
- Instruction *InsertPt = U;
- if (PHINode *PN = dyn_cast<PHINode>(InsertPt)) {
- // We cannot insert instructions before a PHI node, so insert
- // before the incoming block's terminator. This could be
- // suboptimal if the terminator is a conditional.
- InsertPt = PN->getIncomingBlock(Expr->use_begin())->getTerminator();
- }
+ Use *U = &Expr->use_begin().getUse();
Instruction *NewInst = CE->getAsInstruction();
- NewInst->insertBefore(InsertPt);
+ NewInst->insertBefore(PhiSafeInsertPt(U));
NewInst->setName("expanded");
- U->replaceUsesOfWith(CE, NewInst);
+ PhiSafeReplaceUses(U, NewInst);
}
}
}
diff --git a/lib/Transforms/NaCl/ExpandUtils.cpp b/lib/Transforms/NaCl/ExpandUtils.cpp
new file mode 100644
index 0000000000..0670ff75ce
--- /dev/null
+++ b/lib/Transforms/NaCl/ExpandUtils.cpp
@@ -0,0 +1,40 @@
+//===-- ExpandUtils.cpp - Helper functions for expansion passes -----------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/IR/BasicBlock.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/Transforms/NaCl.h"
+
+using namespace llvm;
+
+Instruction *llvm::PhiSafeInsertPt(Use *U) {
+ Instruction *InsertPt = cast<Instruction>(U->getUser());
+ if (PHINode *PN = dyn_cast<PHINode>(InsertPt)) {
+ // We cannot insert instructions before a PHI node, so insert
+ // before the incoming block's terminator. This could be
+ // suboptimal if the terminator is a conditional.
+ InsertPt = PN->getIncomingBlock(*U)->getTerminator();
+ }
+ return InsertPt;
+}
+
+void llvm::PhiSafeReplaceUses(Use *U, Value *NewVal) {
+ if (PHINode *PN = dyn_cast<PHINode>(U->getUser())) {
+ // A PHI node can have multiple incoming edges from the same
+ // block, in which case all these edges must have the same
+ // incoming value.
+ BasicBlock *BB = PN->getIncomingBlock(*U);
+ for (unsigned I = 0; I < PN->getNumIncomingValues(); ++I) {
+ if (PN->getIncomingBlock(I) == BB)
+ PN->setIncomingValue(I, NewVal);
+ }
+ } else {
+ U->getUser()->replaceUsesOfWith(U->get(), NewVal);
+ }
+}
diff --git a/test/Transforms/NaCl/expand-tls-constexpr.ll b/test/Transforms/NaCl/expand-tls-constexpr.ll
index 06bb8ed830..b7ab253692 100644
--- a/test/Transforms/NaCl/expand-tls-constexpr.ll
+++ b/test/Transforms/NaCl/expand-tls-constexpr.ll
@@ -114,3 +114,39 @@ return:
; CHECK: %expanded = getelementptr inbounds i32* @tvar, i32 1
; CHECK: return:
; CHECK: %result = phi i32* [ %expanded, %entry ], [ null, %else ]
+
+
+; This tests that ExpandTlsConstantExpr correctly handles a PHI node
+; that contains the same ConstantExpr twice. Using
+; replaceAllUsesWith() is not correct on a PHI node when the new
+; instruction has to be added to an incoming block.
+define i32 @test_converting_phi_twice(i1 %arg) {
+ br i1 %arg, label %iftrue, label %iffalse
+iftrue:
+ br label %exit
+iffalse:
+ br label %exit
+exit:
+ %result = phi i32 [ ptrtoint (i32* @tvar to i32), %iftrue ],
+ [ ptrtoint (i32* @tvar to i32), %iffalse ]
+ ret i32 %result
+}
+; CHECK: define i32 @test_converting_phi_twice(i1 %arg)
+; CHECK: iftrue:
+; CHECK: %expanded{{.*}} = ptrtoint i32* @tvar to i32
+; CHECK: iffalse:
+; CHECK: %expanded{{.*}} = ptrtoint i32* @tvar to i32
+; CHECK: exit:
+; CHECK: %result = phi i32 [ %expanded1, %iftrue ], [ %expanded, %iffalse ]
+
+
+define i32 @test_converting_phi_multiple_entry(i1 %arg) {
+entry:
+ br i1 %arg, label %done, label %done
+done:
+ %result = phi i32 [ ptrtoint (i32* @tvar to i32), %entry ],
+ [ ptrtoint (i32* @tvar to i32), %entry ]
+ ret i32 %result
+}
+; CHECK: define i32 @test_converting_phi_multiple_entry(i1 %arg)
+; CHECK: %result = phi i32 [ %expanded, %entry ], [ %expanded, %entry ]
diff --git a/test/Transforms/NaCl/expand-tls-phi.ll b/test/Transforms/NaCl/expand-tls-phi.ll
index 0292a1d633..4aa0a7a32c 100644
--- a/test/Transforms/NaCl/expand-tls-phi.ll
+++ b/test/Transforms/NaCl/expand-tls-phi.ll
@@ -16,8 +16,45 @@ return:
}
; The TLS access gets pushed back into the PHI node's incoming block,
; which might be suboptimal but works in all cases.
+; CHECK: define i32* @get_tvar(i1 %cmp) {
; CHECK: entry:
; CHECK: %field = getelementptr %tls_struct* %tls_struct, i32 -1, i32 0, i32 0
; CHECK: else:
; CHECK: return:
; CHECK: %result = phi i32* [ %field, %entry ], [ null, %else ]
+
+
+; This tests that ExpandTls correctly handles a PHI node that contains
+; the same TLS variable twice. Using replaceAllUsesWith() is not
+; correct on a PHI node when the new instruction has to be added to an
+; incoming block.
+define i32* @tls_phi_twice(i1 %arg) {
+ br i1 %arg, label %iftrue, label %iffalse
+iftrue:
+ br label %exit
+iffalse:
+ br label %exit
+exit:
+ %result = phi i32* [ @tvar, %iftrue ], [ @tvar, %iffalse ]
+ ret i32* %result
+}
+; CHECK: define i32* @tls_phi_twice(i1 %arg) {
+; CHECK: iftrue:
+; CHECK: %field{{.*}} = getelementptr %tls_struct* %tls_struct{{.*}}, i32 -1, i32 0, i32 0
+; CHECK: iffalse:
+; CHECK: %field{{.*}} = getelementptr %tls_struct* %tls_struct{{.*}}, i32 -1, i32 0, i32 0
+; CHECK: exit:
+; CHECK: %result = phi i32* [ %field{{.*}}, %iftrue ], [ %field{{.*}}, %iffalse ]
+
+
+; In this corner case, ExpandTls must expand out @tvar only once,
+; otherwise it will produce invalid IR.
+define i32* @tls_phi_multiple_entry(i1 %arg) {
+entry:
+ br i1 %arg, label %done, label %done
+done:
+ %result = phi i32* [ @tvar, %entry ], [ @tvar, %entry ]
+ ret i32* %result
+}
+; CHECK: define i32* @tls_phi_multiple_entry(i1 %arg) {
+; CHECK: %result = phi i32* [ %field, %entry ], [ %field, %entry ]