/
redgpu
/
clspv
Обзор
Документация
Войти
/
redgpu
/
clspv
Код
Запросы
0
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
lib/MultiVersionUBOFunctionsPass.cpp
321 строка
10 KB
alan-baker
Update llvm (#627)
28 авг 2020, 05:42
Не верифицирован
28 авг 2020, 05:42
fe6a3df
Код
Авторство
О чём код?
// Copyright 2019 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 <climits> #include <map> #include <set> #include <utility> #include <vector> #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/UniqueVector.h" #include "llvm/IR/CallingConv.h" #include "llvm/IR/Function.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IR/Instructions.h" #include "llvm/Pass.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Transforms/Utils/Cloning.h" #include "clspv/Passes.h" #include "ArgKind.h" #include "Builtins.h" #include "CallGraphOrderedFunctions.h" #include "Constants.h" #include "Passes.h" using namespace llvm; namespace { class MultiVersionUBOFunctionsPass final : public ModulePass { public: static char ID; MultiVersionUBOFunctionsPass() : ModulePass(ID) {} bool runOnModule(Module &M) override; private: // Struct for tracking specialization information. struct ResourceInfo { // The specific argument. Argument *arg; // The resource var base call. CallInst *base; // Series of GEPs that operate on |base|. std::vector<GetElementPtrInst *> indices; }; // Analyzes the call, |user|, to |fn| in terms of its UBO arguments. Returns // true if |user| can be transformed into a specialized function. // // Currently, this function is only successful in analyzing GEP chains to a // resource variable. bool AnalyzeCall(Function *fn, CallInst *user, std::vector<ResourceInfo> *resources); // Inlines |call|. void InlineCallSite(CallInst *call); // Transforms the call to |fn| into a specialized call based on |resources|. // Replaces |call| with a call to the specialized version. void SpecializeCall(Function *fn, CallInst *call, const std::vector<ResourceInfo> &resources, size_t id); // Adds extra arguments to |fn| by rebuilding the entire function. Function *AddExtraArguments(Function *fn, const std::vector<Value *> &extra_args); }; } // namespace char MultiVersionUBOFunctionsPass::ID = 0; INITIALIZE_PASS(MultiVersionUBOFunctionsPass, "MultiVersionUBOFunctionsPass", "Multi-version functions with UBO params", false, false) namespace clspv { ModulePass *createMultiVersionUBOFunctionsPass() { return new MultiVersionUBOFunctionsPass(); } } // namespace clspv bool MultiVersionUBOFunctionsPass::runOnModule(Module &M) { bool changed = false; UniqueVector<Function *> ordered_functions = clspv::CallGraphOrderedFunctions(M); for (auto fn : ordered_functions) { // Kernels don't need modified. if (fn->isDeclaration() || fn->getCallingConv() == CallingConv::SPIR_KERNEL) continue; bool local_changed = false; size_t count = 0; SmallVector<User *, 8> users(fn->users()); for (auto user : users) { if (auto call = dyn_cast<CallInst>(user)) { std::vector<ResourceInfo> resources; if (AnalyzeCall(fn, call, &resources)) { if (!resources.empty()) { local_changed = true; SpecializeCall(fn, call, resources, count++); } } else { local_changed = true; InlineCallSite(call); } } } fn->removeDeadConstantUsers(); if (local_changed) { // All calls to this function were either specialized or inlined. fn->eraseFromParent(); } changed |= local_changed; } return changed; } bool MultiVersionUBOFunctionsPass::AnalyzeCall( Function *fn, CallInst *user, std::vector<ResourceInfo> *resources) { for (auto &arg : fn->args()) { if (clspv::GetArgKind(arg) != clspv::ArgKind::BufferUBO) continue; Value *arg_operand = user->getOperand(arg.getArgNo()); ResourceInfo info; info.arg = &arg; DenseSet<Value *> visited; std::vector<Value *> stack; stack.push_back(arg_operand); while (!stack.empty()) { Value *value = stack.back(); stack.pop_back(); if (!visited.insert(value).second) continue; if (CallInst *call = dyn_cast<CallInst>(value)) { auto &func_info = clspv::Builtins::Lookup(call->getCalledFunction()); if (func_info.getType() == clspv::Builtins::kClspvResource) { info.base = call; } else { // Unknown function call returning a constant pointer requires // inlining. return false; } } else if (auto gep = dyn_cast<GetElementPtrInst>(value)) { info.indices.push_back(gep); stack.push_back(gep->getOperand(0)); } else { // Unhandled instruction requires inlining. return false; } } resources->push_back(std::move(info)); } return true; } void MultiVersionUBOFunctionsPass::InlineCallSite(CallInst *call) { InlineFunctionInfo IFI; InlineFunction(*call, IFI, nullptr, false); } void MultiVersionUBOFunctionsPass::SpecializeCall( Function *fn, CallInst *call, const std::vector<ResourceInfo> &resources, size_t id) { // The basis of the specialization is a clone of |fn|, however, the clone may // need rebuilt in order to receive extra arguments. ValueToValueMapTy remapped; auto *clone = CloneFunction(fn, remapped); std::string name; raw_string_ostream str(name); str << fn->getName() << "_clspv_" << id; clone->setName(str.str()); std::vector<Value *> extra_args; for (auto info : resources) { // Must traverse the GEPs in reverse order to match how the code will be // generated below so that the iterator for the extra arguments is // consistent. for (auto iter = info.indices.rbegin(); iter != info.indices.rend(); ++iter) { // Skip pointer operand. auto *idx = *iter; for (size_t i = 1; i < idx->getNumOperands(); ++i) { Value *operand = idx->getOperand(i); if (!isa<Constant>(operand)) { extra_args.push_back(operand); } } } } if (!extra_args.empty()) { // Need to add extra arguments to this function. clone = AddExtraArguments(clone, extra_args); } auto where = clone->begin()->begin(); while (isa<AllocaInst>(where)) { ++where; } IRBuilder<> builder(&*where); auto new_arg_iter = clone->arg_begin(); for (size_t i = 0; i < fn->arg_size(); ++i) { ++new_arg_iter; } for (auto info : resources) { // Create the resource var function. SmallVector<Value *, 8> operands; for (size_t i = 0; i < info.base->getNumOperands() - 1; ++i) operands.push_back(info.base->getOperand(i)); CallInst *resource_fn = builder.CreateCall(info.base->getCalledFunction(), operands); // Create the chain of GEPs. Traversed in reverse order because we added // them from use to def. Value *ptr = resource_fn; for (auto iter = info.indices.rbegin(); iter != info.indices.rend(); ++iter) { SmallVector<Value *, 8> indices; for (size_t i = 1; i != (*iter)->getNumOperands(); ++i) { Value *operand = (*iter)->getOperand(i); if (isa<Constant>(operand)) { indices.push_back(operand); } else { // Each extra argument is unique so the iterator is "consumed". indices.push_back(&*new_arg_iter); ++new_arg_iter; } } ptr = builder.CreateGEP(ptr, indices); } // Now replace the use of the argument with the result GEP. Value *remapped_arg = remapped.lookup(info.arg); remapped_arg->replaceAllUsesWith(ptr); } // Replace the call with a call to the newly specialized function. SmallVector<Value *, 16> new_args; for (size_t i = 0; i < call->getNumOperands() - 1; ++i) { new_args.push_back(call->getOperand(i)); } for (auto extra : extra_args) { new_args.push_back(extra); } auto *replacement = CallInst::Create(clone, new_args, "", call); call->replaceAllUsesWith(replacement); call->eraseFromParent(); } Function *MultiVersionUBOFunctionsPass::AddExtraArguments( Function *fn, const std::vector<Value *> &extra_args) { // Generate the new function type. SmallVector<Type *, 8> arg_types; for (auto &arg : fn->args()) { arg_types.push_back(arg.getType()); } for (auto v : extra_args) { arg_types.push_back(v->getType()); } FunctionType *new_type = FunctionType::get(fn->getReturnType(), arg_types, fn->isVarArg()); // Insert the new function and copy calling conv, attributes and metadata. auto *module = fn->getParent(); fn->removeFromParent(); auto pair = module->getOrInsertFunction(fn->getName(), new_type, fn->getAttributes()); Function *new_function = cast<Function>(pair.getCallee()); new_function->setCallingConv(fn->getCallingConv()); new_function->copyMetadata(fn, 0); // Move the basic blocks into the new function if (!fn->isDeclaration()) { std::vector<BasicBlock *> blocks; for (auto &BB : *fn) { blocks.push_back(&BB); } for (auto *BB : blocks) { BB->removeFromParent(); BB->insertInto(new_function); } } // Replace arg uses. for (auto old_arg_iter = fn->arg_begin(), new_arg_iter = new_function->arg_begin(); old_arg_iter != fn->arg_end(); ++old_arg_iter, ++new_arg_iter) { old_arg_iter->replaceAllUsesWith(&*new_arg_iter); } // There are no calls to |fn| yet so we don't need to worry about updating // calls. delete fn; return new_function; }