blob: b055b52b90075c6891bddd25c164608e1975f360 [file] [edit]
// Copyright 2022 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 <vector>
#include "llvm/ADT/SmallVector.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/Module.h"
#include "llvm/Pass.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/InstIterator.h"
#include "llvm/IR/InstVisitor.h"
#include "llvm/IR/Intrinsics.h"
#include "llvm/IR/ValueHandle.h"
#include "llvm/Support/Debug.h"
#include "llvm/Transforms/Utils/Cloning.h"
#include "llvm/Transforms/Utils/Local.h"
#include "ArgKind.h"
#include "BitcastUtils.h"
#include "Types.h"
#include "clspv/Option.h"
#include "RewritePackedStructs.h"
using namespace llvm;
#define DEBUG_TYPE "RewritePackedStructs"
namespace {
unsigned getStructAlignment(Type *Ty, const DataLayout *DL) {
unsigned structAlignment = 1;
for (unsigned i = 0; i < Ty->getNumContainedTypes(); i++) {
Type *ElemTy = Ty->getContainedType(i);
if (ElemTy->isAggregateType() || ElemTy->isVectorTy()) {
structAlignment =
std::max(structAlignment, getStructAlignment(ElemTy, DL));
} else {
structAlignment =
std::max(structAlignment, (unsigned)DL->getTypeAllocSize(ElemTy));
}
}
return structAlignment;
}
} // namespace
PreservedAnalyses clspv::RewritePackedStructs::run(Module &M,
ModuleAnalysisManager &) {
PreservedAnalyses PA;
DL = &M.getDataLayout();
for (auto &F : M.functions()) {
if (structsShouldBeLowered(F)) {
runOnFunction(F);
}
}
return PA;
}
bool clspv::RewritePackedStructs::structsShouldBeLowered(Function &F) {
for (Instruction &I : instructions(F)) {
if (auto gep = dyn_cast<GetElementPtrInst>(&I)) {
Type *source_ty = clspv::InferType(gep->getPointerOperand(),
F.getContext(), &type_cache_);
Type *dest_ty = gep->getSourceElementType();
if (auto STy = dyn_cast<StructType>(dest_ty)) {
if (STy->isPacked())
return true;
}
if (source_ty && dest_ty &&
(source_ty->isStructTy() || dest_ty->isStructTy()) &&
source_ty != dest_ty) {
return true;
}
}
}
return false;
}
Type *clspv::RewritePackedStructs::getEquivalentType(Type *Ty) {
auto it = TypeMap.find(Ty);
if (it != TypeMap.end()) {
return it->second;
}
// Recursive implementation, taking advantage of the cache.
auto *EquivalentTy = getEquivalentTypeImpl(Ty);
TypeMap.insert({Ty, EquivalentTy});
if (EquivalentTy) {
LLVM_DEBUG(dbgs() << "Generating equivalent type for " << *Ty << ": "
<< *EquivalentTy << '\n');
}
return EquivalentTy;
}
Type *clspv::RewritePackedStructs::getEquivalentTypeImpl(Type *Ty) {
if (!Ty->isPointerTy() && !Ty->isStructTy() && !Ty->isFunctionTy()) {
// No rewriting required.
return nullptr;
}
if (Ty->isPointerTy()) {
return nullptr;
}
if (auto *StructTy = dyn_cast<StructType>(Ty)) {
if (StructTy->getStructNumElements() == 0)
return nullptr;
LLVMContext &Ctx = StructTy->getContainedType(0)->getContext();
SmallVector<Type *, 16> Types;
bool Packed = StructTy->isPacked();
uint32_t structSize = DL->getTypeAllocSize(StructTy);
uint32_t structAlignment = getStructAlignment(StructTy, DL);
ArrayType *ArrayTy = ArrayType::get(Type::getInt8Ty(Ctx), structSize);
if (Packed && structSize % structAlignment != 0) {
return StructType::get(Ctx, {ArrayTy}, Packed);
} else {
return ArrayTy;
}
}
if (auto *FunctionTy = dyn_cast<FunctionType>(Ty)) {
if (FunctionTy->isVarArg()) {
llvm_unreachable("varargs not supported");
}
bool RequireRewriting = false;
// Convert parameter types.
SmallVector<Type *, 16> EquivalentParamTys;
EquivalentParamTys.reserve(FunctionTy->getNumParams());
for (auto *ParamTy : FunctionTy->params()) {
auto *EquivalentParamTy = getEquivalentTypeOrSelf(ParamTy);
EquivalentParamTys.push_back(EquivalentParamTy);
RequireRewriting |= (EquivalentParamTy != ParamTy);
}
if (RequireRewriting) {
return FunctionType::get(FunctionTy->getReturnType(), EquivalentParamTys,
FunctionTy->isVarArg());
} else {
return nullptr;
}
}
#ifndef NDEBUG
dbgs() << "Unsupported type: " << *Ty << '\n';
#endif
llvm_unreachable("Unsupported kind of Type.");
}
bool clspv::RewritePackedStructs::runOnFunction(Function &F) {
LLVM_DEBUG(dbgs() << "Processing " << F.getName() << '\n');
// Skip declarations.
if (F.isDeclaration()) {
return false;
}
SmallVector<GetElementPtrInst *, 16> WorkList;
for (BasicBlock &BB : F) {
for (Instruction &I : BB) {
if (auto gep = dyn_cast<GetElementPtrInst>(&I)) {
if (gep->getSourceElementType()->isStructTy()) {
WorkList.push_back(gep);
}
}
}
}
bool changed = false;
for (auto gep : WorkList) {
auto EquivalentTy = getEquivalentType(gep->getSourceElementType());
if (EquivalentTy == gep->getSourceElementType()) {
continue;
}
auto NewGEP =
GetElementPtrInst::Create(EquivalentTy, gep->getPointerOperand(),
{gep->getOperand(1)}, "", gep->getIterator());
if (gep->getNumOperands() > 2) {
SmallVector<Value *, 2> Indices;
Indices.push_back(ConstantInt::get(Type::getInt32Ty(F.getContext()), 0));
for (unsigned i = 2; i < gep->getNumOperands(); i++) {
Indices.push_back(gep->getOperand(i));
}
NewGEP = GetElementPtrInst::Create(gep->getSourceElementType(), NewGEP,
Indices, "", gep->getIterator());
}
gep->replaceAllUsesWith(NewGEP);
gep->eraseFromParent();
changed = true;
}
LLVM_DEBUG(dbgs() << "Final version for " << F.getName() << '\n');
LLVM_DEBUG(dbgs() << F << '\n');
return changed;
}