teemperor created this revision. teemperor added a reviewer: LLDB. teemperor added a project: LLDB. Herald added a subscriber: JDevlieghere. teemperor requested review of this revision.
At the moment we codegen or interpret the IR in exactly the way as it is generated by Clang. This patch runs the default O2 <https://reviews.llvm.org/owners/package/2/> optimizations over the generated LLVM module before we interpret or JIT it. The motivation for this patch is twofold. First, LLDB contains an LLVM IR interpreter that supports a very limited subset of LLVM instructions for the sake of maintainability. The IR interpreter is intended as a fast first path to run the code provided by the user (fast in comparison to JITing and injecting the code). It also is the only way LLDB can evaluate expressions for targets where we cant JIT&inject code. Because the LLVM IR interpreter only supports a limited set of instructions, most non-trivial Clang expression can't be interpreted which is really limiting the expression evaluator functionality when we can't JIT code. By running running the default -O2 passes over the IR before interpreting it, LLVM will give us usually a much more concise IR that the IR interpreter can more often than not handle. Second, sometimes users want to run expressions that are simple but might process quite a bit of data. An example for that would be any algorithm that users can now invoke via the `std` C++ module. Calling `std::count_if` with a relatively simple predicate on a large vector currently takes far too long to be considered usable. To give some stats on the first claim: - Currently we run about 7700 expressions in a test suit run (the actual number of expressions is fluctuating a bit....) - Around 5800 of these expressions are simple enough that the IR interpreter can interpret them right now (most of them just print simple variables) - This leaves us with around 1900 expressions that we currently can't interpret in the test suite. - After applying this patch the number of expressions we can't interpret drops to around 1300 (**-33%**). - The average expression evaluation time when running on my local machine doesn't change with this patch (around 50ms before and after). https://reviews.llvm.org/D102624 Files: lldb/source/Plugins/ExpressionParser/Clang/ASTResultSynthesizer.cpp lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.h lldb/source/Plugins/ExpressionParser/Clang/IRForTarget.cpp lldb/source/Plugins/ExpressionParser/Clang/IRForTarget.h
Index: lldb/source/Plugins/ExpressionParser/Clang/IRForTarget.h =================================================================== --- lldb/source/Plugins/ExpressionParser/Clang/IRForTarget.h +++ lldb/source/Plugins/ExpressionParser/Clang/IRForTarget.h @@ -103,6 +103,11 @@ /// True on success; false otherwise bool runOnModule(llvm::Module &llvm_module) override; + /// Removes all calls to the function with the given name in the module. + /// + /// All calls found by this function must be safe to remove. + void RemoveAllCallsToFunction(llvm::StringRef function_name); + /// Interface stub /// /// Implementation of the llvm::ModulePass::assignPassManager() function. Index: lldb/source/Plugins/ExpressionParser/Clang/IRForTarget.cpp =================================================================== --- lldb/source/Plugins/ExpressionParser/Clang/IRForTarget.cpp +++ lldb/source/Plugins/ExpressionParser/Clang/IRForTarget.cpp @@ -364,7 +364,9 @@ LLDB_LOG(log, "Replacing \"{0}\" with \"{1}\"", PrintValue(result_global), PrintValue(new_result_global)); - if (result_global->use_empty()) { + // If the old global had an initializer, create a store that initializes + // the new global with the same value. + if (result_global->hasInitializer()) { // We need to synthesize a store for this variable, because otherwise // there's nothing to put into its equivalent persistent variable. @@ -374,16 +376,6 @@ if (!first_entry_instruction) return false; - if (!result_global->hasInitializer()) { - LLDB_LOG(log, "Couldn't find initializer for unused variable"); - - m_error_stream.Format("Internal error [IRForTarget]: Result variable " - "({0}) has no writes and no initializer\n", - result_name); - - return false; - } - Constant *initializer = result_global->getInitializer(); StoreInst *synthesized_store = @@ -391,9 +383,9 @@ LLDB_LOG(log, "Synthesized result store \"{0}\"\n", PrintValue(synthesized_store)); - } else { - result_global->replaceAllUsesWith(new_result_global); } + // About to remove the old result global, so RAUW the new global. + result_global->replaceAllUsesWith(new_result_global); if (!m_decl_map->AddPersistentVariable( result_decl, m_result_name, m_result_type, true, m_result_is_pointer)) @@ -2023,6 +2015,24 @@ return true; } +void IRForTarget::RemoveAllCallsToFunction(llvm::StringRef function_name) { + for (llvm::Function &function : *m_module) { + for (BasicBlock &bb : function) { + for (BasicBlock::iterator i = bb.begin(), end = bb.end(); i != end;) { + if (CallInst *call = dyn_cast<CallInst>(&(*i))) { + if (llvm::Function *func = call->getCalledFunction()) { + if (func->getName() == function_name) { + i = call->eraseFromParent(); + continue; + } + } + } + ++i; + } + } + } +} + void IRForTarget::assignPassManager(PMStack &pass_mgr_stack, PassManagerType pass_mgr_type) {} Index: lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.h =================================================================== --- lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.h +++ lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.h @@ -73,6 +73,10 @@ bool GetOriginalBodyBounds(std::string transformed_text, size_t &start_loc, size_t &end_loc); + /// Returns the name of the function that is injected in the source code + /// to make sure the expression result is considered used by the optimizer. + static llvm::StringRef GetUseExprResultFunctionName(); + protected: ClangExpressionSourceCode(llvm::StringRef filename, llvm::StringRef name, llvm::StringRef prefix, llvm::StringRef body, Index: lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp =================================================================== --- lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp +++ lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp @@ -32,6 +32,8 @@ #define PREFIX_NAME "<lldb wrapper prefix>" #define SUFFIX_NAME "<lldb wrapper suffix>" +/// \see ClangExpressionSourceCode::GetUseExprResultFunctionName +#define LLDB_USE_EXPR_RESULT "__lldb_use_expr_result" const llvm::StringRef ClangExpressionSourceCode::g_prefix_file_name = PREFIX_NAME; @@ -71,6 +73,7 @@ extern "C" { int printf(const char * __restrict, ...); + void )" LLDB_USE_EXPR_RESULT R"((void *); } )"; @@ -415,6 +418,7 @@ "{ \n" " %s; \n" "%s" + " " LLDB_USE_EXPR_RESULT "(0);\n" "} \n", module_imports.c_str(), m_name.c_str(), lldb_local_var_decls.GetData(), tagged_body.c_str()); @@ -427,6 +431,7 @@ "{ \n" " %s; \n" "%s" + " " LLDB_USE_EXPR_RESULT "(nullptr); \n" "} \n", module_imports.c_str(), m_name.c_str(), lldb_local_var_decls.GetData(), tagged_body.c_str()); @@ -442,6 +447,7 @@ "{ \n" " %s; \n" "%s" + " " LLDB_USE_EXPR_RESULT "(0); \n" "} \n" "@end \n", module_imports.c_str(), m_name.c_str(), m_name.c_str(), @@ -459,6 +465,7 @@ "{ \n" " %s; \n" "%s" + " " LLDB_USE_EXPR_RESULT "(0); \n" "} \n" "@end \n", module_imports.c_str(), m_name.c_str(), m_name.c_str(), @@ -483,3 +490,7 @@ end_loc = transformed_text.find(m_end_marker); return end_loc != std::string::npos; } + +llvm::StringRef ClangExpressionSourceCode::GetUseExprResultFunctionName() { + return LLDB_USE_EXPR_RESULT; +} Index: lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp =================================================================== --- lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp +++ lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp @@ -44,11 +44,13 @@ #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Module.h" +#include "llvm/Passes/PassBuilder.h" #include "llvm/Support/DynamicLibrary.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/Host.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Signals.h" +#include "llvm/Transforms/IPO/PassManagerBuilder.h" #include "ClangDiagnostic.h" #include "ClangExpressionParser.h" @@ -648,8 +650,18 @@ CodeGenOptions::FramePointerKind::All); if (generate_debug_info) m_compiler->getCodeGenOpts().setDebugInfo(codegenoptions::FullDebugInfo); - else + else { m_compiler->getCodeGenOpts().setDebugInfo(codegenoptions::NoDebugInfo); + // If the user doesn't want to debug the expression we can optimize it. + // This makes it run a bit faster in case it's a long-running expression + // and it makes the life of the IRInterpreter easier as all the verbose + // IR nodes generated by Clang are simplified. + // The level set here gets propagated to the pass manager. + m_compiler->getCodeGenOpts().OptimizationLevel = 2; + // LLVM's lifetime markers aren't supported by the IRInterpreter so avoid + // generating them. + m_compiler->getCodeGenOpts().DisableLifetimeMarkers = true; + } // Disable some warnings. SetupDefaultClangDiagnostics(*m_compiler); @@ -1319,6 +1331,45 @@ return false; } +static void RunOptimizationPipeline(llvm::Module &module, + clang::CodeGenOptions &opts) { + // Build a fitting target machine for our triple. + llvm::Triple triple(module.getTargetTriple()); + std::unique_ptr<llvm::TargetMachine> target_machine; + llvm::SmallVector<std::string, 0> mAttrs; + target_machine.reset(llvm::EngineBuilder().selectTarget(triple, + /*MArch=*/"", + /*MCPU=*/"", mAttrs)); + + // Build the default analysis managers. + LoopAnalysisManager LAM; + FunctionAnalysisManager FAM; + CGSCCAnalysisManager CGAM; + ModuleAnalysisManager MAM; + + PassInstrumentationCallbacks PIC; + Optional<PGOOptions> P; + PipelineTuningOptions PTO; + PassBuilder PB(target_machine.get(), PTO, P, &PIC); + + // Register all passes. + PB.registerModuleAnalyses(MAM); + PB.registerCGSCCAnalyses(CGAM); + PB.registerFunctionAnalyses(FAM); + PB.registerLoopAnalyses(LAM); + PB.crossRegisterProxies(LAM, FAM, CGAM, MAM); + + ModulePassManager MPM; + // Pick a pipeline that fits to our CodeGenOpts optimization level and add + // the respective passes. + std::string pipelineString = + "default<O" + std::to_string(opts.OptimizationLevel) + ">"; + if (auto Err = PB.parsePassPipeline(MPM, pipelineString)) + llvm_unreachable("Failed to parse pipeline"); + + MPM.run(module, MAM); +} + lldb_private::Status ClangExpressionParser::PrepareForExecution( lldb::addr_t &func_addr, lldb::addr_t &func_end, lldb::IRExecutionUnitSP &execution_unit_sp, ExecutionContext &exe_ctx, @@ -1407,6 +1458,17 @@ return err; } + // Run the default LLVM optimization passes over the generated module unless + // this is an expression that the user wants to debug (in which case the + // optimizations would degrade the debug experience). + if (!GetGenerateDebugInfo()) + RunOptimizationPipeline(*execution_unit_sp->GetModule(), + m_compiler->getCodeGenOpts()); + // Remove the injected function call that prevents the optimizer from + // removing the seemingly dead store of the expression result. + ir_for_target.RemoveAllCallsToFunction( + ClangExpressionSourceCode::GetUseExprResultFunctionName()); + Process *process = exe_ctx.GetProcessPtr(); if (execution_policy != eExecutionPolicyAlways && Index: lldb/source/Plugins/ExpressionParser/Clang/ASTResultSynthesizer.cpp =================================================================== --- lldb/source/Plugins/ExpressionParser/Clang/ASTResultSynthesizer.cpp +++ lldb/source/Plugins/ExpressionParser/Clang/ASTResultSynthesizer.cpp @@ -9,6 +9,7 @@ #include "ASTResultSynthesizer.h" #include "ClangASTImporter.h" +#include "ClangExpressionSourceCode.h" #include "ClangPersistentVariables.h" #include "Plugins/TypeSystem/Clang/TypeSystemClang.h" @@ -209,6 +210,14 @@ return false; Stmt **last_stmt_ptr = Body->body_end() - 1; + // The last statement in the expression is our injected call to + // __lldb_use_expr_result. The expression before is the actual user expression + // so skip the injected function call. + CallExpr *use_call = cast<CallExpr>(*last_stmt_ptr); + assert(cast<FunctionDecl>(use_call->getCalleeDecl())->getName() == + ClangExpressionSourceCode::GetUseExprResultFunctionName()); + --last_stmt_ptr; + Stmt *last_stmt = *last_stmt_ptr; while (dyn_cast<NullStmt>(last_stmt)) { @@ -380,6 +389,52 @@ *last_stmt_ptr = static_cast<Stmt *>(result_initialization_stmt_result.get()); + // At this point we created the result variable that stores the result of + // the last expression. However, in a situation where we finish on a local + // variable, the expression result is just a pointer to something on the stack + // and LLVM will optimize that away. E.g., `MyClass c; c` becomes: + // + // MyClass c; + // static MyClass *__lldb_expr_result_ptr = &c; // Points to stack. + // } // End of expression function. + // + // The optimizer will just remove this store. So we add a trailing call to + // the undefined function `__lldb_use_expr_result` which will take an address + // of `__lldb_expr_result_ptr`. We will manually remove that call later, + // but until we remove that function the optimizer can no longer remove + // the store to our result variable: + // + // MyClass c; + // static MyClass *__lldb_expr_result_ptr = &c; // Points to stack. + // __lldb_expr_result_ptr(__lldb_expr_result_ptr); + // } // End of expression function. + // + // We already have a call at the end in the form: + // __lldb_expr_result_ptr(nullptr); + // which we injected into the wrapping source code. We just need to change + // the placeholder `nullptr` argument to the result variable we created above + // and do any casting. + Expr *ref_result = DeclRefExpr::Create( + Ctx, NestedNameSpecifierLoc(), SourceLocation(), result_decl, + /*RefersToEnclosingVariableOrCapture=*/false, SourceLocation(), + result_decl->getType(), ExprValueKind::VK_LValue); + QualType ref_type = ref_result->getType(); + // __lldb_expr_result isn't a pointer for non-lvalues, so inject an &: + // __lldb_expr_result_ptr(&__lldb_expression_result) + if (!ref_type->isPointerType()) + ref_result = UnaryOperator::Create( + Ctx, ref_result, UnaryOperator::Opcode::UO_AddrOf, + Ctx.getPointerType(ref_type), ExprValueKind::VK_RValue, + ExprObjectKind::OK_Ordinary, SourceLocation(), /*CanOverflow=*/false, + FPOptionsOverride()); + // Cast the pointer type we have to void so that the type system is not + // complaining. + ImplicitCastExpr *c = ImplicitCastExpr::Create( + Ctx, Ctx.VoidPtrTy, CastKind::CK_BitCast, ref_result, nullptr, + ExprValueKind::VK_RValue, FPOptionsOverride()); + // Overwrite the placeholder argument. + use_call->getArgs()[0] = c; + return true; }
_______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits