/* * 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 #include #include #include #include #include #include #include #include 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 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(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(kernelPrototypeVarMD->getType()); DT = llvm::cast(DT->getBaseType()); llvm::DISubroutineType *ST = llvm::cast(DT->getBaseType()); kernelPrototypeMD = llvm::cast(ST->getRawTypeArray()); indexVarType = llvm::dyn_cast_or_null( 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(&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(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 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 X("addrsdi", "Add RS DebugInfo Pass"); } // end anonymous namespace namespace bcc { llvm::ModulePass * createRSAddDebugInfoPass() { return new RSAddDebugInfoPass(); } } // end namespace bcc