blob: 4c3b281fd624f843e25fd1d074b340130aab983c [file] [edit]
// Copyright 2017 The Clspv Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/Module.h"
#include "llvm/Pass.h"
#include "llvm/Support/raw_ostream.h"
#include <tuple>
#include "BitcastUtils.h"
#include "SimplifyPointerBitcastPass.h"
#include "Types.h"
#include "clspv/AddressSpace.h"
#include "clspv/Option.h"
using namespace llvm;
using namespace BitcastUtils;
#define DEBUG_TYPE "SimplifyPointerBitcast"
PreservedAnalyses
clspv::SimplifyPointerBitcastPass::run(Module &M, ModuleAnalysisManager &) {
PreservedAnalyses PA;
// Start by removing all Constant Expressions. Do it only once as all the
// functions below will not generate new Constant Expression.
runOnInstFromCstExpr(M);
// Loop through our individual simplification passes until they stop changing
// things.
bool changed = true;
while (changed) {
changed = false;
changed |= runOnTrivialBitcast(M);
changed |= runOnBitcastFromBitcast(M);
changed |= runOnImplicitGEP(M);
while (runOnUpgradeableConstantCasts(M)) {
changed = true;
}
changed |= runOnUnneededIndices(M);
changed |= runOnImplicitCasts(M);
changed |= runOnAllocaNotAliasing(M);
changed |= runOnPHIFromGEP(M);
changed |= runOnGEPFromGEP(M);
}
return PA;
}
void clspv::SimplifyPointerBitcastPass::runOnInstFromCstExpr(Module &M) const {
for (Function &F : M) {
BitcastUtils::RemoveCstExprFromFunction(&F);
}
}
bool clspv::SimplifyPointerBitcastPass::runOnTrivialBitcast(Module &M) const {
// Remove things like:
// bitcast i32 addrspace(1)* %ptr to i32 addrspace(1)*
SmallVector<BitCastInst *, 16> WorkList;
for (Function &F : M) {
for (BasicBlock &BB : F) {
for (Instruction &I : BB) {
// If we have a bitcast instruction...
if (auto Bitcast = dyn_cast<BitCastInst>(&I)) {
// ... whose source type is the same as the destination type.
auto Source = Bitcast->getOperand(0);
if (Source->getType() == Bitcast->getType()) {
// ... record the bitcast as something we need to process.
WorkList.push_back(Bitcast);
}
}
}
}
}
const bool Changed = !WorkList.empty();
for (auto Bitcast : WorkList) {
auto Source = Bitcast->getOperand(0);
LLVM_DEBUG(dbgs() << "\n##runOnTrivialBitcast:\nreplaceAllUses of :";
Bitcast->dump(); dbgs() << "by: "; Source->dump());
Bitcast->replaceAllUsesWith(Source);
// Remove the bitcast as it has no users now.
Bitcast->eraseFromParent();
// Check if the source value is an instruction and had no other users...
if (auto SourceInst = dyn_cast<Instruction>(Source)) {
if (0 == SourceInst->getNumUses()) {
// ... and remove it if we were its only user.
SourceInst->eraseFromParent();
}
}
}
return Changed;
}
bool clspv::SimplifyPointerBitcastPass::runOnBitcastFromBitcast(
Module &M) const {
SmallVector<BitCastInst *, 16> WorkList;
for (Function &F : M) {
for (BasicBlock &BB : F) {
for (Instruction &I : BB) {
// If we have a bitcast instruction...
if (auto Bitcast = dyn_cast<BitCastInst>(&I)) {
// ... whose source is a bitcast instruction...
if (isa<BitCastInst>(Bitcast->getOperand(0))) {
// ... record the bitcast as something we need to process.
WorkList.push_back(Bitcast);
}
// ... whose source is a bitcast constant expression
if (const auto CE = dyn_cast<ConstantExpr>(Bitcast->getOperand(0))) {
if (CE->getOpcode() == Instruction::BitCast) {
// ... record the bitcast as something we need to process.
WorkList.push_back(Bitcast);
}
}
}
}
}
}
const bool Changed = !WorkList.empty();
for (auto Bitcast : WorkList) {
Instruction *OtherBitcast = dyn_cast<BitCastInst>(Bitcast->getOperand(0));
if (!OtherBitcast) {
auto CE = dyn_cast<ConstantExpr>(Bitcast->getOperand(0));
assert(CE && CE->getOpcode() == Instruction::BitCast);
OtherBitcast = CE->getAsInstruction();
OtherBitcast->insertBefore(Bitcast->getIterator());
}
LLVM_DEBUG(dbgs() << "\n##runOnBitcastFromBitcast:\nremove:";
Bitcast->dump());
if (OtherBitcast->getType() == Bitcast->getType()) {
Bitcast->replaceAllUsesWith(OtherBitcast);
LLVM_DEBUG(dbgs() << "in favor of: "; OtherBitcast->dump());
} else {
// Create a new bitcast from the other bitcasts argument to our type.
auto NewBitcast =
CastInst::Create(Instruction::BitCast, OtherBitcast->getOperand(0),
Bitcast->getType(), "", Bitcast->getIterator());
// And replace the original bitcast with our replacement bitcast.
Bitcast->replaceAllUsesWith(NewBitcast);
LLVM_DEBUG(dbgs() << "in favor of: "; NewBitcast->dump());
}
// Remove the bitcast as it has no users now.
Bitcast->eraseFromParent();
// Check if the other bitcast had no other users...
if (0 == OtherBitcast->getNumUses()) {
// ... and remove it if we were its only user.
OtherBitcast->eraseFromParent();
}
}
return Changed;
}
bool clspv::SimplifyPointerBitcastPass::runOnGEPFromGEP(Module &M) const {
SmallVector<GetElementPtrInst *, 16> WorkList;
for (Function &F : M) {
for (BasicBlock &BB : F) {
for (Instruction &I : BB) {
// If we have a GEP instruction...
if (auto GEP = dyn_cast<GetElementPtrInst>(&I)) {
if (Option::UntypedPointerAddressSpace(
GEP->getType()->getPointerAddressSpace())) {
continue;
}
// ... whose operand is also a GEP instruction...
if (auto OtherGEP =
dyn_cast<GetElementPtrInst>(GEP->getPointerOperand())) {
// ... with no implicit cast between them...
if (OtherGEP->getResultElementType() ==
GEP->getSourceElementType()) {
auto LastTypeStruct = [](Type *Ty, SmallVector<Value *> Idxs) {
Idxs.pop_back();
return dyn_cast<StructType>(
GetElementPtrInst::getIndexedType(Ty, Idxs));
};
auto ConstantValue = [](Value *V) {
if (auto CstVal = dyn_cast<ConstantInt>(V)) {
return CstVal->getZExtValue();
}
return UINT64_MAX;
};
auto LastTypeStructTy =
LastTypeStruct(OtherGEP->getSourceElementType(),
SmallVector<Value *>(OtherGEP->indices()));
if (GEP->getNumOperands() > 1 && LastTypeStructTy != nullptr &&
((ConstantValue(GEP->getOperand(1)) >=
LastTypeStructTy->getStructNumElements() &&
IsArrayLike(LastTypeStructTy)) ||
(ConstantValue(GEP->getOperand(1)) != 0 &&
!IsArrayLike(LastTypeStructTy))) &&
!(GEP->hasAllConstantIndices() &&
OtherGEP->hasAllConstantIndices())) {
LLVM_DEBUG(dbgs() << "\n##runOnGEPFromGEP:\nskip (out-of-bound "
"struct access): ";
OtherGEP->dump(); GEP->dump());
continue;
}
// ... record the GEP as something we need to process.
WorkList.push_back(GEP);
}
}
}
}
}
}
const bool Changed = !WorkList.empty();
for (GetElementPtrInst *GEP : WorkList) {
IRBuilder<> Builder(GEP);
auto OtherGEP = cast<GetElementPtrInst>(GEP->getPointerOperand());
LLVM_DEBUG(dbgs() << "\n##runOnGEPFromGEP:\nreplace: "; OtherGEP->dump();
GEP->dump());
Value *SrcLastIdxOp = OtherGEP->getOperand(OtherGEP->getNumOperands() - 1);
Value *GEPIdxOp = GEP->getOperand(1);
// Add the indices together, if the last one from before is not zero.
auto *CstSrcLastIdxOp = dyn_cast<ConstantInt>(SrcLastIdxOp);
auto CstGEPIdxOp = dyn_cast<ConstantInt>(GEPIdxOp);
// We need to know the number of element of the commun type between GEP and
// OtherGEP to avoid generating a constant index greater than this size for
// private pointer
uint32_t VecOrArraySize = UINT32_MAX;
if (OtherGEP->getNumOperands() > 2) {
SmallVector<Value *, 8> Idxs;
Idxs.append(OtherGEP->op_begin() + 1, OtherGEP->op_end() - 1);
unsigned numEle =
BitcastUtils::GetNumEle(GetElementPtrInst::getIndexedType(
OtherGEP->getSourceElementType(), Idxs));
if (numEle > 1 || numEle == 0) {
VecOrArraySize = numEle;
}
} else if (OtherGEP->getNumOperands() == 2) {
VecOrArraySize =
BitcastUtils::GetNumEle(OtherGEP->getSourceElementType());
}
bool OtherGEPPrevIsCstToStruct = false;
ConstantInt *OtherGEPLastIdx = nullptr;
Type *OtherGEPPrevTy = nullptr;
SmallVector<Value *, 4> OtherGEPIdxs;
if (OtherGEP->getNumIndices() > 1) {
OtherGEPIdxs.append(SmallVector<Value *, 4>(OtherGEP->indices()));
OtherGEPLastIdx =
dyn_cast<ConstantInt>(OtherGEPIdxs[OtherGEP->getNumIndices() - 1]);
OtherGEPIdxs.pop_back();
OtherGEPPrevTy = GetElementPtrInst::getIndexedType(
OtherGEP->getSourceElementType(), OtherGEPIdxs);
OtherGEPPrevIsCstToStruct =
GEP->hasAllConstantIndices() && OtherGEPPrevTy &&
OtherGEPPrevTy->isStructTy() && OtherGEPLastIdx &&
!GEP->getSourceElementType()->isAggregateType();
}
SmallVector<Value *, 8> Idxs;
if (OtherGEPPrevIsCstToStruct) {
int64_t cstVal;
Value *dynVal;
size_t smallerBitWidths;
ExtractOffsetFromGEP(M.getDataLayout(), Builder, GEP, cstVal, dynVal,
smallerBitWidths);
assert(dynVal == nullptr);
cstVal *= smallerBitWidths;
int64_t cstVal2 = 0;
size_t smallerBitWidths2 =
SizeInBits(M.getDataLayout(), OtherGEP->getResultElementType());
ExtractOffsetFromStruct(M.getDataLayout(), OtherGEPLastIdx,
dyn_cast<StructType>(OtherGEPPrevTy), cstVal2,
smallerBitWidths2);
cstVal += smallerBitWidths2 * cstVal2;
smallerBitWidths = std::min(smallerBitWidths, smallerBitWidths2);
cstVal /= smallerBitWidths;
auto newGEPIdxs = GetIdxsForTyFromOffset(
M.getDataLayout(), Builder, OtherGEPPrevTy,
GEP->getResultElementType(), cstVal, dynVal, smallerBitWidths,
OtherGEP->getPointerOperand());
Idxs.append(OtherGEPIdxs);
Idxs.append(
SmallVector<Value *, 4>(newGEPIdxs.begin() + 1, newGEPIdxs.end()));
} else if (GEP->hasAllConstantIndices() &&
OtherGEP->hasAllConstantIndices() &&
(GEP->getPointerAddressSpace() == clspv::AddressSpace::Private ||
GEP->getPointerAddressSpace() == clspv::AddressSpace::Local)) {
int64_t cstVal;
Value *dynVal;
size_t smallerBitWidths;
ExtractOffsetFromGEP(M.getDataLayout(), Builder, OtherGEP, cstVal, dynVal,
smallerBitWidths);
assert(dynVal == nullptr);
int64_t cstVal2;
size_t smallerBitWidths2;
ExtractOffsetFromGEP(M.getDataLayout(), Builder, GEP, cstVal2, dynVal,
smallerBitWidths2);
assert(dynVal == nullptr);
cstVal = cstVal * smallerBitWidths + cstVal2 * smallerBitWidths2;
smallerBitWidths = std::min(smallerBitWidths, smallerBitWidths2);
cstVal /= smallerBitWidths;
auto NewGEPIdxs = GetIdxsForTyFromOffset(
M.getDataLayout(), Builder, OtherGEP->getSourceElementType(),
GEP->getResultElementType(), cstVal, dynVal, smallerBitWidths,
OtherGEP->getPointerOperand());
Idxs.append(NewGEPIdxs);
} else {
if (CstSrcLastIdxOp && CstGEPIdxOp &&
(CstSrcLastIdxOp->getZExtValue() + CstGEPIdxOp->getZExtValue() <
VecOrArraySize)) {
Idxs.append(OtherGEP->op_begin() + 1, OtherGEP->op_end() - 1);
if (CstGEPIdxOp->isZero()) {
Idxs.push_back(SrcLastIdxOp);
} else if (CstSrcLastIdxOp->isZero()) {
Idxs.push_back(GEPIdxOp);
} else {
Idxs.push_back(ConstantInt::get(
IntegerType::get(M.getContext(),
clspv::PointersAre64Bit(M) &&
!OtherGEP->getType()->isStructTy()
? 64
: 32),
CstGEPIdxOp->getZExtValue() + CstSrcLastIdxOp->getZExtValue()));
}
} else if (CstGEPIdxOp && CstGEPIdxOp->isZero()) {
Idxs.append(OtherGEP->op_begin() + 1, OtherGEP->op_end());
} else if (VecOrArraySize == 0) {
Idxs.append(OtherGEP->op_begin() + 1, OtherGEP->op_end() - 1);
if (CstSrcLastIdxOp && CstGEPIdxOp) {
Idxs.push_back(ConstantInt::get(
IntegerType::get(M.getContext(),
clspv::PointersAre64Bit(M) &&
!OtherGEP->getType()->isStructTy()
? 64
: 32),
CstGEPIdxOp->getZExtValue() + CstSrcLastIdxOp->getZExtValue()));
} else if (CstSrcLastIdxOp && CstSrcLastIdxOp->isZero()) {
Idxs.push_back(*(GEP->op_begin() + 1));
} else if (CstGEPIdxOp && CstGEPIdxOp->isZero()) {
Idxs.push_back(*(OtherGEP->op_end() - 1));
} else {
Idxs.push_back(CreateAdd(Builder, *(GEP->op_begin() + 1),
*(OtherGEP->op_end() - 1)));
}
} else {
uint32_t startIndex = 0;
SmallVector<Value *, 8> TyIdxs;
SmallVector<Type *, 8> Types;
for (uint32_t i = 0; i < OtherGEP->getNumOperands() - 1; i++) {
Value *op = OtherGEP->getOperand(i + 1);
TyIdxs.push_back(op);
Type *idxTy = GetElementPtrInst::getIndexedType(
OtherGEP->getSourceElementType(), TyIdxs);
Types.push_back(idxTy);
if (i > 0 && isa<StructType>(Types[i - 1])) {
startIndex = i + 1;
}
// If an unsized type appears due to a representation of a descriptor,
// skip the outer struct.
if (SizeInBits(M.getDataLayout(), idxTy) == 0) {
startIndex = 0;
}
}
if (startIndex != 0) {
// We have to assume that these geps were simply split since we
// traversed a struct. We would not calculate an appropriate offset
// into a particular struct.
Idxs.append(OtherGEP->op_begin() + 1, OtherGEP->op_end() - 1);
if (CstSrcLastIdxOp && CstSrcLastIdxOp->isZero()) {
Idxs.push_back(GEP->getOperand(1));
} else {
Idxs.push_back(
Builder.CreateAdd(GEPIdxOp, *(OtherGEP->op_end() - 1)));
}
} else {
int64_t cstVal;
Value *dynVal;
size_t smallerBitWidths;
ExtractOffsetFromGEP(M.getDataLayout(), Builder, OtherGEP, cstVal,
dynVal, smallerBitWidths);
if (CstGEPIdxOp) {
cstVal += CstGEPIdxOp->getZExtValue();
} else if (dynVal) {
dynVal = CreateAdd(Builder, dynVal, GEPIdxOp);
} else {
dynVal = GEPIdxOp;
}
auto NewGEPIdxs = GetIdxsForTyFromOffset(
M.getDataLayout(), Builder, OtherGEP->getSourceElementType(),
OtherGEP->getResultElementType(), cstVal, dynVal,
smallerBitWidths, OtherGEP->getPointerOperand());
Idxs.append(NewGEPIdxs);
}
}
Idxs.append(GEP->op_begin() + 2, GEP->op_end());
}
// Struct types require i32 indexes, so fix up any constant i64s
// that are safe to change
if (clspv::PointersAre64Bit(M) &&
OtherGEP->getSourceElementType()->isStructTy()) {
SmallVector<Value *, 8> NewIdxs;
for (auto *Idx : Idxs) {
if (auto ConstIdx = dyn_cast<ConstantInt>(Idx)) {
uint64_t C = ConstIdx->getZExtValue();
if (C < std::numeric_limits<uint32_t>::max()) {
NewIdxs.push_back(
ConstantInt::get(IntegerType::get(M.getContext(), 32), C));
} else {
NewIdxs.push_back(Idx);
}
} else {
NewIdxs.push_back(Idx);
}
}
Idxs = NewIdxs;
}
Value *NewGEP = nullptr;
// Create the new GEP. If we used the Builder it will do some folding
// that we don't want. In particular, if the first GEP is to an LLVM
// constant then the combined GEP will become a ConstantExpr and it
// will hide the pointer from subsequent passes. So bypass the Builder
// and create the GEP instruction directly.
if (GEP->isInBounds() && OtherGEP->isInBounds()) {
NewGEP = GetElementPtrInst::CreateInBounds(
OtherGEP->getSourceElementType(), OtherGEP->getPointerOperand(), Idxs,
"", GEP->getIterator());
} else {
NewGEP = GetElementPtrInst::Create(OtherGEP->getSourceElementType(),
OtherGEP->getPointerOperand(), Idxs,
"", GEP->getIterator());
}
LLVM_DEBUG(dbgs() << "by: "; NewGEP->dump());
// And replace the original GEP with our replacement GEP.
GEP->replaceAllUsesWith(NewGEP);
// Remove the GEP as it has no users now.
GEP->eraseFromParent();
// Check if the other GEP had no other users...
if (0 == OtherGEP->getNumUses()) {
// ... and remove it if we were its only user.
OtherGEP->eraseFromParent();
}
}
return Changed;
}
bool clspv::SimplifyPointerBitcastPass::runOnImplicitGEP(Module &M) const {
const DataLayout &DL = M.getDataLayout();
bool changed = false;
DenseMap<Value *, Type *> type_cache;
struct ImplicitGEPAliasing {
Instruction *inst;
int steps;
GetElementPtrInst *gep;
};
struct ImplicitGEPBeforeStore {
Instruction *inst;
Type *ty;
};
SmallVector<ImplicitGEPAliasing> GEPAliasingList;
SmallVector<ImplicitGEPBeforeStore> GEPBeforeStoreList;
SmallVector<LoadInst *> GEPBeforeLoadList;
SmallVector<GetElementPtrInst *> GEPCastList;
SmallVector<GetElementPtrInst *> GEPGVList;
for (auto &F : M) {
for (auto &BB : F) {
for (auto &I : BB) {
Value *source = nullptr;
Type *source_ty = nullptr;
Type *dest_ty = nullptr;
if (!IsImplicitCasts(M, type_cache, I, source, source_ty, dest_ty, true,
false /* do not rework unsized types */)) {
continue;
}
int Steps = 0;
bool PerfectMatch;
if (FindAliasingContainedType(source_ty, dest_ty, Steps, PerfectMatch,
DL)) {
// PHI Node are handle in runOnPHIFromGEP if it is really needed
if (isa<PHINode>(&I)) {
continue;
}
// Single level GEP is ok to transform, but beyond
// that the address math must be divided among other
// entries.
auto *gep = dyn_cast<GetElementPtrInst>(&I);
auto *call = dyn_cast_or_null<CallInst>(&I);
bool userCall = call && !call->getCalledFunction()->isDeclaration();
if (!userCall && (gep || PerfectMatch)) {
GEPAliasingList.push_back(
{&I, Steps, PerfectMatch ? nullptr : gep});
}
} else if (isa<StoreInst>(&I) && !isa<GetElementPtrInst>(source)) {
GEPBeforeStoreList.push_back({&I, dest_ty});
} else if (isa<LoadInst>(&I) && isa<GetElementPtrInst>(source) &&
SizeInBits(DL, dest_ty) < SizeInBits(DL, source_ty)) {
GEPBeforeLoadList.push_back(dyn_cast<LoadInst>(&I));
} else if (auto gep = dyn_cast<GetElementPtrInst>(&I)) {
if (IsClspvResourceOrLocal(gep->getPointerOperand()) ||
gep->getResultElementType() == source_ty) {
GEPCastList.push_back(dyn_cast<GetElementPtrInst>(&I));
} else if (IsGVConstantGEP(gep)) {
GEPGVList.push_back(dyn_cast<GetElementPtrInst>(&I));
}
}
}
}
}
// Implicit GEPs (i.e. GEPs that are elided because all indices are zero) are
// handled by explcitly inserting the GEP.
for (auto GEPInfo : GEPAliasingList) {
auto *I = GEPInfo.inst;
auto Steps = GEPInfo.steps;
auto *gep = GEPInfo.gep;
IRBuilder<> Builder{I};
SmallVector<Value *, 8> GEPIndices{};
unsigned PointerOperandNum = BitcastUtils::PointerOperandNum(I);
for (int i = 0; i < Steps + 1; i++) {
GEPIndices.push_back(Builder.getInt32(0));
}
Value *PointerOp = I->getOperand(PointerOperandNum);
auto *PointerOpType =
clspv::InferType(PointerOp, M.getContext(), &type_cache);
auto *NewGEP = GetElementPtrInst::Create(PointerOpType, PointerOp,
GEPIndices, "", I->getIterator());
LLVM_DEBUG(dbgs() << "\n##runOnImplicitGEP (aliasing):\nadding: ";
NewGEP->dump());
if (gep) {
// Typical usecase here is a GEP on a struct of float, followed by a GEP
// on a int. Replace the last GEP by a GEP on a float.
auto *NewCastGEP = GetElementPtrInst::Create(
NewGEP->getResultElementType(), NewGEP,
SmallVector<Value *, 1>(gep->indices()), "", I->getIterator());
LLVM_DEBUG(dbgs() << "instead of: "; I->dump());
I->replaceAllUsesWith(NewCastGEP);
I->eraseFromParent();
} else {
LLVM_DEBUG(dbgs() << "instead of operand " << PointerOperandNum
<< " of: ";
I->dump());
I->setOperand(PointerOperandNum, NewGEP);
}
changed = true;
}
for (auto GEPInfo : GEPBeforeStoreList) {
auto *I = GEPInfo.inst;
auto *Ty = GEPInfo.ty;
IRBuilder<> Builder{I};
unsigned PointerOperandNum = BitcastUtils::PointerOperandNum(I);
Value *PointerOp = I->getOperand(PointerOperandNum);
auto gep = GetElementPtrInst::Create(Ty, PointerOp, {Builder.getInt32(0)},
"", I->getIterator());
LLVM_DEBUG(dbgs() << "\n##runOnImplicitGEP (before store):\nadding: ";
gep->dump());
LLVM_DEBUG(dbgs() << "instead of operand " << PointerOperandNum << " of: ";
I->dump());
I->setOperand(PointerOperandNum, gep);
changed = true;
}
for (auto *LoadInst : GEPBeforeLoadList) {
IRBuilder<> Builder{LoadInst};
auto *Ty = LoadInst->getType();
auto initial_gep =
dyn_cast<GetElementPtrInst>(LoadInst->getPointerOperand());
auto Ptr = initial_gep->getPointerOperand();
int64_t cstVal;
Value *dynVal;
size_t smallerBitWidths;
ExtractOffsetFromGEP(M.getDataLayout(), Builder, initial_gep, cstVal,
dynVal, smallerBitWidths);
int64_t newBitWidths = SizeInBits(DL, Ty);
cstVal *= smallerBitWidths;
if (cstVal < newBitWidths) {
continue;
}
cstVal /= newBitWidths;
if (dynVal) {
dynVal = CreateMul(Builder, smallerBitWidths / newBitWidths, dynVal);
}
auto NewGEPIdxs =
GetIdxsForTyFromOffset(M.getDataLayout(), Builder, Ty, Ty, cstVal,
dynVal, SizeInBits(DL, Ty), Ptr);
auto gep = GetElementPtrInst::Create(Ty, Ptr, NewGEPIdxs, "",
LoadInst->getIterator());
unsigned PointerOperandNum = BitcastUtils::PointerOperandNum(LoadInst);
LLVM_DEBUG(dbgs() << "\n##runOnImplicitGEP (before load):\nadding: ";
gep->dump());
LLVM_DEBUG(dbgs() << "instead of operand " << PointerOperandNum << ": ";
LoadInst->getPointerOperand()->dump(););
LLVM_DEBUG(dbgs() << "of: "; LoadInst->dump(););
LoadInst->setOperand(PointerOperandNum, gep);
if (initial_gep->getNumUses() == 0) {
initial_gep->eraseFromParent();
}
changed = true;
}
for (auto gep : GEPCastList) {
auto ptr = gep->getPointerOperand();
auto ty = InferType(ptr, M.getContext(), &type_cache);
IRBuilder<> Builder{gep};
int64_t cstVal;
Value *dynVal;
size_t smallerBitWidths;
ExtractOffsetFromGEP(DL, Builder, gep, cstVal, dynVal, smallerBitWidths);
auto new_gep_idxs =
GetIdxsForTyFromOffset(DL, Builder, ty, reworkUnsizedType(DL, ty),
cstVal, dynVal, smallerBitWidths, ptr);
auto new_gep = GetElementPtrInst::Create(ty, ptr, new_gep_idxs, "",
gep->getIterator());
LLVM_DEBUG(dbgs() << "\n##runOnImplicitGEP (gep cast):\nreplacing: ";
gep->dump());
LLVM_DEBUG(dbgs() << "by: "; new_gep->dump(););
gep->replaceAllUsesWith(new_gep);
gep->eraseFromParent();
changed = true;
}
for (auto gep : GEPGVList) {
auto ptr = gep->getPointerOperand();
auto ty = InferType(ptr, M.getContext(), &type_cache);
IRBuilder<> Builder{gep};
int64_t cstVal;
Value *dynVal;
size_t smallerBitWidths;
ExtractOffsetFromGEP(DL, Builder, gep, cstVal, dynVal, smallerBitWidths);
auto new_gep_idxs = GetIdxsForTyFromOffset(DL, Builder, ty, nullptr, cstVal,
dynVal, smallerBitWidths, ptr);
auto new_gep = GetElementPtrInst::Create(ty, ptr, new_gep_idxs, "",
gep->getIterator());
LLVM_DEBUG(dbgs() << "\n##runOnImplicitGEP (from GV):\nreplacing: ";
gep->dump());
LLVM_DEBUG(dbgs() << "by: "; new_gep->dump(););
gep->replaceAllUsesWith(new_gep);
gep->eraseFromParent();
changed = true;
}
return changed;
}
bool clspv::SimplifyPointerBitcastPass::runOnImplicitCasts(Module &M) const {
const DataLayout &DL = M.getDataLayout();
bool changed = false;
DenseMap<Value *, Type *> type_cache;
SmallVector<GetElementPtrInst *, 8> Worklist;
for (auto &F : M) {
for (auto &BB : F) {
for (auto &I : BB) {
Value *source = nullptr;
Type *source_ty = nullptr;
Type *dest_ty = nullptr;
if (!IsImplicitCasts(M, type_cache, I, source, source_ty, dest_ty,
true)) {
continue;
}
if (isa<GetElementPtrInst>(source)) {
if (auto *inst_gep = dyn_cast<GetElementPtrInst>(&I)) {
auto VecSrcTy = dyn_cast<FixedVectorType>(source_ty);
auto VecDstTy = dyn_cast<FixedVectorType>(dest_ty);
// Do not lower implicit cast containing vec3, this would revert
// ThreeElementVectorLoweringPass and ReplacePointerBitcastPass
// should be able to deal with it without issues.
if (!(VecSrcTy && VecDstTy &&
(VecSrcTy->getNumElements() == 3 ||
VecDstTy->getNumElements() == 3))) {
Worklist.emplace_back(inst_gep);
}
}
}
}
}
}
for (auto inst_gep : Worklist) {
GetElementPtrInst *src_gep =
cast<GetElementPtrInst>(inst_gep->getPointerOperand());
IRBuilder<> Builder{inst_gep};
SmallVector<Value *> Idxs;
Value *src;
Type *src_ty;
if (src_gep->hasAllZeroIndices()) {
src_ty = inst_gep->getSourceElementType();
src = src_gep->getPointerOperand();
Idxs = SmallVector<Value *>(inst_gep->indices());
} else {
src_ty = src_gep->getResultElementType();
src = src_gep;
int64_t CstVal;
Value *DynVal;
size_t SmallerBitWidths;
ExtractOffsetFromGEP(DL, Builder, inst_gep, CstVal, DynVal,
SmallerBitWidths);
if (DynVal == nullptr &&
GoThroughTypeAtOffset(DL, Builder, src_ty, src_ty, nullptr,
CstVal * SmallerBitWidths, nullptr) != 0) {
src = src_gep->getPointerOperand();
src_ty = inst_gep->getSourceElementType();
int64_t srcCstVal;
Value *srcDynVal;
size_t srcSmallerBitWidths;
ExtractOffsetFromGEP(DL, Builder, src_gep, srcCstVal, srcDynVal,
srcSmallerBitWidths);
if (SmallerBitWidths < srcSmallerBitWidths) {
srcCstVal *= srcSmallerBitWidths / SmallerBitWidths;
if (srcDynVal) {
srcDynVal = CreateMul(
Builder, srcSmallerBitWidths / SmallerBitWidths, srcDynVal);
}
} else if (SmallerBitWidths > srcSmallerBitWidths) {
CstVal *= SmallerBitWidths / srcSmallerBitWidths;
if (DynVal) {
DynVal = CreateMul(Builder, SmallerBitWidths / srcSmallerBitWidths,
DynVal);
}
}
CstVal += srcCstVal;
if (DynVal && srcDynVal) {
DynVal = Builder.CreateAdd(DynVal, srcDynVal);
} else if (srcDynVal) {
DynVal = srcDynVal;
}
}
Idxs = GetIdxsForTyFromOffset(
DL, Builder, src_ty, inst_gep->getResultElementType(), CstVal, DynVal,
SmallerBitWidths, src_gep->getPointerOperand());
}
auto new_gep = GetElementPtrInst::Create(src_ty, src, Idxs, "",
inst_gep->getIterator());
LLVM_DEBUG(dbgs() << "\n##runOnImplicitCasts:\nreplace: "; inst_gep->dump();
dbgs() << "by: "; new_gep->dump());
inst_gep->replaceAllUsesWith(new_gep);
inst_gep->eraseFromParent();
changed = true;
}
return changed;
}
bool clspv::SimplifyPointerBitcastPass::runOnUpgradeableConstantCasts(
Module &M) const {
const DataLayout &DL = M.getDataLayout();
bool changed = false;
DenseMap<Value *, Type *> type_cache;
struct UpgradeInfo {
Instruction *inst;
int64_t cst;
Value *val;
size_t smallerBitWidth;
Type *dest_ty;
GetElementPtrInst *gep;
};
auto gepIndicesCanBeUpgradedTo = [&DL, &M](Type *ty, GetElementPtrInst *gep,
int64_t &cstVal, Value *&dynVal,
size_t &smallerBitWidths) {
if (gep->hasAllConstantIndices()) {
// should not be used as all indices are constant
IRBuilder<> Builder{gep};
ExtractOffsetFromGEP(DL, Builder, gep, cstVal, dynVal, smallerBitWidths);
assert(dynVal == nullptr);
if (((cstVal * smallerBitWidths) % SizeInBits(DL, ty)) != 0) {
return false;
}
} else {
// if gep contains dynamic indices, only consider i8 gep width and look
// for mul and shl composing the single indice.
if (gep->getSourceElementType() != Type::getInt8Ty(M.getContext()) ||
gep->getNumIndices() != 1) {
return false;
}
cstVal = 0;
smallerBitWidths = CHAR_BIT;
dynVal = gep->getOperand(gep->getNumOperands() - 1);
SmallVector<Value *> vector;
vector.push_back(dynVal);
uint32_t coef = CHAR_BIT;
uint32_t coef_target = SizeInBits(DL, ty);
while (!vector.empty()) {
auto val = vector.back();
vector.pop_back();
if (auto cst = dyn_cast<ConstantInt>(val)) {
coef *= cst->getZExtValue();
continue;
}
auto binary_op = dyn_cast<BinaryOperator>(val);
if (!binary_op) {
continue;
}
switch (binary_op->getOpcode()) {
case Instruction::BinaryOps::Mul:
vector.push_back(binary_op->getOperand(0));
vector.push_back(binary_op->getOperand(1));
break;
case Instruction::BinaryOps::Shl:
vector.push_back(binary_op->getOperand(0));
if (auto cst = dyn_cast<ConstantInt>(binary_op->getOperand(1))) {
coef <<= cst->getZExtValue();
}
break;
default:
continue;
}
}
if ((coef % coef_target) != 0) {
return false;
}
}
return true;
};
SmallVector<UpgradeInfo, 8> Worklist;
SmallVector<UpgradeInfo, 8> GEPsDefiningPHIsWorklist;
DenseSet<GetElementPtrInst *> GEPsDefiningPHISeen;
for (auto &F : M) {
for (auto &BB : F) {
for (auto &I : BB) {
Value *source = nullptr;
Type *source_ty = nullptr;
Type *dest_ty = nullptr;
auto isMemcpy = [](Instruction *I) {
auto memcpy = dyn_cast<CallInst>(I);
if (memcpy == nullptr)
return false;
return memcpy->getCalledFunction()->getIntrinsicID() ==
Intrinsic::memcpy;
};
// memcpy cannot be simplify by this function, skip them to avoid having
// to support them in function like `BitcastUtils::PointerOperandNum`.
if (!IsImplicitCasts(M, type_cache, I, source, source_ty, dest_ty,
true) ||
isMemcpy(&I)) {
continue;
}
if (auto *gep = dyn_cast<GetElementPtrInst>(source)) {
if (SizeInBits(DL, source_ty) >= SizeInBits(DL, dest_ty) ||
IsClspvResourceOrLocal(gep->getPointerOperand())) {
continue;
}
if (IsGVConstantGEP(gep))
continue;
int64_t cstVal;
Value *dynVal;
size_t smallerBitWidths;
auto &context = M.getContext();
auto findBiggerTyToUpdate = [DL, &context, gepIndicesCanBeUpgradedTo,
source_ty, gep, &cstVal, &dynVal,
&smallerBitWidths](Type *dest_ty) {
if (gepIndicesCanBeUpgradedTo(dest_ty, gep, cstVal, dynVal,
smallerBitWidths)) {
return dest_ty;
}
if (!dest_ty->isIntegerTy()) {
return source_ty;
}
size_t dest_ty_size = SizeInBits(DL, dest_ty);
dest_ty_size /= 2;
while (dest_ty_size > SizeInBits(DL, source_ty)) {
dest_ty = Type::getIntNTy(context, dest_ty_size);
if (gepIndicesCanBeUpgradedTo(dest_ty, gep, cstVal, dynVal,
smallerBitWidths)) {
return dest_ty;
}
dest_ty_size /= 2;
}
return source_ty;
};
dest_ty = findBiggerTyToUpdate(dest_ty);
if (dest_ty != source_ty) {
Worklist.push_back(
{&I, cstVal, dynVal, smallerBitWidths, dest_ty, gep});
}
} else if (auto *phi = dyn_cast<PHINode>(source)) {
auto &context = M.getContext();
auto get_geps_defining_phis_type = [phi, source_ty, dest_ty, &context,
&type_cache, &GEPsDefiningPHISeen,
gepIndicesCanBeUpgradedTo]() {
SmallVector<UpgradeInfo> geps;
SmallVector<Value *> values;
SmallVector<PHINode *> phis;
DenseSet<PHINode *> phis_seen;
phis.push_back(phi);
// phi can depend on other phis recursively, go through them to find
// all the values that needing to be upgraded to change the phi
// type.
while (!phis.empty()) {
auto current_phi = phis.back();
phis.pop_back();
if (phis_seen.count(current_phi) != 0) {
continue;
}
phis_seen.insert(current_phi);
for (auto &incoming_value : current_phi->incoming_values()) {
auto val = incoming_value.get();
if (auto phi_node = dyn_cast<PHINode>(val)) {
phis.push_back(phi_node);
} else {
values.push_back(val);
}
}
for (auto user : current_phi->users()) {
if (auto phi_node = dyn_cast<PHINode>(user)) {
phis.push_back(phi_node);
} else {
values.push_back(user);
}
}
}
for (auto value : values) {
auto user_ty = clspv::InferType(value, context, &type_cache);
if (user_ty != source_ty) {
continue;
}
auto gep = dyn_cast<GetElementPtrInst>(value);
int64_t cstVal;
Value *dynVal;
size_t smallerBitWidths;
if (gep == nullptr ||
!gepIndicesCanBeUpgradedTo(dest_ty, gep, cstVal, dynVal,
smallerBitWidths)) {
geps.clear();
return geps;
}
if (GEPsDefiningPHISeen.count(gep) == 0) {
GEPsDefiningPHISeen.insert(gep);
geps.push_back(
{gep, cstVal, dynVal, smallerBitWidths, dest_ty, gep});
}
}
return geps;
};
GEPsDefiningPHIsWorklist.append(get_geps_defining_phis_type());
}
}
}
}
for (auto GEPInfo : GEPsDefiningPHIsWorklist) {
auto gep = dyn_cast<GetElementPtrInst>(GEPInfo.inst);
int64_t cst = GEPInfo.cst;
Value *val = GEPInfo.val;
size_t smallerBitWidths = GEPInfo.smallerBitWidth;
Type *dest_ty = GEPInfo.dest_ty;
Value *ptr = GEPInfo.gep->getPointerOperand();
IRBuilder Builder{gep};
auto NewGEPIdxs =
GetIdxsForTyFromOffset(M.getDataLayout(), Builder, dest_ty, dest_ty,
cst, val, smallerBitWidths, ptr);
auto new_gep = GetElementPtrInst::Create(dest_ty, ptr, NewGEPIdxs, "",
gep->getIterator());
LLVM_DEBUG(dbgs() << "\n##runOnUpgradeableConstantCasts:\nreplace gep "
"defining phi type: ";
gep->dump(); dbgs() << "by: "; new_gep->dump());
gep->replaceAllUsesWith(new_gep);
gep->eraseFromParent();
changed = true;
}
if (changed) {
return changed;
}
DenseSet<Instruction *> ToBeRemoved;
for (auto GEPInfo : Worklist) {
Instruction *I = GEPInfo.inst;
int64_t cst = GEPInfo.cst;
Value *val = GEPInfo.val;
size_t smallerBitWidths = GEPInfo.smallerBitWidth;
Type *dest_ty = GEPInfo.dest_ty;
Value *ptr = GEPInfo.gep->getPointerOperand();
IRBuilder Builder{GEPInfo.gep};
auto NewGEPIdxs =
GetIdxsForTyFromOffset(M.getDataLayout(), Builder, dest_ty, dest_ty,
cst, val, smallerBitWidths, ptr);
auto new_gep = GetElementPtrInst::Create(dest_ty, ptr, NewGEPIdxs, "",
GEPInfo.gep->getIterator());
unsigned PointerOperandNum = BitcastUtils::PointerOperandNum(I);
if (auto phi = dyn_cast<PHINode>(I)) {
for (uint32_t iOp = 0; iOp < phi->getNumOperands(); iOp++) {
if (I->getOperand(iOp) == GEPInfo.gep) {
PointerOperandNum = iOp;
}
}
}
LLVM_DEBUG(dbgs() << "\n##runOnUpgradeableConstantCasts:\nreplace operand "
<< PointerOperandNum << " of: ";
I->dump(); dbgs() << "by: "; new_gep->dump());
auto initial_inst = dyn_cast<Instruction>(I->getOperand(PointerOperandNum));
I->setOperand(PointerOperandNum, new_gep);
if (initial_inst != nullptr) {
ToBeRemoved.insert(initial_inst);
if (initial_inst->getNumUses() > 0) {
initial_inst->replaceAllUsesWith(new_gep);
}
}
changed = true;
}
for (auto *I : ToBeRemoved) {
I->eraseFromParent();
}
return changed;
}
bool clspv::SimplifyPointerBitcastPass::runOnUnneededIndices(Module &M) const {
const DataLayout &DL = M.getDataLayout();
bool changed = false;
DenseMap<Value *, Type *> type_cache;
SmallVector<std::pair<GetElementPtrInst *, Instruction *>> Worklist;
for (auto &F : M) {
for (auto &BB : F) {
for (auto &I : BB) {
Value *source = nullptr;
Type *source_ty = nullptr;
Type *dest_ty = nullptr;
if (!IsImplicitCasts(M, type_cache, I, source, source_ty, dest_ty,
true)) {
continue;
}
if (auto gep = dyn_cast<GetElementPtrInst>(source)) {
if (gep->getNumIndices() <= 1)
continue;
if (IsGVConstantGEP(gep))
continue;
if (SizeInBits(DL, source_ty) < SizeInBits(DL, dest_ty)) {
if (auto cst = dyn_cast<ConstantInt>(
gep->getOperand(gep->getNumOperands() - 1))) {
if (cst->getZExtValue() == 0) {
Worklist.push_back(std::make_pair(gep, &I));
}
}
}
}
}
}
}
for (auto pair : Worklist) {
GetElementPtrInst *gep = pair.first;
Instruction *I = pair.second;
SmallVector<Value *> Indices(gep->indices());
while (Indices.size() > 1) {
auto lastIndice = Indices.back();
if (auto cst = dyn_cast<ConstantInt>(lastIndice)) {
if (cst->getZExtValue() == 0) {
Indices.pop_back();
} else {
break;
}
} else {
break;
}
}
IRBuilder<> Builder{gep};
auto new_gep = GetElementPtrInst::Create(gep->getSourceElementType(),
gep->getPointerOperand(), Indices,
"", gep->getIterator());
LLVM_DEBUG(dbgs() << "\nrunOnUnneededIndices:\nreplace: "; gep->dump();
dbgs() << "by: "; new_gep->dump(); dbgs() << "in: "; I->dump());
I->replaceUsesOfWith(gep, new_gep);
if (gep->getNumUses() == 0) {
gep->eraseFromParent();
}
changed = true;
}
return changed;
}
bool clspv::SimplifyPointerBitcastPass::runOnPHIFromGEP(Module &M) const {
const DataLayout &DL = M.getDataLayout();
bool changed = false;
DenseMap<Value *, Type *> type_cache;
struct ImplicitGEPAliasing {
PHINode *phi;
int steps;
GetElementPtrInst *gep;
};
DenseSet<GetElementPtrInst *> Seen;
SmallVector<ImplicitGEPAliasing> GEPAliasingList;
SmallVector<std::pair<GetElementPtrInst *, Type *>> Worklist;
for (auto &F : M) {
for (auto &BB : F) {
for (auto &I : BB) {
Value *source = nullptr;
Type *source_ty = nullptr;
Type *dest_ty = nullptr;
if (!IsImplicitCasts(M, type_cache, I, source, source_ty, dest_ty,
true)) {
continue;
}
if (auto gep = dyn_cast<GetElementPtrInst>(source)) {
if (Seen.contains(gep)) {
continue;
}
if (auto phi = dyn_cast<PHINode>(&I)) {
int Steps;
bool PerfectMatch;
if (FindAliasingContainedType(source_ty, dest_ty, Steps,
PerfectMatch, DL) &&
PerfectMatch) {
GEPAliasingList.push_back({phi, Steps, gep});
} else if (!FindAliasingContainedType(dest_ty, source_ty, Steps,
PerfectMatch, DL)) {
Worklist.emplace_back(std::make_pair(gep, dest_ty));
}
Seen.insert(gep);
}
}
}
}
}
for (auto pair : Worklist) {
GetElementPtrInst *gep = pair.first;
Type *Ty = pair.second;
IRBuilder<> Builder{gep};
int64_t CstVal;
Value *DynVal;
size_t SmallerBitWidths;
ExtractOffsetFromGEP(DL, Builder, gep, CstVal, DynVal, SmallerBitWidths);
auto Idxs =
GetIdxsForTyFromOffset(DL, Builder, Ty, nullptr, CstVal, DynVal,
SmallerBitWidths, gep->getPointerOperand());
auto new_gep = GetElementPtrInst::Create(Ty, gep->getPointerOperand(), Idxs,
"", gep->getIterator());
LLVM_DEBUG(dbgs() << "\n##runOnPHIFromGEP:\nreplace: "; gep->dump();
dbgs() << "by: "; new_gep->dump());
gep->replaceAllUsesWith(new_gep);
gep->eraseFromParent();
changed = true;
}
for (auto GEPInfo : GEPAliasingList) {
auto *phi = GEPInfo.phi;
auto Steps = GEPInfo.steps;
auto *gep = GEPInfo.gep;
IRBuilder<> B(gep);
SmallVector<Value *> Idxs;
for (int i = 0; i < Steps + 1; i++) {
Idxs.push_back(B.getInt32(0));
}
auto new_gep =
GetElementPtrInst::Create(gep->getResultElementType(), gep, Idxs, "",
gep->getNextNode()->getIterator());
for (unsigned i = 0; i < phi->getNumIncomingValues(); i++) {
if (phi->getIncomingValue(i) != gep) {
continue;
}
changed = true;
LLVM_DEBUG(dbgs() << "\n##runOnPHIFromGEPAliasing:\nreplace: ";
gep->dump(); dbgs() << "by: "; new_gep->dump();
dbgs() << "in: "; phi->dump());
phi->setIncomingValue(i, new_gep);
}
}
return changed;
}
bool clspv::SimplifyPointerBitcastPass::runOnAllocaNotAliasing(
Module &M) const {
const DataLayout &DL = M.getDataLayout();
DenseMap<Value *, Type *> type_cache;
DenseMap<AllocaInst *, Type *> Allocas;
for (auto &F : M) {
for (auto &BB : F) {
for (auto &I : BB) {
Value *source = nullptr;
Type *source_ty = nullptr;
Type *dest_ty = nullptr;
if (!IsImplicitCasts(M, type_cache, I, source, source_ty, dest_ty,
true)) {
continue;
}
auto alloca = dyn_cast<AllocaInst>(source);
auto gep = dyn_cast<GetElementPtrInst>(&I);
if (!alloca || !gep || (gep && gep->getNumUses() == 0)) {
continue;
}
int Steps;
bool PerfectMatch;
dest_ty = gep->getResultElementType();
Type *source_ele_ty = GetEleType(source_ty);
while (source_ele_ty != source_ty) {
source_ty = source_ele_ty;
source_ele_ty = GetEleType(source_ty);
}
if (FindAliasingContainedType(source_ty, dest_ty, Steps, PerfectMatch,
DL) ||
SizeInBits(DL, dest_ty) >= SizeInBits(DL, source_ty)) {
continue;
}
if (Allocas.count(alloca) == 0) {
Allocas.insert(std::make_pair(cast<AllocaInst>(source), dest_ty));
} else if (SizeInBits(DL, dest_ty) < SizeInBits(DL, Allocas[alloca])) {
Allocas[alloca] = dest_ty;
}
}
}
}
for (auto &pair : Allocas) {
auto *alloca = pair.first;
auto *Ty = pair.second;
IRBuilder<> B(alloca);
auto nb_elem =
alloca->getAllocationSizeInBits(DL).value() / SizeInBits(DL, Ty);
if (nb_elem > 1) {
Ty = ArrayType::get(Ty, nb_elem);
}
auto new_alloca = B.CreateAlloca(Ty, alloca->getAddressSpace());
LLVM_DEBUG(dbgs() << "\n##runOnAllocaNotAliasing:\nreplace: ";
alloca->dump(); dbgs() << "by: "; new_alloca->dump());
alloca->replaceAllUsesWith(new_alloca);
alloca->eraseFromParent();
}
return Allocas.size() != 0;
}