blob: ccf009755e8c8580e622f504beb14cd313ad9a20 [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/ADT/DenseSet.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Operator.h"
#include "llvm/Pass.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Transforms/Utils/Local.h"
#include "BitcastUtils.h"
#include "ReplacePointerBitcastPass.h"
#include "Types.h"
#include "clspv/AddressSpace.h"
#include "clspv/Option.h"
#include <cmath>
using namespace llvm;
using namespace BitcastUtils;
#define DEBUG_TYPE "replacepointerbitcast"
namespace {
// TODO: It should be safe to leave these bitcasts as-is, but replacing them
// anyway helps work around Vulkan driver bugs
cl::opt<bool> ReplacePhysicalPointerBitcasts(
"replace-physical-pointer-bitcasts", cl::init(true), cl::Hidden,
cl::desc("Try to remove pointer bitcasts in physical address spaces"));
} // namespace
using WeakInstructions = SmallVector<WeakTrackingVH, 16>;
namespace {
// 'Val' is expected to be a vector.
// 'Idx' is the index where to extract the subvector, but in the casted type
// coordinate. If null, just extract from the origin of the vector.
// At the end of the function, 'Idx' has been updated with the potential
// remainder of the index to get to the expected element.
Value *ExtractSubVector(IRBuilder<> &Builder, Value *&Idx, Value *Val,
unsigned DstSize) {
LLVM_DEBUG(
fprintf(stderr, "%s: ", __func__); Val->dump();
fprintf(stderr, "\tIdx: ");
if (Idx != NULL) { Idx->dump(); } else { fprintf(stderr, "nullptr\n"); });
Type *ValueTy = Val->getType();
assert(ValueTy->isVectorTy() && GetNumEle(ValueTy) == 4);
if (Idx == NULL) {
Val = Builder.CreateShuffleVector(Val, {0, 1});
} else {
// Compute with subvector to keep ({0, 1} or {2, 3}) and update Idx.
unsigned SrcSize = SizeInBits(Builder, ValueTy);
assert((SrcSize / 2) % DstSize == 0);
unsigned NumDstInHalfSrc = SrcSize / (2 * DstSize);
auto ValIdx = CreateDiv(Builder, NumDstInHalfSrc, Idx);
Idx = CreateRem(Builder, NumDstInHalfSrc, Idx);
// Select the appropriate subvector
Value *Val0 = Builder.CreateShuffleVector(Val, {0, 1});
Value *Val1 = Builder.CreateShuffleVector(Val, {2, 3});
Value *Cmp = Builder.CreateICmpEQ(ValIdx, GetIndexTyConst(Builder, 0));
Val = Builder.CreateSelect(Cmp, Val0, Val1);
}
return Val;
}
// 'Values' is expected to contain either vectors or scalars.
// At the end of the function, 'Idx' has been updated with the potential
// remainder of the index to get to the expected element.
// Return the sub element into the first element of 'Values'.
void ExtractSubElementUntilEleSizeLE(Type *Ty, IRBuilder<> &Builder,
SmallVector<Value *, 8> &Values,
Value *&Idx) {
Type *ValueTy = Values[0]->getType();
unsigned SrcSize = SizeInBits(Builder, ValueTy);
unsigned SrcEleSize = SizeInBits(Builder, GetEleType(ValueTy));
unsigned SrcNumEle = GetNumEle(ValueTy);
unsigned DstSize = SizeInBits(Builder, Ty);
unsigned DstEleSize = SizeInBits(Builder, GetEleType(Ty));
while (SrcEleSize > DstSize) {
if (!ValueTy->isVectorTy()) {
// ValueTy: i32 - Ty: i8
// i32 -> <4 x i8>
assert(SrcSize % DstSize == 0);
BitcastIntoVector(Builder, Values,
std::min(SrcSize / DstEleSize, (unsigned)4), Ty);
} else {
// ValueTy->isVectorTy()
if (SrcNumEle == 2) {
// <2 x i32> -> <4 x i16>
BitcastIntoVector(Builder, Values, 4, Ty);
} else if (SrcNumEle == 4) {
// <4 x i32> -> {<2 x i32>, <2 x i32>}[Idx] -> <2 x i32>
Values[0] = ExtractSubVector(Builder, Idx, Values[0], DstSize);
} else {
llvm_unreachable("ExtractSubElement internal error");
}
}
ValueTy = Values[0]->getType();
SrcNumEle = GetNumEle(ValueTy);
SrcEleSize = SizeInBits(Builder, GetEleType(ValueTy));
SrcSize = SizeInBits(Builder, ValueTy);
}
}
// 'Val' is expected to be a vector.
// At the end of the function, 'Idx' has been updated with the potential
// remainder of the index to get to the expected element.
Value *ExtractElementOrSubVector(Type *Ty, IRBuilder<> &Builder, Value *Val,
Value *&Idx) {
Type *ValueTy = Val->getType();
unsigned DstSize = SizeInBits(Builder, Ty);
unsigned SrcEleSize = SizeInBits(Builder, GetEleType(ValueTy));
assert(DstSize % SrcEleSize == 0);
unsigned NumElements = DstSize / SrcEleSize;
assert(NumElements <= 4);
if (NumElements == 1) {
// ValueTy: <4 x i32> - Ty: <2 x i16>
// <4 x i32> -> <4 x i32>[Idx] -> i32
assert(SrcEleSize == DstSize);
return Builder.CreateExtractElement(Val, Idx ? Idx : Builder.getInt32(0));
} else if (NumElements == 2) {
// ValueTy: <4 x i32> - Ty: <4 x i16>
// <4 x i32> -> {<2 x i32>, <2 x i32>}[Idx] -> <2 x i32>
return ExtractSubVector(Builder, Idx, Val, DstSize);
}
return Val;
}
// 'Values' is expected to contain only 1 element.
// This element should either be a vector or a scalar.
// Return the sub element of type 'Ty' into the first element of 'Values'.
void ExtractSubElement(Type *Ty, IRBuilder<> &Builder, Value *Idx,
SmallVector<Value *, 8> &Values) {
LLVM_DEBUG(
fprintf(stderr, "%s:", __func__); Ty->dump(); fprintf(stderr, "\tSrc: ");
Values[0]->dump(); fprintf(stderr, "\tIdx: ");
if (Idx != NULL) { Idx->dump(); } else { fprintf(stderr, "nullptr\n"); });
assert(Values.size() == 1);
Type *ValueTy = Values[0]->getType();
if (Ty == ValueTy) {
return;
}
// Consider only the index for the size that has been loaded (the rest have
// already been considered during the load).
if (Idx != NULL) {
unsigned SrcSize = SizeInBits(Builder, ValueTy);
unsigned DstSize = SizeInBits(Builder, Ty);
assert(SrcSize % DstSize == 0);
Idx = CreateRem(Builder, SrcSize / DstSize, Idx);
}
// Reduce Src until SrcEleSize is smaller or equal to Ty.
ExtractSubElementUntilEleSizeLE(Ty, Builder, Values, Idx);
assert(Values[0]->getType()->isVectorTy());
// extract proper element(s)
Values[0] = ExtractElementOrSubVector(Ty, Builder, Values[0], Idx);
assert(SizeInBits(Builder, Values[0]->getType()) == SizeInBits(Builder, Ty));
// Convert into 'Ty'
ConvertInto(Ty, Builder, Values);
}
// Reduce SrcTy to do as few load/store operations as possible while not loading
// unneeded data.
// Return the appropriate AddIdxs that will need to be used in 'OutAddrIdxs'.
void ReduceType(IRBuilder<> &Builder, bool IsGEPUser, Value *OrgGEPIdx,
Type *&SrcTy, unsigned DstTyBitWidth,
SmallVector<Value *, 4> &InAddrIdxs,
SmallVector<Value *, 4> &OutAddrIdxs,
WeakInstructions &ToBeDeleted) {
Type *SrcEleTy = GetEleType(SrcTy);
unsigned SrcTyBitWidth = SizeInBits(Builder, SrcTy);
unsigned SrcEleTyBitWidth = SizeInBits(Builder, SrcEleTy);
unsigned InIdx = 0;
if (!IsGEPUser) {
while (true) {
OutAddrIdxs.push_back(Builder.getInt32(0));
if ((SrcTy != GetEleType(SrcTy)) && SrcTyBitWidth > DstTyBitWidth &&
SrcEleTyBitWidth >= DstTyBitWidth) {
SrcTy = GetEleType(SrcTy);
SrcTyBitWidth = SrcEleTyBitWidth;
SrcEleTy = GetEleType(SrcTy);
SrcEleTyBitWidth = SizeInBits(Builder, SrcEleTy);
} else {
break;
}
}
} else {
if (SrcTyBitWidth == DstTyBitWidth && OrgGEPIdx) {
OutAddrIdxs.push_back(OrgGEPIdx);
} else {
OutAddrIdxs.push_back(InAddrIdxs[InIdx++]);
while ((SrcTy != GetEleType(SrcTy)) && SrcTyBitWidth > DstTyBitWidth &&
InAddrIdxs.size() > InIdx) {
SrcTy = GetEleType(SrcTy);
SrcTyBitWidth = SrcEleTyBitWidth;
SrcEleTy = GetEleType(SrcTy);
SrcEleTyBitWidth = SizeInBits(Builder, SrcEleTy);
OutAddrIdxs.push_back(InAddrIdxs[InIdx++]);
}
}
}
// Make sure we will delete all unused addridxs.
for (; InIdx < InAddrIdxs.size(); InIdx++) {
ToBeDeleted.push_back(InAddrIdxs[InIdx]);
}
}
unsigned CalculateNumIter(unsigned SrcTyBitWidth, unsigned DstTyBitWidth) {
unsigned NumIter = 1;
if (SrcTyBitWidth < DstTyBitWidth) {
NumIter = (SrcTyBitWidth - 1 + DstTyBitWidth) / SrcTyBitWidth;
}
return NumIter;
}
Value *ComputeLoad(IRBuilder<> &Builder, Value *OrgGEPIdx, bool IsGEPUser,
Value *Src, Type *SrcTy, Type *DstTy,
SmallVector<Value *, 4> &NewAddrIdxs,
WeakInstructions &ToBeDeleted) {
bool isPackedStructSrc = false;
if (auto StructTy = dyn_cast<StructType>(SrcTy)) {
isPackedStructSrc = StructTy->isPacked();
}
Type *DstEleTy = GetEleType(DstTy);
unsigned DstTyBitWidth = SizeInBits(Builder, DstTy);
unsigned DstEleTyBitWidth = SizeInBits(Builder, DstEleTy);
Type *OrigSrcTy = SrcTy;
SmallVector<Value *, 4> AddrIdxs;
ReduceType(Builder, IsGEPUser, OrgGEPIdx, SrcTy, DstTyBitWidth, NewAddrIdxs,
AddrIdxs, ToBeDeleted);
Type *SrcEleTy = GetEleType(SrcTy);
unsigned SrcTyBitWidth = SizeInBits(Builder, SrcTy);
unsigned SrcEleTyBitWidth = SizeInBits(Builder, SrcEleTy);
// Load the values
SmallVector<Value *, 8> LDValues;
for (unsigned i = 0; i < CalculateNumIter(SrcTyBitWidth, DstTyBitWidth);
i++) {
if (i > 0) {
Value *LastAddrIdx = AddrIdxs.pop_back_val();
auto *IndexTy = GetIndexTy(Builder);
if (LastAddrIdx->getType() != IndexTy)
LastAddrIdx = Builder.CreateZExt(LastAddrIdx, IndexTy);
LastAddrIdx = Builder.CreateAdd(LastAddrIdx, GetIndexTyConst(Builder, 1));
AddrIdxs.push_back(LastAddrIdx);
}
auto *SrcAddr = Builder.CreateGEP(OrigSrcTy, Src, AddrIdxs);
Type *LoadTy = GetElementPtrInst::getIndexedType(OrigSrcTy, AddrIdxs);
LoadInst *SrcVal = Builder.CreateLoad(LoadTy, SrcAddr);
LDValues.push_back(SrcVal);
}
// If load values are array, extract scalar elements from them.
if (SrcTy->isArrayTy()) {
// If the main source of the array was from a packed struct, extract values
if (isPackedStructSrc) {
ExtractFromArray(Builder, LDValues, isPackedStructSrc, DstEleTyBitWidth);
} else {
ExtractFromArray(Builder, LDValues);
}
SrcTy = SrcEleTy;
SrcTyBitWidth = SrcEleTyBitWidth;
}
// If the output is a vec3 let's consider that the output is a vec4.
bool IsVec3 = DstTy->isVectorTy() && GetNumEle(DstTy) == 3;
// Because the vec3 lowering pass is run before this one, we should not have a
// vec3 src; however, it seems that some llvm passes after vec3 lowering can
// produce a new vec3. At the moment the only case known is to produce a vec3
// that will be bitcast to another vec3 whose elements have the same size as
// the src vec3. In that particular case, just keep the vec3 as we only need
// to bitcast them, which will be handled correctly by this pass.
IsVec3 &= !(SrcTy->isVectorTy() && GetNumEle(SrcTy) == 3 &&
SrcEleTyBitWidth == DstEleTyBitWidth);
if (IsVec3) {
DstTy = FixedVectorType::get(DstEleTy, 4);
}
if (SrcTyBitWidth > DstTyBitWidth) {
assert(LDValues.size() == 1);
ExtractSubElement(DstTy, Builder, OrgGEPIdx, LDValues);
} else {
ConvertInto(DstTy, Builder, LDValues);
}
// recreate the vec3 from the vec4
if (IsVec3) {
assert(LDValues.size() == 1);
LDValues[0] = Builder.CreateShuffleVector(LDValues[0], {0, 1, 2});
}
return LDValues[0];
}
void ComputeStore(IRBuilder<> &Builder, StoreInst *ST, Value *OrgGEPIdx,
bool IsGEPUser, Value *Src, Type *SrcTy, Type *DstTy,
SmallVector<Value *, 4> &NewAddrIdxs,
WeakInstructions &ToBeDeleted) {
// Careful with srcty and dstty concept in store.
// The usual pattern is:
//
// %bt = bitcast srcty* %src to dsty*
// %gep = gep dstty*, dstty* %bt, %i
// store dstty %stval, dstty* %gep
//
// Which convert to:
//
// %stval_converted = convert dstty %stval into srcty, at f(%i)
// %gep = gep srcty*, srcty* %src, g(%i)
// store srcty %stval_converted, srcty* %gep
//
// Which means that what we need to do is to convert stval from dstty to
// srcty. Thus, while srcty is the source of the bitcast, it is the
// destination/target type of stval.
Type *DstEleTy = GetEleType(DstTy);
unsigned DstTyBitWidth = SizeInBits(Builder, DstTy);
unsigned DstEleTyBitWidth = SizeInBits(Builder, DstEleTy);
Type *OrigSrcTy = SrcTy;
SmallVector<Value *, 4> AddrIdxs;
ReduceType(Builder, IsGEPUser, OrgGEPIdx, SrcTy, DstTyBitWidth, NewAddrIdxs,
AddrIdxs, ToBeDeleted);
Type *SrcEleTy = GetEleType(SrcTy);
unsigned SrcTyBitWidth = SizeInBits(Builder, SrcTy);
unsigned SrcEleTyBitWidth = SizeInBits(Builder, SrcEleTy);
SmallVector<Value *, 8> STValues;
Value *STVal = ST->getValueOperand();
auto cst = dyn_cast<Constant>(STVal);
if (cst && cst->isNullValue()) {
for (uint32_t i = 0;
i < (DstTyBitWidth + SrcTyBitWidth - 1) / SrcTyBitWidth; i++) {
STValues.push_back(Constant::getNullValue(SrcTy));
}
} else {
STValues.push_back(STVal);
}
// If the output is a vec3, let's extract those 3 elements.
bool IsVec3 = DstTy->isVectorTy() && GetNumEle(DstTy) == 3;
// Because the vec3 to vec4 pass is before this one, we should not have a vec3
// src. But it seems that some llvm passes after vec3 to vec4 can produce new
// vec3. At the moment the only case known is to produce vec3 that will be
// bitcast to another vec3 which element has the same time as the src vec3. In
// that particular case, just keep the vec3 as we only need to bitcast them,
// which will be handled correctly by this pass.
IsVec3 &= !(SrcTy->isVectorTy() && GetNumEle(SrcTy) == 3 &&
SrcEleTyBitWidth == DstEleTyBitWidth);
// If cst is null value, STValues already contains SrcTy elements, which do
// not need to be extracted.
IsVec3 &= !(cst && cst->isNullValue());
if (IsVec3) {
ExtractFromVector(Builder, STValues);
DstTy = DstEleTy;
DstTyBitWidth = DstEleTyBitWidth;
}
if (SrcTyBitWidth > DstTyBitWidth) {
if (SrcEleTyBitWidth > DstTyBitWidth) {
// float -> <2 x i8>
// In this example, we cannot store 2 bytes into a object only accessible
// by group of 4.
SrcTy->print(errs());
DstTy->print(errs());
llvm_unreachable("Cannot handle above src/dst types.");
}
// SrcTy: <N x s> - DstTy: <M x d>
// we have: N*s > M*d && s <= M*d
// thus: N > 1, which means that source is either a vector or an array or a
// struct.
assert(SrcTy->isVectorTy() || SrcTy->isArrayTy() || SrcTy->isStructTy());
// SrcTy: <4 x i32> - DstTy: i64
// Let's convert i64 into the element type (i32) as we could not store a
// <2 x i32> into SrcTy.
ConvertInto(SrcEleTy, Builder, STValues);
// Reduce should have given the Idxs to access the vector (or array).
// Because we know we want to access the element here, let's add the
// appropriate Idx to 'AddrIdxs'.
if (IsGEPUser) {
AddrIdxs.push_back(NewAddrIdxs[AddrIdxs.size()]);
} else {
AddrIdxs.push_back(Builder.getInt32(0));
}
} else {
if (DstTy->isArrayTy()) {
ExtractFromArray(Builder, STValues);
}
ConvertInto(SrcTy, Builder, STValues);
}
// Generate stores.
unsigned NumSTElement = STValues.size();
for (unsigned i = 0; i < NumSTElement; i++) {
if (i > 0) {
// Calculate next store address
Value *LastAddrIdx = AddrIdxs.pop_back_val();
auto *IndexTy = GetIndexTy(Builder);
if (LastAddrIdx->getType() != IndexTy)
LastAddrIdx = Builder.CreateZExt(LastAddrIdx, IndexTy);
LastAddrIdx = Builder.CreateAdd(LastAddrIdx, GetIndexTyConst(Builder, 1));
AddrIdxs.push_back(LastAddrIdx);
}
Value *DstAddr = Builder.CreateGEP(OrigSrcTy, Src, AddrIdxs);
Builder.CreateStore(STValues[i], DstAddr);
}
}
void CleanModule(WeakInstructions &ToBeDeleted) {
// Remove all dead instructions, including their dead operands. Proceed with a
// fixed-point algorithm to handle dependencies.
for (bool Progress = true; Progress;) {
std::size_t PreviousSize = ToBeDeleted.size();
WeakInstructions Deads;
WeakInstructions NextBatch;
for (WeakTrackingVH Handle : ToBeDeleted) {
if (!Handle.pointsToAliveValue() || !isa<Instruction>(Handle))
continue;
auto *Inst = cast<Instruction>(Handle);
// We need to remove stores manually given they are never trivially dead.
if (auto *Store = dyn_cast<StoreInst>(Inst)) {
Store->eraseFromParent();
continue;
}
if (isInstructionTriviallyDead(Inst)) {
Deads.push_back(Handle);
} else {
NextBatch.push_back(Handle);
}
}
RecursivelyDeleteTriviallyDeadInstructions(Deads);
ToBeDeleted = std::move(NextBatch);
Progress = (ToBeDeleted.size() < PreviousSize);
}
}
bool DowngradeSourceToTy(const DataLayout &DL, Value *Src, Type *Ty) {
bool changed = false;
while (auto gep = dyn_cast<GetElementPtrInst>(Src)) {
IRBuilder<> B(gep);
int64_t CstVal;
Value *DynVal;
size_t SmallerBitWidths;
ExtractOffsetFromGEP(DL, B, gep, CstVal, DynVal, SmallerBitWidths);
auto Idxs =
GetIdxsForTyFromOffset(DL, B, Ty, Ty, CstVal, DynVal, SmallerBitWidths,
gep->getPointerOperand());
auto *new_gep = GetElementPtrInst::Create(Ty, gep->getPointerOperand(),
Idxs, "", gep->getIterator());
gep->replaceAllUsesWith(new_gep);
gep->eraseFromParent();
Src = new_gep->getPointerOperand();
changed = true;
}
if (auto alloca = dyn_cast<AllocaInst>(Src)) {
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());
alloca->replaceAllUsesWith(new_alloca);
alloca->eraseFromParent();
changed = true;
} else if (auto GV = dyn_cast<GlobalVariable>(Src)) {
auto nb_elem = SizeInBits(DL, GV->getValueType()) / SizeInBits(DL, Ty);
if (nb_elem > 1) {
Ty = ArrayType::get(Ty, nb_elem);
}
if (!isa<StructType>(Ty) &&
GV->getAddressSpace() == clspv::AddressSpace::PushConstant) {
Ty = StructType::get(Ty);
}
auto initializer = GV->getInitializer();
if (initializer && !initializer->isOneValue() &&
!initializer->isNullValue() && !isa<UndefValue>(initializer)) {
// unsupported do nothing...
return changed;
} else if (initializer && initializer->isOneValue()) {
initializer = Constant::getAllOnesValue(Ty);
} else if (initializer && initializer->isNullValue()) {
initializer = Constant::getNullValue(Ty);
} else if (initializer && isa<UndefValue>(initializer)) {
initializer = UndefValue::get(Ty);
}
auto new_GV = new GlobalVariable(
*GV->getParent(), Ty, GV->isConstant(), GV->getLinkage(), initializer,
"", GV, GV->getThreadLocalMode(), GV->getAddressSpace(),
GV->isExternallyInitialized());
new_GV->takeName(GV);
new_GV->setAlignment(GV->getAlign());
new_GV->copyMetadata(GV, /* offset: */ 0);
new_GV->copyAttributesFrom(GV);
GV->replaceAllUsesWith(new_GV);
GV->eraseFromParent();
changed = true;
} else if (auto Arg = dyn_cast<Argument>(Src)) {
SmallVector<User *, 16> UserWorkList;
auto TySize = SizeInBits(DL, Ty);
for (auto *U : Arg->users()) {
UserWorkList.push_back(U);
}
while (!UserWorkList.empty()) {
auto *user = UserWorkList.back();
UserWorkList.pop_back();
auto gep = dyn_cast<GetElementPtrInst>(user);
if (gep && TySize < SizeInBits(DL, gep->getSourceElementType())) {
for (auto *U : user->users()) {
UserWorkList.push_back(U);
}
IRBuilder<> B(gep);
int64_t CstVal;
Value *DynVal;
size_t SmallerBitWidths;
Type *RetTy = Ty;
if (TySize > SizeInBits(DL, gep->getResultElementType())) {
RetTy = gep->getResultElementType();
}
ExtractOffsetFromGEP(DL, B, gep, CstVal, DynVal, SmallerBitWidths);
auto Idxs =
GetIdxsForTyFromOffset(DL, B, Ty, RetTy, CstVal, DynVal,
SmallerBitWidths, gep->getPointerOperand());
auto *new_gep = GetElementPtrInst::Create(Ty, gep->getPointerOperand(),
Idxs, "", gep->getIterator());
gep->replaceAllUsesWith(new_gep);
gep->eraseFromParent();
changed = true;
}
}
}
return changed;
}
bool DowngradeModule(Module &M) {
DenseMap<Value *, Type *> type_cache;
const DataLayout &DL = M.getDataLayout();
// Downgrade object type when detecting implicit cast with inner source type
// bigger than destination type in 2 cases:
// - storing => avoid trying to store an element smaller that the object type
// (not supported later on).
// - complex structures (whatever is done with the ptr casted afterwards) =>
// avoid complex load/store with structures casted into other types where it
// can be hard to reassemble everything to get the proper type/value from the
// structure.
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,
ReplacePhysicalPointerBitcasts)) {
continue;
}
bool isStore = isa<StoreInst>(I);
if (!isStore) {
for (User *U : I.users()) {
if (isa<StoreInst>(U)) {
isStore = true;
}
}
}
if (!isStore && !IsComplexStruct(DL, source_ty)) {
continue;
}
if (auto gep = dyn_cast<GetElementPtrInst>(&I)) {
dest_ty = gep->getResultElementType();
}
Type *EleTy = GetEleType(source_ty);
while (source_ty != EleTy) {
source_ty = EleTy;
EleTy = GetEleType(source_ty);
}
size_t source_size = SizeInBits(DL, source_ty);
size_t dest_size = SizeInBits(DL, dest_ty);
if (source_size > dest_size) {
if (isa<IntToPtrInst>(source)) {
return DowngradeSourceToTy(DL, &I, dest_ty);
} else {
return DowngradeSourceToTy(DL, source, dest_ty);
}
}
}
}
}
return false;
}
} // namespace
PreservedAnalyses
clspv::ReplacePointerBitcastPass::run(Module &M, ModuleAnalysisManager &) {
PreservedAnalyses PA;
DenseMap<Value *, Type *> type_cache;
WeakInstructions ToBeDeleted;
const DataLayout &DL = M.getDataLayout();
bool changed;
do {
changed = DowngradeModule(M);
} while (changed);
DenseSet<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,
ReplacePhysicalPointerBitcasts))
continue;
if (isa<Instruction>(source) &&
WorkList.count(cast<Instruction>(source)) > 0)
continue;
if (IsComplexStruct(DL, source_ty))
continue;
bool ok = true;
SmallVector<User *, 16> UserWorkList;
UserWorkList.push_back(&I);
while (!UserWorkList.empty()) {
auto *user = UserWorkList.back();
UserWorkList.pop_back();
if (isa<GetElementPtrInst>(user)) {
for (auto *U : user->users())
UserWorkList.push_back(U);
} else if (!isa<StoreInst>(user) && !isa<LoadInst>(user)) {
ok = false;
break;
}
}
if (!ok)
continue;
WorkList.insert(&I);
}
}
}
for (Instruction *Inst : WorkList) {
LLVM_DEBUG(dbgs() << "## Inst: "; Inst->dump());
Value *Src = nullptr;
Type *SrcTy = nullptr;
Type *DstTy = nullptr;
if (auto *gep = dyn_cast<GetElementPtrInst>(Inst)) {
Src = gep->getPointerOperand();
DstTy = gep->getSourceElementType();
} else if (auto *ld = dyn_cast<LoadInst>(Inst)) {
Src = ld->getPointerOperand();
DstTy = ld->getType();
} else if (auto *st = dyn_cast<StoreInst>(Inst)) {
Src = st->getPointerOperand();
DstTy = st->getValueOperand()->getType();
} else {
llvm_unreachable("unsupported opaque pointer cast");
}
SrcTy = clspv::InferType(Src, M.getContext(), &type_cache);
SrcTy = BitcastUtils::reworkUnsizedType(DL, SrcTy);
DstTy = BitcastUtils::reworkUnsizedType(DL, DstTy);
SmallVector<Value *, 4> NewAddrIdxs;
// It consist of User* and bool whether user is gep or not.
SmallVector<std::pair<User *, bool>, 32> AllUsers;
Value *OrgGEPIdx = nullptr;
if (auto GEP = dyn_cast<GetElementPtrInst>(Inst)) {
IRBuilder<> Builder(GEP);
int64_t CstVal;
Value *DynVal;
size_t SmallerBitWidths;
ExtractOffsetFromGEP(DL, Builder, GEP, CstVal, DynVal, SmallerBitWidths);
OrgGEPIdx = DynVal;
if (DynVal == nullptr) {
OrgGEPIdx = Builder.getInt32(CstVal);
} else if (CstVal != 0) {
OrgGEPIdx = Builder.CreateAdd(
ConstantInt::get(DynVal->getType(), CstVal), DynVal);
}
DstTy = GEP->getResultElementType();
auto Idx =
GetIdxsForTyFromOffset(DL, Builder, SrcTy, DstTy, CstVal, DynVal,
SmallerBitWidths, GEP->getPointerOperand());
NewAddrIdxs.append(Idx);
// If bitcast's user is gep, investigate gep's users too.
for (User *GEPUser : GEP->users()) {
if (auto GEPUserGEP = dyn_cast<GetElementPtrInst>(GEPUser)) {
if (GEPUserGEP->getSourceElementType() == GEP->getResultElementType())
continue;
}
AllUsers.push_back(std::make_pair(GEPUser, true));
}
if (!GEP->users().empty()) {
ToBeDeleted.push_back(GEP);
}
} else {
AllUsers.push_back(std::make_pair(Inst, false));
}
// Handle users.
bool IsGEPUser = false;
for (auto UserIter : AllUsers) {
User *U = UserIter.first;
IsGEPUser = UserIter.second;
LLVM_DEBUG(dbgs() << "###### User (isGEP: " << IsGEPUser << ") : ";
U->dump());
IRBuilder<> Builder(cast<Instruction>(U));
if (StoreInst *ST = dyn_cast<StoreInst>(U)) {
ComputeStore(Builder, ST,
DstTy == ST->getValueOperand()->getType() ? OrgGEPIdx
: nullptr,
IsGEPUser, Src, SrcTy, ST->getValueOperand()->getType(),
NewAddrIdxs, ToBeDeleted);
} else if (LoadInst *LD = dyn_cast<LoadInst>(U)) {
Value *DstVal = ComputeLoad(Builder, OrgGEPIdx, IsGEPUser, Src, SrcTy,
LD->getType(), NewAddrIdxs, ToBeDeleted);
// Update LD's users with DstVal.
LD->replaceAllUsesWith(DstVal);
} else {
U->print(errs());
llvm_unreachable(
"Handle above user of gep on ReplacePointerBitcastPass");
}
ToBeDeleted.push_back(cast<Instruction>(U));
}
// Schedule for removal only if Inst has no users. If all its users are
// later also replaced in the module, Inst will be remove by transitivity.
if (Inst->user_empty()) {
ToBeDeleted.push_back(Inst);
}
}
CleanModule(ToBeDeleted);
return PA;
}