blob: c9cde68d7e0b7c3c41adc5be296c235c2cef8d0b [file] [log] [blame]
// 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.
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/UniqueVector.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/Module.h"
#include "llvm/Pass.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/raw_ostream.h"
#include "clspv/AddressSpace.h"
#include "clspv/Option.h"
#include "ArgKind.h"
#include "Passes.h"
using namespace llvm;
namespace {
cl::opt<bool> ShowSMSV("show-smsv", cl::init(false), cl::Hidden,
cl::desc("Show share module scope variables details"));
class ShareModuleScopeVariablesPass final : public ModulePass {
public:
typedef DenseMap<Function *, UniqueVector<Function *>> EntryPointMap;
static char ID;
ShareModuleScopeVariablesPass() : ModulePass(ID) {}
bool runOnModule(Module &M) override;
private:
// Maps functions to entry points that can call them including themselves.
void MapEntryPoints(Module &M);
// Traces the callable functions from |function| and maps them to
// |entry_point|.
void TraceFunction(Function *function, Function *entry_point);
// Attempts to share module scope variables. Returns true if any variables are
// shared. Shares variables of the same type that are used by
// non-intersecting sets of kernels.
bool ShareModuleScopeVariables(Module &M);
// Collects the entry points that can reach |value| into |user_entry_points|.
void CollectUserEntryPoints(Value *value,
UniqueVector<Function *> *user_entry_points);
// Returns true if there is an intersection between the |user_functions| and
// |other_entry_points|.
bool HasSharedEntryPoints(const DenseSet<Function *> &user_functions,
const UniqueVector<Function *> &other_entry_points);
EntryPointMap function_to_entry_points_;
};
} // namespace
char ShareModuleScopeVariablesPass::ID = 0;
INITIALIZE_PASS(ShareModuleScopeVariablesPass, "ShareModuleScopeVariablesPass",
"Share module scope variables", false, false)
namespace clspv {
ModulePass *createShareModuleScopeVariablesPass() {
return new ShareModuleScopeVariablesPass();
}
} // namespace clspv
namespace {
bool ShareModuleScopeVariablesPass::runOnModule(Module &M) {
bool Changed = false;
if (clspv::Option::ShareModuleScopeVariables()) {
MapEntryPoints(M);
Changed = ShareModuleScopeVariables(M);
}
return Changed;
}
void ShareModuleScopeVariablesPass::MapEntryPoints(Module &M) {
// TODO: this could be more efficient if it memoized results for non-kernel
// functions.
for (auto &func : M) {
if (func.isDeclaration() ||
func.getCallingConv() != CallingConv::SPIR_KERNEL)
continue;
TraceFunction(&func, &func);
}
}
void ShareModuleScopeVariablesPass::TraceFunction(Function *function,
Function *entry_point) {
function_to_entry_points_[function].insert(entry_point);
for (auto &BB : *function) {
for (auto &I : BB) {
if (auto call = dyn_cast<CallInst>(&I)) {
Function *callee = call->getCalledFunction();
if (!callee->isDeclaration())
TraceFunction(callee, entry_point);
}
}
}
}
bool ShareModuleScopeVariablesPass::ShareModuleScopeVariables(Module &M) {
// Greedily attempts to share global variables.
// TODO: this should be analysis driven to aid direct resource access more
// directly.
bool Changed = false;
DenseMap<Value *, UniqueVector<Function *>> global_entry_points;
for (auto &G : M.globals()) {
if (!clspv::IsLocalPtr(G.getType()))
continue;
auto &entry_points = global_entry_points[&G];
CollectUserEntryPoints(&G, &entry_points);
}
SmallVector<GlobalVariable *, 8> dead_globals;
for (auto global = M.global_begin(); global != M.global_end(); ++global) {
if (!clspv::IsLocalPtr(global->getType()))
continue;
if (global->user_empty())
continue;
auto &entry_points = global_entry_points[&*global];
DenseSet<Function *> user_functions;
for (auto entry_point : entry_points) {
user_functions.insert(entry_point);
}
auto next = global;
++next;
for (; next != M.global_end(); ++next) {
if (global->getType() != next->getType())
continue;
if (next->user_empty())
continue;
auto &other_entry_points = global_entry_points[&*next];
if (!HasSharedEntryPoints(user_functions, other_entry_points)) {
if (ShowSMSV) {
outs() << "SMSV: Combining module scope variables\n"
<< " " << *global << "\n"
<< " " << *next << "\n";
}
next->replaceAllUsesWith(&*global);
// Don't need to update |entry_points| because we won't revisit this
// global variable after the outer loop iteration.
for (auto fn : other_entry_points) {
user_functions.insert(fn);
}
dead_globals.push_back(&*next);
Changed = true;
}
}
}
// Remove the unused variables that were merged.
for (auto GV : dead_globals) {
GV->eraseFromParent();
}
return Changed;
}
void ShareModuleScopeVariablesPass::CollectUserEntryPoints(
Value *value, UniqueVector<Function *> *user_entry_points) {
if (auto I = dyn_cast<Instruction>(value)) {
Function *function = I->getParent()->getParent();
auto &entry_points = function_to_entry_points_[function];
for (auto fn : entry_points) {
user_entry_points->insert(fn);
}
// We're looking for the first use inside a function to identify the entry
// points. Mutations beyond that point do not prevent sharing so we do not
// consider them.
return;
}
for (auto user : value->users()) {
CollectUserEntryPoints(user, user_entry_points);
}
}
bool ShareModuleScopeVariablesPass::HasSharedEntryPoints(
const DenseSet<Function *> &user_functions,
const UniqueVector<Function *> &other_entry_points) {
for (auto fn : other_entry_points) {
if (user_functions.count(fn))
return true;
}
return false;
}
} // namespace