blob: 26e088bbf4bce27a8a992e82ad895c0309738d7d [file] [edit]
// Copyright 2018 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.
// Cluster module-scope __constant variables. But only if option
// ModuleScopeConstantsInUniformBuffer is true.
#include <cassert>
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/UniqueVector.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/GlobalVariable.h"
#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 "clspv/AddressSpace.h"
#include "clspv/Option.h"
#include "clspv/PushConstant.h"
#include "ArgKind.h"
#include "ClusterConstants.h"
#include "Constants.h"
#include "NormalizeGlobalVariable.h"
#include "PushConstant.h"
using namespace llvm;
#define DEBUG_TYPE "clusterconstants"
PreservedAnalyses
clspv::ClusterModuleScopeConstantVars::run(Module &M, ModuleAnalysisManager &) {
PreservedAnalyses PA;
LLVMContext &Context = M.getContext();
const DataLayout &DL = M.getDataLayout();
clspv::NormalizeGlobalVariables(M);
SmallVector<GlobalVariable *, 8> global_constants;
UniqueVector<Constant *> initializers;
SmallVector<GlobalVariable *, 8> dead_global_constants;
std::map<Constant *, uint64_t> initializers_alignment;
for (GlobalVariable &GV : M.globals()) {
if (GV.hasInitializer() && GV.getType()->getPointerAddressSpace() ==
clspv::AddressSpace::Constant) {
// Only keep live __constant variables.
if (GV.use_empty()) {
dead_global_constants.push_back(&GV);
} else {
global_constants.push_back(&GV);
initializers.insert(GV.getInitializer());
initializers_alignment[GV.getInitializer()] =
GV.getAlign().valueOrOne().value();
}
}
}
for (GlobalVariable *GV : dead_global_constants) {
GV->eraseFromParent();
}
if (global_constants.size() > 1 ||
(global_constants.size() == 1 &&
!global_constants[0]->getType()->isStructTy())) {
// Make the struct type.
SmallVector<Type *, 8> types;
// Make the global variable.
SmallVector<Constant *, 8> initializers_as_vec;
uint64_t max_alignment = 0;
// Make sure that every element is at the proper offset regarding its
// alignment (which might not be the one of its type due to the lowering of
// long vectors).
{
UniqueVector<Constant *> new_initializers;
for (Constant *init : initializers) {
Type *ty = init->getType();
types.push_back(ty);
auto align = initializers_alignment[init];
max_alignment = std::max(max_alignment, align);
auto offset = DL.getStructLayout(StructType::get(Context, types))
->getElementOffset(types.size() - 1);
auto padding = align != 0 ? (align - (offset % align)) % align : 0;
if (padding) {
types.pop_back();
Type *pad_elem_ty;
if (padding % sizeof(uint32_t) == 0) {
pad_elem_ty = ty->getInt32Ty(Context);
} else if (padding % sizeof(uint16_t) == 0) {
pad_elem_ty = ty->getInt16Ty(Context);
} else {
pad_elem_ty = ty->getInt8Ty(Context);
}
padding /= (pad_elem_ty->getScalarSizeInBits() / 8);
ArrayType *pad_ty = ArrayType::get(pad_elem_ty, padding);
SmallVector<Constant *, 4> data;
for (unsigned i = 0; i < padding; i++) {
data.push_back(ConstantInt::get(pad_elem_ty, 0));
}
types.push_back(pad_ty);
types.push_back(ty);
initializers_as_vec.push_back(ConstantArray::get(pad_ty, data));
new_initializers.insert(nullptr);
}
initializers_as_vec.push_back(init);
new_initializers.insert(init);
}
initializers = std::move(new_initializers);
}
StructType *type = StructType::get(Context, types);
PointerType *ptr_type =
PointerType::get(Context, clspv::AddressSpace::Constant);
Constant *clustered_initializer =
ConstantStruct::get(type, initializers_as_vec);
GlobalVariable *clustered_gv = new GlobalVariable(
M, type, true, GlobalValue::InternalLinkage, clustered_initializer,
clspv::ClusteredConstantsVariableName(), nullptr,
GlobalValue::ThreadLocalMode::NotThreadLocal,
clspv::AddressSpace::Constant);
assert(clustered_gv);
clustered_gv->setAlignment(MaybeAlign(max_alignment));
// Replace uses of the other globals with references to the members of the
// clustered constant.
IRBuilder<> Builder(Context);
Value *zero = Builder.getInt32(0);
for (GlobalVariable *GV : global_constants) {
SmallVector<User *, 8> users(GV->users());
for (User *user : users) {
if (GV == user) {
// This is the original global variable declaration. Skip it.
} else if (auto *inst = dyn_cast<Instruction>(user)) {
unsigned index = initializers.idFor(GV->getInitializer()) - 1;
auto getTypeAndPtr = [&clustered_gv, &M, &Builder, &type, &ptr_type,
&zero](Type *&PointeeType, Value *&Ptr,
llvm::Instruction::InstListType::iterator
InsertBefore) {
if (clspv::Option::PhysicalStorageBuffers()) {
auto *bb = InsertBefore->getParent();
auto *clustered_ptr_ty = clspv::GetPushConstantType(
M, clspv::PushConstant::ModuleConstantsPointer);
auto *ptr_to_ptr = clspv::GetPushConstantPointer(
bb, clspv::PushConstant::ModuleConstantsPointer);
Value *indices[] = {zero};
auto *ptr_gep = Builder.CreateInBoundsGEP(clustered_ptr_ty,
ptr_to_ptr, indices);
auto *clustered_ptr_val = new LoadInst(
clustered_ptr_ty, ptr_gep, "", InsertBefore->getIterator());
auto *clustered_ptr = CastInst::Create(
Instruction::CastOps::IntToPtr, clustered_ptr_val, ptr_type,
"", InsertBefore->getIterator());
PointeeType = type;
Ptr = clustered_ptr;
} else {
PointeeType = clustered_gv->getValueType();
Ptr = clustered_gv;
}
};
if (auto phi = dyn_cast<PHINode>(inst)) {
for (unsigned i = 0; i < phi->getNumIncomingValues(); i++) {
if (phi->getIncomingValue(i) != GV) {
continue;
}
auto InsertBefore = phi->getIncomingBlock(i)->getFirstNonPHIIt();
Type *PointeeType;
Value *Ptr;
getTypeAndPtr(PointeeType, Ptr, InsertBefore);
Instruction *gep = GetElementPtrInst::CreateInBounds(
PointeeType, Ptr, {zero, Builder.getInt32(index)}, "",
InsertBefore->getIterator());
phi->setIncomingValue(i, gep);
}
} else {
Type *PointeeType;
Value *Ptr;
getTypeAndPtr(PointeeType, Ptr, inst->getIterator());
Instruction *gep = GetElementPtrInst::CreateInBounds(
PointeeType, Ptr, {zero, Builder.getInt32(index)}, "",
inst->getIterator());
user->replaceUsesOfWith(GV, gep);
}
} else {
errs() << "Don't know how to handle updating user of __constant: "
<< *user << "\n";
llvm_unreachable("Unhandled case replacing a user of __constant");
}
}
}
// Remove the old constants.
for (GlobalVariable *GV : global_constants) {
GV->eraseFromParent();
}
}
return PA;
}