aboutsummaryrefslogtreecommitdiff
path: root/lib/Transforms/NaCl/ExpandGetElementPtr.cpp
blob: 1fe11293ca52cc571db95e7dab8522f45ba3f32e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
//===- 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);
        if (ElementSize != 1) {
          Index = CopyDebug(
              BinaryOperator::Create(Instruction::Mul, Index,
                                     ConstantInt::get(PtrType, ElementSize),
                                     "gep_array", GEP),
              GEP);
        }
        Ptr = BinaryOperator::Create(Instruction::Add, Ptr,
                                     Index, "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();
}