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(*last_stmt_ptr); + assert(cast(use_call->getCalleeDecl())->getName() == + ClangExpressionSourceCode::GetUseExprResultFunctionName()); + --last_stmt_ptr; + Stmt *last_stmt = *last_stmt_ptr; while (dyn_cast(last_stmt)) { @@ -380,6 +389,52 @@ *last_stmt_ptr = static_cast(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; } 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 target_machine; + llvm::SmallVector 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 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"; + 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/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 "" #define SUFFIX_NAME "" +/// \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/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(&(*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) {}