aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/llvm/InitializePasses.h1
-rw-r--r--include/llvm/Transforms/NaCl.h2
-rw-r--r--lib/Transforms/NaCl/CMakeLists.txt1
-rw-r--r--lib/Transforms/NaCl/ExpandGetElementPtr.cpp146
-rw-r--r--test/Transforms/NaCl/expand-getelementptr.ll109
-rw-r--r--tools/opt/opt.cpp1
6 files changed, 260 insertions, 0 deletions
diff --git a/include/llvm/InitializePasses.h b/include/llvm/InitializePasses.h
index 9934ff3a9a..d03a51fd70 100644
--- a/include/llvm/InitializePasses.h
+++ b/include/llvm/InitializePasses.h
@@ -279,6 +279,7 @@ void initializeMachineFunctionPrinterPassPass(PassRegistry&);
// @LOCALMOD-BEGIN
void initializeExpandConstantExprPass(PassRegistry&);
void initializeExpandCtorsPass(PassRegistry&);
+void initializeExpandGetElementPtrPass(PassRegistry&);
void initializeExpandTlsConstantExprPass(PassRegistry&);
void initializeExpandTlsPass(PassRegistry&);
void initializeExpandVarArgsPass(PassRegistry&);
diff --git a/include/llvm/Transforms/NaCl.h b/include/llvm/Transforms/NaCl.h
index b5108fbf32..6782f6581f 100644
--- a/include/llvm/Transforms/NaCl.h
+++ b/include/llvm/Transforms/NaCl.h
@@ -12,11 +12,13 @@
namespace llvm {
+class BasicBlockPass;
class FunctionPass;
class ModulePass;
FunctionPass *createExpandConstantExprPass();
ModulePass *createExpandCtorsPass();
+BasicBlockPass *createExpandGetElementPtrPass();
ModulePass *createExpandTlsPass();
ModulePass *createExpandTlsConstantExprPass();
ModulePass *createExpandVarArgsPass();
diff --git a/lib/Transforms/NaCl/CMakeLists.txt b/lib/Transforms/NaCl/CMakeLists.txt
index 5b1de88dc4..4e33abc1a7 100644
--- a/lib/Transforms/NaCl/CMakeLists.txt
+++ b/lib/Transforms/NaCl/CMakeLists.txt
@@ -1,6 +1,7 @@
add_llvm_library(LLVMTransformsNaCl
ExpandConstantExpr.cpp
ExpandCtors.cpp
+ ExpandGetElementPtr.cpp
ExpandTls.cpp
ExpandTlsConstantExpr.cpp
ExpandVarArgs.cpp
diff --git a/lib/Transforms/NaCl/ExpandGetElementPtr.cpp b/lib/Transforms/NaCl/ExpandGetElementPtr.cpp
new file mode 100644
index 0000000000..974c3193de
--- /dev/null
+++ b/lib/Transforms/NaCl/ExpandGetElementPtr.cpp
@@ -0,0 +1,146 @@
+//===- ExpandGetElementPtr.cpp - Expand GetElementPtr into arithmetic------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This pass expands out GetElementPtr instructions into ptrtoint,
+// inttoptr and arithmetic instructions.
+//
+// This simplifies the language so that the PNaCl translator does not
+// need to handle GetElementPtr and struct types as part of a stable
+// wire format for PNaCl.
+//
+// Note that we drop the "inbounds" attribute of GetElementPtr.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/IR/BasicBlock.h"
+#include "llvm/IR/DataLayout.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/InstrTypes.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/Type.h"
+#include "llvm/Pass.h"
+#include "llvm/Transforms/NaCl.h"
+
+using namespace llvm;
+
+namespace {
+ class ExpandGetElementPtr : public BasicBlockPass {
+ public:
+ static char ID; // Pass identification, replacement for typeid
+ ExpandGetElementPtr() : BasicBlockPass(ID) {
+ initializeExpandGetElementPtrPass(*PassRegistry::getPassRegistry());
+ }
+
+ virtual bool runOnBasicBlock(BasicBlock &BB);
+ };
+}
+
+char ExpandGetElementPtr::ID = 0;
+INITIALIZE_PASS(ExpandGetElementPtr, "expand-getelementptr",
+ "Expand out GetElementPtr instructions into arithmetic",
+ false, false)
+
+static Value *CastToPtrSize(Value *Val, Instruction *InsertPt,
+ const DebugLoc &Debug, Type *PtrType) {
+ unsigned ValSize = Val->getType()->getIntegerBitWidth();
+ unsigned PtrSize = PtrType->getIntegerBitWidth();
+ if (ValSize == PtrSize)
+ return Val;
+ Instruction *Inst;
+ if (ValSize > PtrSize) {
+ Inst = new TruncInst(Val, PtrType, "gep_trunc", InsertPt);
+ } else {
+ // GEP indexes must be sign-extended.
+ Inst = new SExtInst(Val, PtrType, "gep_sext", InsertPt);
+ }
+ Inst->setDebugLoc(Debug);
+ return Inst;
+}
+
+static void FlushOffset(Instruction **Ptr, uint64_t *CurrentOffset,
+ Instruction *InsertPt, const DebugLoc &Debug,
+ Type *PtrType) {
+ if (*CurrentOffset) {
+ *Ptr = BinaryOperator::Create(Instruction::Add, *Ptr,
+ ConstantInt::get(PtrType, *CurrentOffset),
+ "gep", InsertPt);
+ (*Ptr)->setDebugLoc(Debug);
+ *CurrentOffset = 0;
+ }
+}
+
+static void ExpandGEP(GetElementPtrInst *GEP, DataLayout *DL, Type *PtrType) {
+ const DebugLoc &Debug = GEP->getDebugLoc();
+ Instruction *Ptr = new PtrToIntInst(GEP->getPointerOperand(), PtrType,
+ "gep_int", GEP);
+ Ptr->setDebugLoc(Debug);
+
+ Type *CurrentTy = GEP->getPointerOperand()->getType();
+ // We do some limited constant folding ourselves. An alternative
+ // would be to generate verbose, unfolded output (e.g. multiple
+ // adds; adds of zero constants) and use a later pass such as
+ // "-instcombine" to clean that up. However, "-instcombine" can
+ // reintroduce GetElementPtr instructions.
+ uint64_t CurrentOffset = 0;
+
+ for (GetElementPtrInst::op_iterator Op = GEP->op_begin() + 1;
+ Op != GEP->op_end();
+ ++Op) {
+ Value *Index = *Op;
+ if (StructType *StTy = dyn_cast<StructType>(CurrentTy)) {
+ uint64_t Field = cast<ConstantInt>(Op)->getZExtValue();
+ CurrentTy = StTy->getElementType(Field);
+ CurrentOffset += DL->getStructLayout(StTy)->getElementOffset(Field);
+ } else {
+ CurrentTy = cast<SequentialType>(CurrentTy)->getElementType();
+ uint64_t ElementSize = DL->getTypeAllocSize(CurrentTy);
+ if (ConstantInt *C = dyn_cast<ConstantInt>(Index)) {
+ CurrentOffset += C->getSExtValue() * ElementSize;
+ } else {
+ FlushOffset(&Ptr, &CurrentOffset, GEP, Debug, PtrType);
+ Index = CastToPtrSize(Index, GEP, Debug, PtrType);
+ Instruction *Mul = BinaryOperator::Create(
+ Instruction::Mul, Index, ConstantInt::get(PtrType, ElementSize),
+ "gep_array", GEP);
+ Mul->setDebugLoc(Debug);
+ Ptr = BinaryOperator::Create(Instruction::Add, Ptr, Mul, "gep", GEP);
+ Ptr->setDebugLoc(Debug);
+ }
+ }
+ }
+ FlushOffset(&Ptr, &CurrentOffset, GEP, Debug, PtrType);
+
+ assert(CurrentTy == GEP->getType()->getElementType());
+ Instruction *Result = new IntToPtrInst(Ptr, GEP->getType(), "", GEP);
+ Result->setDebugLoc(Debug);
+ Result->takeName(GEP);
+ GEP->replaceAllUsesWith(Result);
+ GEP->eraseFromParent();
+}
+
+bool ExpandGetElementPtr::runOnBasicBlock(BasicBlock &BB) {
+ bool Modified = false;
+ DataLayout DL(BB.getParent()->getParent());
+ Type *PtrType = DL.getIntPtrType(BB.getContext());
+
+ for (BasicBlock::InstListType::iterator Iter = BB.begin();
+ Iter != BB.end(); ) {
+ Instruction *Inst = Iter++;
+ if (GetElementPtrInst *GEP = dyn_cast<GetElementPtrInst>(Inst)) {
+ Modified = true;
+ ExpandGEP(GEP, &DL, PtrType);
+ }
+ }
+ return Modified;
+}
+
+BasicBlockPass *llvm::createExpandGetElementPtrPass() {
+ return new ExpandGetElementPtr();
+}
diff --git a/test/Transforms/NaCl/expand-getelementptr.ll b/test/Transforms/NaCl/expand-getelementptr.ll
new file mode 100644
index 0000000000..2a089f2dc4
--- /dev/null
+++ b/test/Transforms/NaCl/expand-getelementptr.ll
@@ -0,0 +1,109 @@
+; RUN: opt < %s -expand-getelementptr -S | FileCheck %s
+
+target datalayout = "p:32:32:32"
+
+%MyStruct = type { i8, i32, i8 }
+%MyArray = type { [100 x i64] }
+
+
+; Test indexing struct field
+define i8* @test_struct_field(%MyStruct* %ptr) {
+ %addr = getelementptr %MyStruct* %ptr, i32 0, i32 2
+ ret i8* %addr
+}
+; CHECK: @test_struct_field
+; CHECK-NEXT: %gep_int = ptrtoint %MyStruct* %ptr to i32
+; CHECK-NEXT: %gep = add i32 %gep_int, 8
+; CHECK-NEXT: %addr = inttoptr i32 %gep to i8*
+; CHECK-NEXT: ret i8* %addr
+
+
+; Test non-constant index into an array
+define i64* @test_array_index(%MyArray* %ptr, i32 %index) {
+ %addr = getelementptr %MyArray* %ptr, i32 0, i32 0, i32 %index
+ ret i64* %addr
+}
+; CHECK: @test_array_index
+; CHECK-NEXT: %gep_int = ptrtoint %MyArray* %ptr to i32
+; CHECK-NEXT: %gep_array = mul i32 %index, 8
+; CHECK-NEXT: %gep = add i32 %gep_int, %gep_array
+; CHECK-NEXT: %addr = inttoptr i32 %gep to i64*
+; CHECK-NEXT: ret i64* %addr
+
+
+; Test constant index into an array (as a pointer)
+define %MyStruct* @test_ptr_add(%MyStruct* %ptr) {
+ %addr = getelementptr %MyStruct* %ptr, i32 2
+ ret %MyStruct* %addr
+}
+; CHECK: @test_ptr_add
+; CHECK-NEXT: %gep_int = ptrtoint %MyStruct* %ptr to i32
+; CHECK-NEXT: %gep = add i32 %gep_int, 24
+; CHECK-NEXT: %addr = inttoptr i32 %gep to %MyStruct*
+; CHECK-NEXT: ret %MyStruct* %addr
+
+
+; Test that additions and multiplications are combined properly
+define i64* @test_add_and_index(%MyArray* %ptr, i32 %index) {
+ %addr = getelementptr %MyArray* %ptr, i32 1, i32 0, i32 %index
+ ret i64* %addr
+}
+; CHECK: @test_add_and_index
+; CHECK-NEXT: %gep_int = ptrtoint %MyArray* %ptr to i32
+; CHECK-NEXT: %gep = add i32 %gep_int, 800
+; CHECK-NEXT: %gep_array = mul i32 %index, 8
+; CHECK-NEXT: %gep1 = add i32 %gep, %gep_array
+; CHECK-NEXT: %addr = inttoptr i32 %gep1 to i64*
+; CHECK-NEXT: ret i64* %addr
+
+
+; Test >32-bit array index
+define i64* @test_array_index64(%MyArray* %ptr, i64 %index) {
+ %addr = getelementptr %MyArray* %ptr, i32 0, i32 0, i64 %index
+ ret i64* %addr
+}
+; CHECK: @test_array_index64
+; CHECK-NEXT: %gep_int = ptrtoint %MyArray* %ptr to i32
+; CHECK-NEXT: %gep_trunc = trunc i64 %index to i32
+; CHECK-NEXT: %gep_array = mul i32 %gep_trunc, 8
+; CHECK-NEXT: %gep = add i32 %gep_int, %gep_array
+; CHECK-NEXT: %addr = inttoptr i32 %gep to i64*
+; CHECK-NEXT: ret i64* %addr
+
+
+; Test <32-bit array index
+define i64* @test_array_index16(%MyArray* %ptr, i16 %index) {
+ %addr = getelementptr %MyArray* %ptr, i32 0, i32 0, i16 %index
+ ret i64* %addr
+}
+; CHECK: @test_array_index16
+; CHECK-NEXT: %gep_int = ptrtoint %MyArray* %ptr to i32
+; CHECK-NEXT: %gep_sext = sext i16 %index to i32
+; CHECK-NEXT: %gep_array = mul i32 %gep_sext, 8
+; CHECK-NEXT: %gep = add i32 %gep_int, %gep_array
+; CHECK-NEXT: %addr = inttoptr i32 %gep to i64*
+; CHECK-NEXT: ret i64* %addr
+
+
+; Test >32-bit constant array index
+define i64* @test_array_index64_const(%MyArray* %ptr) {
+ %addr = getelementptr %MyArray* %ptr, i32 0, i32 0, i64 100
+ ret i64* %addr
+}
+; CHECK: @test_array_index64_const
+; CHECK-NEXT: %gep_int = ptrtoint %MyArray* %ptr to i32
+; CHECK-NEXT: %gep = add i32 %gep_int, 800
+; CHECK-NEXT: %addr = inttoptr i32 %gep to i64*
+; CHECK-NEXT: ret i64* %addr
+
+
+; Test <32-bit constant array index -- test sign extension
+define i64* @test_array_index16_const(%MyArray* %ptr) {
+ %addr = getelementptr %MyArray* %ptr, i32 0, i32 0, i16 -100
+ ret i64* %addr
+}
+; CHECK: @test_array_index16_const
+; CHECK-NEXT: %gep_int = ptrtoint %MyArray* %ptr to i32
+; CHECK-NEXT: %gep = add i32 %gep_int, -800
+; CHECK-NEXT: %addr = inttoptr i32 %gep to i64*
+; CHECK-NEXT: ret i64* %addr
diff --git a/tools/opt/opt.cpp b/tools/opt/opt.cpp
index 337419553a..c682768164 100644
--- a/tools/opt/opt.cpp
+++ b/tools/opt/opt.cpp
@@ -579,6 +579,7 @@ int main(int argc, char **argv) {
// @LOCALMOD-BEGIN
initializeExpandConstantExprPass(Registry);
initializeExpandCtorsPass(Registry);
+ initializeExpandGetElementPtrPass(Registry);
initializeExpandTlsPass(Registry);
initializeExpandTlsConstantExprPass(Registry);
initializeExpandVarArgsPass(Registry);