blob: 743cada622337aa8cf72a98237e115e5e089eab9 [file] [log] [blame] [edit]
//===- ConstantInsertExtractElementIndex.cpp - Insert/Extract element -----===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Transform all InsertElement and ExtractElement with non-constant or
// out-of-bounds indices into either in-bounds constant accesses or
// stack accesses. This moves all undefined behavior to the stack,
// making InsertElement and ExtractElement well-defined.
//
//===----------------------------------------------------------------------===//
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/Instruction.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/Module.h"
#include "llvm/Pass.h"
#include "llvm/Transforms/NaCl.h"
#include <algorithm>
using namespace llvm;
namespace {
class ConstantInsertExtractElementIndex : public BasicBlockPass {
public:
static char ID; // Pass identification, replacement for typeid
ConstantInsertExtractElementIndex() : BasicBlockPass(ID), M(0), DL(0) {
initializeConstantInsertExtractElementIndexPass(
*PassRegistry::getPassRegistry());
}
using BasicBlockPass::doInitialization;
bool doInitialization(Module &Mod) override {
M = &Mod;
return false; // Unchanged.
}
bool runOnBasicBlock(BasicBlock &BB) override;
private:
typedef SmallVector<Instruction *, 8> Instructions;
const Module *M;
const DataLayout *DL;
void findNonConstantInsertExtractElements(
const BasicBlock &BB, Instructions &OutOfRangeConstantIndices,
Instructions &NonConstantVectorIndices) const;
void fixOutOfRangeConstantIndices(BasicBlock &BB,
const Instructions &Instrs) const;
void fixNonConstantVectorIndices(BasicBlock &BB,
const Instructions &Instrs) const;
};
/// Number of elements in a vector instruction.
unsigned vectorNumElements(const Instruction *I) {
return cast<VectorType>(I->getOperand(0)->getType())->getNumElements();
}
/// Get the index of an InsertElement or ExtractElement instruction, or null.
Value *getInsertExtractElementIdx(const Instruction *I) {
switch (I->getOpcode()) {
default: return NULL;
case Instruction::InsertElement: return I->getOperand(2);
case Instruction::ExtractElement: return I->getOperand(1);
}
}
/// Set the index of an InsertElement or ExtractElement instruction.
void setInsertExtractElementIdx(Instruction *I, Value *NewIdx) {
switch (I->getOpcode()) {
default:
llvm_unreachable(
"expected instruction to be InsertElement or ExtractElement");
case Instruction::InsertElement: I->setOperand(2, NewIdx); break;
case Instruction::ExtractElement: I->setOperand(1, NewIdx); break;
}
}
} // anonymous namespace
char ConstantInsertExtractElementIndex::ID = 0;
INITIALIZE_PASS(
ConstantInsertExtractElementIndex, "constant-insert-extract-element-index",
"Force insert and extract vector element to always be in bounds", false,
false)
void ConstantInsertExtractElementIndex::findNonConstantInsertExtractElements(
const BasicBlock &BB, Instructions &OutOfRangeConstantIndices,
Instructions &NonConstantVectorIndices) const {
for (BasicBlock::const_iterator BBI = BB.begin(), BBE = BB.end(); BBI != BBE;
++BBI) {
const Instruction *I = &*BBI;
if (Value *Idx = getInsertExtractElementIdx(I)) {
if (ConstantInt *CI = dyn_cast<ConstantInt>(Idx)) {
if (!CI->getValue().ult(vectorNumElements(I)))
OutOfRangeConstantIndices.push_back(const_cast<Instruction *>(I));
} else
NonConstantVectorIndices.push_back(const_cast<Instruction *>(I));
}
}
}
void ConstantInsertExtractElementIndex::fixOutOfRangeConstantIndices(
BasicBlock &BB, const Instructions &Instrs) const {
for (Instructions::const_iterator IB = Instrs.begin(), IE = Instrs.end();
IB != IE; ++IB) {
Instruction *I = *IB;
const APInt &Idx =
cast<ConstantInt>(getInsertExtractElementIdx(I))->getValue();
APInt NumElements = APInt(Idx.getBitWidth(), vectorNumElements(I));
APInt NewIdx = Idx.urem(NumElements);
setInsertExtractElementIdx(I, ConstantInt::get(M->getContext(), NewIdx));
}
}
void ConstantInsertExtractElementIndex::fixNonConstantVectorIndices(
BasicBlock &BB, const Instructions &Instrs) const {
for (Instructions::const_iterator IB = Instrs.begin(), IE = Instrs.end();
IB != IE; ++IB) {
Instruction *I = *IB;
Value *Vec = I->getOperand(0);
Value *Idx = getInsertExtractElementIdx(I);
VectorType *VecTy = cast<VectorType>(Vec->getType());
Type *ElemTy = VecTy->getElementType();
unsigned ElemAlign = DL->getPrefTypeAlignment(ElemTy);
unsigned VecAlign = std::max(ElemAlign, DL->getPrefTypeAlignment(VecTy));
IRBuilder<> IRB(I);
AllocaInst *Alloca = IRB.CreateAlloca(
ElemTy, ConstantInt::get(Type::getInt32Ty(M->getContext()),
vectorNumElements(I)));
Alloca->setAlignment(VecAlign);
Value *AllocaAsVec = IRB.CreateBitCast(Alloca, VecTy->getPointerTo());
IRB.CreateAlignedStore(Vec, AllocaAsVec, Alloca->getAlignment());
Value *GEP = IRB.CreateGEP(Alloca, Idx);
Value *Res;
switch (I->getOpcode()) {
default:
llvm_unreachable("expected InsertElement or ExtractElement");
case Instruction::InsertElement:
IRB.CreateAlignedStore(I->getOperand(1), GEP, ElemAlign);
Res = IRB.CreateAlignedLoad(AllocaAsVec, Alloca->getAlignment());
break;
case Instruction::ExtractElement:
Res = IRB.CreateAlignedLoad(GEP, ElemAlign);
break;
}
I->replaceAllUsesWith(Res);
I->eraseFromParent();
}
}
bool ConstantInsertExtractElementIndex::runOnBasicBlock(BasicBlock &BB) {
bool Changed = false;
if (!DL)
DL = &BB.getParent()->getParent()->getDataLayout();
Instructions OutOfRangeConstantIndices;
Instructions NonConstantVectorIndices;
findNonConstantInsertExtractElements(BB, OutOfRangeConstantIndices,
NonConstantVectorIndices);
if (!OutOfRangeConstantIndices.empty()) {
Changed = true;
fixOutOfRangeConstantIndices(BB, OutOfRangeConstantIndices);
}
if (!NonConstantVectorIndices.empty()) {
Changed = true;
fixNonConstantVectorIndices(BB, NonConstantVectorIndices);
}
return Changed;
}
BasicBlockPass *llvm::createConstantInsertExtractElementIndexPass() {
return new ConstantInsertExtractElementIndex();
}