| /* | |
| * 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 |