/*
* Copyright 2015, 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 "bcc/Assert.h"
#include "bcc/Renderscript/RSTransforms.h"
#include "bcc/Support/Log.h"
#include "bcinfo/MetadataExtractor.h"
#include <llvm/Pass.h>
#include <llvm/IR/DIBuilder.h>
#include <llvm/IR/Function.h>
#include <llvm/IR/InstIterator.h>
#include <llvm/IR/Instructions.h>
#include <llvm/IR/IRBuilder.h>
#include <llvm/IR/Module.h>
namespace {
const char DEBUG_SOURCE_PATH[] = "/opt/renderscriptdebugger/1";
const char DEBUG_GENERATED_FILE[] = "generated.rs";
const char DEBUG_PROTOTYPE_VAR_NAME[] = "rsDebugOuterForeachT";
const char DEBUG_COMPILE_UNIT_MDNAME[] = "llvm.dbg.cu";
/*
* LLVM pass to attach debug information to the bits of code
* generated by the compiler.
*/
class RSAddDebugInfoPass : public llvm::ModulePass {
public:
// Pass ID
static char ID;
RSAddDebugInfoPass() : ModulePass(ID), kernelTypeMD(nullptr),
sourceFileName(nullptr), emptyExpr(nullptr), abiMetaCU(nullptr),
indexVarType(nullptr) {
}
virtual bool runOnModule(llvm::Module &Module) {
// Gather information about this bcc module.
bcinfo::MetadataExtractor me(&Module);
if (!me.extract()) {
ALOGE("Could not extract metadata from module!");
return false;
}
size_t nForEachKernels = me.getExportForEachSignatureCount();
const char **forEachKernels = me.getExportForEachNameList();
// Set up the debug info builder.
llvm::DIBuilder DebugInfo(Module);
initializeDebugInfo(DebugInfo, Module);
// Attach DI metadata to each generated function.
for (size_t i = 0; i < nForEachKernels; ++i) {
std::string expandedName = forEachKernels[i];
expandedName += ".expand";
if (llvm::Function *kernelFunc = Module.getFunction(expandedName))
attachDebugInfo(DebugInfo, *kernelFunc);
}
DebugInfo.finalize();
cleanupDebugInfo(Module);
return true;
}
private:
// @brief Initialize the debug info generation.
//
// This method does a couple of things:
// * Look up debug metadata for kernel ABI and store it if present.
// * Store a couple of useful pieces of debug metadata in member
// variables so they do not have to be created multiple times.
void initializeDebugInfo(llvm::DIBuilder &DebugInfo,
const llvm::Module &Module) {
llvm::LLVMContext &ctx = Module.getContext();
// Start generating debug information for bcc-generated code.
DebugInfo.createCompileUnit(llvm::dwarf::DW_LANG_C99,
DEBUG_GENERATED_FILE, DEBUG_SOURCE_PATH,
"RS", false, "", 0);
// Pre-generate and save useful pieces of debug metadata.
sourceFileName = DebugInfo.createFile(DEBUG_GENERATED_FILE, DEBUG_SOURCE_PATH);
emptyExpr = DebugInfo.createExpression();
// Lookup compile unit with kernel ABI debug metadata.
llvm::NamedMDNode *mdCompileUnitList =
Module.getNamedMetadata(DEBUG_COMPILE_UNIT_MDNAME);
bccAssert(mdCompileUnitList != nullptr &&
"DebugInfo pass could not find any existing compile units.");
llvm::DIGlobalVariable *kernelPrototypeVarMD = nullptr;
for (llvm::MDNode* CUNode : mdCompileUnitList->operands()) {
if (auto *CU = llvm::dyn_cast<llvm::DICompileUnit>(CUNode)) {
for (llvm::DIGlobalVariable* GV : CU->getGlobalVariables()) {
if (GV->getDisplayName() == DEBUG_PROTOTYPE_VAR_NAME) {
kernelPrototypeVarMD = GV;
abiMetaCU = CU;
break;
}
}
if (kernelPrototypeVarMD != nullptr)
break;
}
}
// Lookup the expanded function interface type metadata.
llvm::MDTuple *kernelPrototypeMD = nullptr;
if (kernelPrototypeVarMD != nullptr) {
// Dig into the metadata to look for function prototype.
llvm::DIDerivedType *DT = nullptr;
DT = llvm::cast<llvm::DIDerivedType>(kernelPrototypeVarMD->getType());
DT = llvm::cast<llvm::DIDerivedType>(DT->getBaseType());
llvm::DISubroutineType *ST = llvm::cast<llvm::DISubroutineType>(DT->getBaseType());
kernelPrototypeMD = llvm::cast<llvm::MDTuple>(ST->getRawTypeArray());
indexVarType = llvm::dyn_cast_or_null<llvm::DIType>(
kernelPrototypeMD->getOperand(2));
}
// Fall back to the function type of void() if there is no proper debug info.
if (kernelPrototypeMD == nullptr)
kernelPrototypeMD = llvm::MDTuple::get(ctx, {nullptr});
// Fall back to unspecified type if we don't have a proper index type.
if (indexVarType == nullptr)
indexVarType = DebugInfo.createBasicType("uint32_t", 32, 32,
llvm::dwarf::DW_ATE_unsigned);
// Capture the expanded kernel type debug info.
kernelTypeMD = DebugInfo.createSubroutineType(kernelPrototypeMD);
}
/// @brief Add debug information to a generated function.
///
/// This procedure adds the following pieces of debug information
/// to the function specified by Func:
/// * Entry for the function to the current compile unit.
/// * Adds debug info entries for each function argument.
/// * Adds debug info entry for the rsIndex local variable.
/// * File/line information to each instruction set to generates.rs:1.
void attachDebugInfo(llvm::DIBuilder &DebugInfo, llvm::Function &Func) {
// Lookup the current thread coordinate variable.
llvm::AllocaInst* indexVar = nullptr;
for (llvm::Instruction &inst : llvm::instructions(Func)) {
if (auto *allocaInst = llvm::dyn_cast<llvm::AllocaInst>(&inst)) {
if (allocaInst->getName() == bcc::BCC_INDEX_VAR_NAME) {
indexVar = allocaInst;
break;
}
}
}
// Create function-level debug metadata.
llvm::DISubprogram *ExpandedFunc = DebugInfo.createFunction(
sourceFileName, // scope
Func.getName(), Func.getName(),
sourceFileName, 1, kernelTypeMD,
false, true, 1, 0, false
);
// IRBuilder for allocating variables for arguments.
llvm::IRBuilder<> ir(&*Func.getEntryBlock().begin());
// Walk through the argument list and expanded function prototype
// debuginfo in lockstep to create debug entries for
// the expanded function arguments.
unsigned argIdx = 1;
llvm::MDTuple *argTypes = kernelTypeMD->getTypeArray().get();
for (llvm::Argument &arg : Func.getArgumentList()) {
// Stop processing arguments if we run out of debug info.
if (argIdx >= argTypes->getNumOperands())
break;
// Create debuginfo entry for the argument and advance.
llvm::DILocalVariable *argVarDI = DebugInfo.createParameterVariable(
ExpandedFunc, arg.getName(), argIdx, sourceFileName, 1,
llvm::cast<llvm::DIType>(argTypes->getOperand(argIdx).get()),
true, 0
);
// Annotate the argument variable in the IR.
llvm::AllocaInst *argVar =
ir.CreateAlloca(arg.getType(), nullptr, arg.getName() + ".var");
llvm::StoreInst *argStore = ir.CreateStore(&arg, argVar);
llvm::LoadInst *loadedVar = ir.CreateLoad(argVar, arg.getName() + ".l");
DebugInfo.insertDeclare(argVar, argVarDI, emptyExpr,
llvm::DebugLoc::get(1, 1, ExpandedFunc), loadedVar);
for (llvm::Use &u : arg.uses())
if (u.getUser() != argStore)
u.set(loadedVar);
argIdx++;
}
// Annotate the index variable with metadata.
if (indexVar) {
// Debug information for loop index variable.
llvm::DILocalVariable *indexVarDI = DebugInfo.createAutoVariable(
ExpandedFunc, bcc::BCC_INDEX_VAR_NAME, sourceFileName, 1,
indexVarType, true
);
// Insert declaration annotation in the instruction stream.
llvm::Instruction *decl = DebugInfo.insertDeclare(
indexVar, indexVarDI, emptyExpr,
llvm::DebugLoc::get(1, 1, ExpandedFunc), indexVar);
indexVar->moveBefore(decl);
}
// Attach location information to each instruction in the function.
for (llvm::Instruction &inst : llvm::instructions(Func)) {
inst.setDebugLoc(llvm::DebugLoc::get(1, 1, ExpandedFunc));
}
}
// @brief Clean up the debug info.
//
// At the moment, it only finds the compile unit for the expanded function
// metadata generated by clang and removes it.
void cleanupDebugInfo(llvm::Module& Module) {
if (abiMetaCU == nullptr)
return;
// Remove the compile unit with the runtime interface DI.
llvm::SmallVector<llvm::MDNode*, 4> unitsTmp;
llvm::NamedMDNode *debugMD =
Module.getNamedMetadata(DEBUG_COMPILE_UNIT_MDNAME);
for (llvm::MDNode *cu : debugMD->operands())
if (cu != abiMetaCU)
unitsTmp.push_back(cu);
debugMD->eraseFromParent();
debugMD = Module.getOrInsertNamedMetadata(DEBUG_COMPILE_UNIT_MDNAME);
for (llvm::MDNode *cu : unitsTmp)
debugMD->addOperand(cu);
}
private:
// private attributes
llvm::DISubroutineType* kernelTypeMD;
llvm::DIFile *sourceFileName;
llvm::DIExpression *emptyExpr;
llvm::DICompileUnit *abiMetaCU;
llvm::DIType *indexVarType;
}; // end class RSAddDebugInfoPass
char RSAddDebugInfoPass::ID = 0;
static llvm::RegisterPass<RSAddDebugInfoPass> X("addrsdi", "Add RS DebugInfo Pass");
} // end anonymous namespace
namespace bcc {
llvm::ModulePass * createRSAddDebugInfoPass() {
return new RSAddDebugInfoPass();
}
} // end namespace bcc