| // 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 "ArgKind.h" |
| #include "NormalizeGlobalVariable.h" |
| #include "Passes.h" |
| |
| using namespace llvm; |
| |
| #define DEBUG_TYPE "clusterconstants" |
| |
| namespace { |
| struct ClusterModuleScopeConstantVars : public ModulePass { |
| static char ID; |
| ClusterModuleScopeConstantVars() : ModulePass(ID) {} |
| |
| bool runOnModule(Module &M) override; |
| }; |
| |
| } // namespace |
| |
| char ClusterModuleScopeConstantVars::ID = 0; |
| INITIALIZE_PASS(ClusterModuleScopeConstantVars, |
| "ClusterModuleScopeConstantVars", |
| "Cluster module-scope __constant variables", false, false) |
| |
| namespace clspv { |
| llvm::ModulePass *createClusterModuleScopeConstantVars() { |
| return new ClusterModuleScopeConstantVars(); |
| } |
| } // namespace clspv |
| |
| bool ClusterModuleScopeConstantVars::runOnModule(Module &M) { |
| bool Changed = false; |
| LLVMContext &Context = M.getContext(); |
| |
| clspv::NormalizeGlobalVariables(M); |
| |
| SmallVector<GlobalVariable *, 8> global_constants; |
| UniqueVector<Constant *> initializers; |
| SmallVector<GlobalVariable *, 8> dead_global_constants; |
| 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()); |
| } |
| } |
| } |
| |
| for (GlobalVariable *GV : dead_global_constants) { |
| Changed = true; |
| GV->eraseFromParent(); |
| } |
| |
| if (global_constants.size() > 1 || |
| (global_constants.size() == 1 && |
| !global_constants[0]->getType()->isStructTy())) { |
| |
| Changed = true; |
| |
| // Make the struct type. |
| SmallVector<Type *, 8> types; |
| types.reserve(initializers.size()); |
| for (Value *init : initializers) { |
| Type *ty = init->getType(); |
| types.push_back(ty); |
| } |
| StructType *type = StructType::get(Context, types); |
| |
| // Make the global variable. |
| SmallVector<Constant *, 8> initializers_as_vec(initializers.begin(), |
| initializers.end()); |
| Constant *clustered_initializer = |
| ConstantStruct::get(type, initializers_as_vec); |
| GlobalVariable *clustered_gv = new GlobalVariable( |
| M, type, true, GlobalValue::InternalLinkage, clustered_initializer, |
| "clspv.clustered_constants", nullptr, |
| GlobalValue::ThreadLocalMode::NotThreadLocal, |
| clspv::AddressSpace::Constant); |
| assert(clustered_gv); |
| |
| // 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; |
| Instruction *gep = GetElementPtrInst::CreateInBounds( |
| clustered_gv, {zero, Builder.getInt32(index)}, "", inst); |
| 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 Changed; |
| } |