blob: 77a9328e341ee821522e026e65f420d8ae3caf5c [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you 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 "codegen/codegen-callgraph.h"
#include <llvm/IR/Constants.h>
#include <llvm/IR/Function.h>
#include <llvm/IR/GlobalVariable.h>
#include "runtime/lib-cache.h"
#include "common/names.h"
using namespace strings;
namespace impala {
bool CodegenCallGraph::IsDefinedInImpalad(const string& fn_name) {
void* fn_ptr = nullptr;
// Looking up fn in process so mtime is set to -1 (no versioning issue).
Status status =
LibCache::instance()->GetSoFunctionPtr("", fn_name, -1, &fn_ptr, nullptr, true);
return status.ok();
}
void CodegenCallGraph::FindGlobalUsers(
llvm::User* val, vector<llvm::GlobalObject*>* users) {
for (llvm::Use& u: val->uses()) {
llvm::User* user = u.getUser();
if (llvm::isa<llvm::Instruction>(user)) {
llvm::Instruction* inst = llvm::dyn_cast<llvm::Instruction>(u.getUser());
users->push_back(inst->getFunction());
} else if (llvm::isa<llvm::GlobalVariable>(user)) {
llvm::GlobalVariable* gv = llvm::cast<llvm::GlobalVariable>(user);
string val_name = gv->getName();
// We strip global ctors and dtors out of the modules as they are not run.
if (val_name.find("llvm.global_ctors") == string::npos &&
val_name.find("llvm.global_dtors") == string::npos) {
users->push_back(gv);;
}
} else if (llvm::isa<llvm::Constant>(user)) {
FindGlobalUsers(user, users);
} else {
DCHECK(false) << "Unknown user's types for " << val->getName().str();
}
}
}
void CodegenCallGraph::Init(llvm::Module* module) {
DCHECK(!inited_);
// Create a mapping of functions to their referenced functions.
for (llvm::Function& fn : module->functions()) {
if (fn.isIntrinsic() || fn.isDeclaration()) continue;
string fn_name = fn.getName();
// Create an entry for a function if it doesn't exist already.
// This creates entries for functions which don't have any callee.
if (call_graph_.find(fn_name) == call_graph_.end()) {
call_graph_.emplace(fn_name, unordered_set<string>());
}
vector<llvm::GlobalObject*> users;
FindGlobalUsers(&fn, &users);
for (llvm::GlobalValue* val : users) {
const string& caller_name = val->getName();
DCHECK(llvm::isa<llvm::GlobalVariable>(val) || llvm::isa<llvm::Function>(val));
// 'call_graph_' contains functions which need to be materialized when a certain
// IR Function is materialized. We choose to include functions referenced by
// another IR function in the map even if it's defined in Impalad binary so it
// can be inlined for further optimization. This is not applicable for functions
// referenced by global variables only.
if (llvm::isa<llvm::GlobalVariable>(val)) {
if (IsDefinedInImpalad(fn_name)) continue;
fns_referenced_by_gv_.insert(fn_name);
} else {
// There may not be an entry for 'caller_name' yet. Create an entry if needed.
call_graph_[caller_name].insert(fn_name);
}
}
}
inited_ = true;
}
}