| /* |
| * 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 "Assert.h" |
| #include "Log.h" |
| #include "RSTransforms.h" |
| |
| #include "bcinfo/MetadataExtractor.h" |
| |
| #include <string> |
| |
| #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> |
| #include <llvm/ADT/SetVector.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; |
| } |
| |
| const size_t nForEachKernels = me.getExportForEachSignatureCount(); |
| const char **forEachKernels = me.getExportForEachNameList(); |
| const bcinfo::MetadataExtractor::Reduce *reductions = |
| me.getExportReduceList(); |
| const size_t nReductions = me.getExportReduceCount(); |
| |
| llvm::SmallSetVector<llvm::Function *, 16> expandFuncs{}; |
| auto pushExpanded = [&](const char *const name) -> void { |
| bccAssert(name && *name && (::strcmp(name, ".") != 0)); |
| |
| const std::string expandName = std::string(name) + ".expand"; |
| if (llvm::Function *const func = Module.getFunction(expandName)) |
| expandFuncs.insert(func); |
| }; |
| |
| for (size_t i = 0; i < nForEachKernels; ++i) |
| pushExpanded(forEachKernels[i]); |
| |
| for (size_t i = 0; i < nReductions; ++i) { |
| const bcinfo::MetadataExtractor::Reduce &reduction = reductions[i]; |
| pushExpanded(reduction.mAccumulatorName); |
| } |
| |
| // Set up the debug info builder. |
| llvm::DIBuilder DebugInfo(Module); |
| initializeDebugInfo(DebugInfo, Module); |
| |
| for (const auto &expandFunc : expandFuncs) { |
| // Attach DI metadata to each generated function. |
| // No inlining has occurred at this point so it's safe to name match |
| // without worrying about inlined function bodies. |
| attachDebugInfo(DebugInfo, *expandFunc); |
| } |
| |
| 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_GOOGLE_RenderScript, |
| 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 |
| ); |
| Func.setSubprogram(ExpandedFunc); |
| |
| // 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 |