123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243 |
- /*
- * Copyright 2016-2017, The Android Open Source Project
- *
- * 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 "GlobalMergePass.h"
- #include "llvm/IR/Constants.h"
- #include "llvm/IR/DataLayout.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/Debug.h"
- #include "llvm/Support/raw_ostream.h"
- #include "Context.h"
- #include "RSAllocationUtils.h"
- #include <functional>
- #define DEBUG_TYPE "rs2spirv-global-merge"
- using namespace llvm;
- namespace rs2spirv {
- namespace {
- class GlobalMergePass : public ModulePass {
- public:
- static char ID;
- GlobalMergePass(bool CPU = false) : ModulePass(ID), mForCPU(CPU) {}
- const char *getPassName() const override { return "GlobalMergePass"; }
- bool runOnModule(Module &M) override {
- DEBUG(dbgs() << "RS2SPIRVGlobalMergePass\n");
- SmallVector<GlobalVariable *, 8> Globals;
- if (!collectGlobals(M, Globals)) {
- return false; // Module not modified.
- }
- SmallVector<Type *, 8> Tys;
- Tys.reserve(Globals.size());
- Context &RS2SPIRVCtxt = Context::getInstance();
- uint32_t index = 0;
- for (GlobalVariable *GV : Globals) {
- Tys.push_back(GV->getValueType());
- const char *name = GV->getName().data();
- RS2SPIRVCtxt.addExportVarIndex(name, index);
- index++;
- }
- LLVMContext &LLVMCtxt = M.getContext();
- StructType *MergedTy = StructType::create(LLVMCtxt, "struct.__GPUBuffer");
- MergedTy->setBody(Tys, false);
- // Size calculation has to consider data layout
- const DataLayout &DL = M.getDataLayout();
- const uint64_t BufferSize = DL.getTypeAllocSize(MergedTy);
- RS2SPIRVCtxt.setGlobalSize(BufferSize);
- Type *BufferVarTy = mForCPU ? static_cast<Type *>(PointerType::getUnqual(
- Type::getInt8Ty(M.getContext())))
- : static_cast<Type *>(MergedTy);
- GlobalVariable *MergedGV =
- new GlobalVariable(M, BufferVarTy, false, GlobalValue::ExternalLinkage,
- nullptr, "__GPUBlock");
- // For CPU, create a constant struct for initial values, which has each of
- // its fields initialized to the original value of the corresponding global
- // variable.
- // During the script initialization, the driver should copy these initial
- // values to the global buffer.
- if (mForCPU) {
- CreateInitFunction(LLVMCtxt, M, MergedGV, MergedTy, BufferSize, Globals);
- }
- const bool forCPU = mForCPU;
- IntegerType *const Int32Ty = Type::getInt32Ty(LLVMCtxt);
- ConstantInt *const Zero = ConstantInt::get(Int32Ty, 0);
- Value *Idx[] = {Zero, nullptr};
- auto InstMaker = [forCPU, MergedGV, MergedTy,
- &Idx](Instruction *InsertBefore) {
- Value *Base = MergedGV;
- if (forCPU) {
- LoadInst *Load = new LoadInst(MergedGV, "", InsertBefore);
- DEBUG(Load->dump());
- Base = new BitCastInst(Load, PointerType::getUnqual(MergedTy), "",
- InsertBefore);
- DEBUG(Base->dump());
- }
- GetElementPtrInst *GEP = GetElementPtrInst::CreateInBounds(
- MergedTy, Base, Idx, "", InsertBefore);
- DEBUG(GEP->dump());
- return GEP;
- };
- for (size_t i = 0, e = Globals.size(); i != e; ++i) {
- GlobalVariable *G = Globals[i];
- Idx[1] = ConstantInt::get(Int32Ty, i);
- ReplaceAllUsesWithNewInstructions(G, std::cref(InstMaker));
- G->eraseFromParent();
- }
- // Return true, as the pass modifies module.
- return true;
- }
- private:
- // In the User of Value Old, replaces all references of Old with Value New
- static inline void ReplaceUse(User *U, Value *Old, Value *New) {
- for (unsigned i = 0, n = U->getNumOperands(); i < n; ++i) {
- if (U->getOperand(i) == Old) {
- U->getOperandUse(i) = New;
- }
- }
- }
- // Replaces each use of V with new instructions created by
- // funcCreateAndInsert and inserted right before that use. In the cases where
- // the use is not an instruction, but a constant expression, recursively
- // replaces that constant expression with a newly constructed equivalent
- // instruction, before replacing V in that new instruction.
- static inline void ReplaceAllUsesWithNewInstructions(
- Value *V,
- std::function<Instruction *(Instruction *)> funcCreateAndInsert) {
- SmallVector<User *, 8> Users(V->user_begin(), V->user_end());
- for (User *U : Users) {
- if (Instruction *Inst = dyn_cast<Instruction>(U)) {
- DEBUG(dbgs() << "\nBefore replacement:\n");
- DEBUG(Inst->dump());
- DEBUG(dbgs() << "----\n");
- ReplaceUse(U, V, funcCreateAndInsert(Inst));
- DEBUG(Inst->dump());
- } else if (ConstantExpr *CE = dyn_cast<ConstantExpr>(U)) {
- auto InstMaker([CE, V, &funcCreateAndInsert](Instruction *UserOfU) {
- Instruction *Inst = CE->getAsInstruction();
- Inst->insertBefore(UserOfU);
- ReplaceUse(Inst, V, funcCreateAndInsert(Inst));
- DEBUG(Inst->dump());
- return Inst;
- });
- ReplaceAllUsesWithNewInstructions(U, InstMaker);
- } else {
- DEBUG(U->dump());
- llvm_unreachable("Expecting only Instruction or ConstantExpr");
- }
- }
- }
- static inline void
- CreateInitFunction(LLVMContext &LLVMCtxt, Module &M, GlobalVariable *MergedGV,
- StructType *MergedTy, const uint64_t BufferSize,
- const SmallVectorImpl<GlobalVariable *> &Globals) {
- SmallVector<Constant *, 8> Initializers;
- Initializers.reserve(Globals.size());
- for (size_t i = 0, e = Globals.size(); i != e; ++i) {
- GlobalVariable *G = Globals[i];
- Initializers.push_back(G->getInitializer());
- }
- ArrayRef<Constant *> ArrInit(Initializers.begin(), Initializers.end());
- Constant *MergedInitializer = ConstantStruct::get(MergedTy, ArrInit);
- GlobalVariable *MergedInit =
- new GlobalVariable(M, MergedTy, true, GlobalValue::InternalLinkage,
- MergedInitializer, "__GPUBlock0");
- Function *UserInit = M.getFunction("init");
- // If there is no user-defined init() function, make the new global
- // initialization function the init().
- StringRef FName(UserInit ? ".rsov.global_init" : "init");
- Function *Func;
- FunctionType *FTy = FunctionType::get(Type::getVoidTy(LLVMCtxt), false);
- Func = Function::Create(FTy, GlobalValue::ExternalLinkage, FName, &M);
- BasicBlock *Blk = BasicBlock::Create(LLVMCtxt, "entry", Func);
- IRBuilder<> LLVMIRBuilder(Blk);
- LoadInst *Load = LLVMIRBuilder.CreateLoad(MergedGV);
- LLVMIRBuilder.CreateMemCpy(Load, MergedInit, BufferSize, 0);
- LLVMIRBuilder.CreateRetVoid();
- // If there is a user-defined init() function, add a call to the global
- // initialization function in the beginning of that function.
- if (UserInit) {
- BasicBlock &EntryBlk = UserInit->getEntryBlock();
- CallInst::Create(Func, {}, "", &EntryBlk.front());
- }
- }
- bool collectGlobals(Module &M, SmallVectorImpl<GlobalVariable *> &Globals) {
- for (GlobalVariable &GV : M.globals()) {
- assert(!GV.hasComdat() && "global variable has a comdat section");
- assert(!GV.hasSection() && "global variable has a non-default section");
- assert(!GV.isDeclaration() && "global variable is only a declaration");
- assert(!GV.isThreadLocal() && "global variable is thread-local");
- assert(GV.getType()->getAddressSpace() == 0 &&
- "global variable has non-default address space");
- // TODO: Constants accessed by kernels should be handled differently
- if (GV.isConstant()) {
- continue;
- }
- // Global Allocations are handled differently in separate passes
- if (isRSAllocation(GV)) {
- continue;
- }
- Globals.push_back(&GV);
- }
- return !Globals.empty();
- }
- bool mForCPU;
- };
- } // namespace
- char GlobalMergePass::ID = 0;
- ModulePass *createGlobalMergePass(bool CPU) { return new GlobalMergePass(CPU); }
- } // namespace rs2spirv
|