From 9383789d9f0d6b378fdae5c8d4cf56c366c756d2 Mon Sep 17 00:00:00 2001 From: Fred Riss Date: Tue, 9 Nov 2021 09:50:37 -0800 Subject: [PATCH 1/4] [LLDB] Framework umbrella header should to include everything --- lldb/include/lldb/API/LLDB.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lldb/include/lldb/API/LLDB.h b/lldb/include/lldb/API/LLDB.h index eacbbeafcf1cd..f703e8c21fc4f 100644 --- a/lldb/include/lldb/API/LLDB.h +++ b/lldb/include/lldb/API/LLDB.h @@ -9,6 +9,8 @@ #ifndef LLDB_API_LLDB_H #define LLDB_API_LLDB_H +#include "lldb/lldb-public.h" + #include "lldb/API/SBAddress.h" #include "lldb/API/SBAttachInfo.h" #include "lldb/API/SBBlock.h" From 5de52e6615d11189321bc1bb3361ff43934fb297 Mon Sep 17 00:00:00 2001 From: Fred Riss Date: Tue, 9 Nov 2021 09:51:18 -0800 Subject: [PATCH 2/4] [LLDB] Install a modulemap in LLDB.framework --- lldb/cmake/modules/LLDBFramework.cmake | 17 ++++++++++++++++- lldb/include/lldb/lldb-framework.modulemap | 10 ++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 lldb/include/lldb/lldb-framework.modulemap diff --git a/lldb/cmake/modules/LLDBFramework.cmake b/lldb/cmake/modules/LLDBFramework.cmake index 3f04cbde8bac5..1424373879738 100644 --- a/lldb/cmake/modules/LLDBFramework.cmake +++ b/lldb/cmake/modules/LLDBFramework.cmake @@ -59,13 +59,28 @@ set(CMAKE_XCODE_ATTRIBUTE_CLANG_WARN_DOCUMENTATION_COMMENTS "YES") # On iOS, there is no versioned framework layout. Skip this symlink step. if(NOT APPLE_EMBEDDED) - # Apart from this one, CMake creates all required symlinks in the framework bundle. + add_custom_command(TARGET liblldb POST_BUILD + COMMAND ${CMAKE_COMMAND} -E make_directory + ${LLDB_FRAMEWORK_ABSOLUTE_BUILD_DIR}/LLDB.framework/Versions/A/Modules/ + COMMAND ${CMAKE_COMMAND} -E copy_if_different + ${LLDB_SOURCE_DIR}/include/lldb/lldb-framework.modulemap + ${LLDB_FRAMEWORK_ABSOLUTE_BUILD_DIR}/LLDB.framework/Versions/A/Modules/module.modulemap + COMMENT "LLDB.framework: copy framework modulemap" + ) + + # Apart from these ones, CMake creates all required symlinks in the framework bundle. add_custom_command(TARGET liblldb POST_BUILD COMMAND ${CMAKE_COMMAND} -E create_symlink Versions/Current/Headers ${LLDB_FRAMEWORK_ABSOLUTE_BUILD_DIR}/LLDB.framework/Headers COMMENT "LLDB.framework: create Headers symlink" ) + add_custom_command(TARGET liblldb POST_BUILD + COMMAND ${CMAKE_COMMAND} -E create_symlink + Versions/Current/Modules + ${LLDB_FRAMEWORK_ABSOLUTE_BUILD_DIR}/LLDB.framework/Modules + COMMENT "LLDB.framework: create Modules symlink" + ) endif() # At configuration time, collect headers for the framework bundle and copy them diff --git a/lldb/include/lldb/lldb-framework.modulemap b/lldb/include/lldb/lldb-framework.modulemap new file mode 100644 index 0000000000000..bdf482e901383 --- /dev/null +++ b/lldb/include/lldb/lldb-framework.modulemap @@ -0,0 +1,10 @@ +framework module LLDB { + requires cplusplus + umbrella header "LLDB.h" + + export * + module * { + export * + } +} + From a74f6fe71e51bc1bceb428b1a2c6283ef1ddc600 Mon Sep 17 00:00:00 2001 From: Fred Riss Date: Tue, 9 Nov 2021 14:02:25 -0800 Subject: [PATCH 3/4] [LLDB] Add a Swift script interpreter This uses the JIT to compile swift code on the fly and the Swift C++ interop feature to expose the LLDB SBAPI in the Swift language. --- lldb/include/lldb/lldb-enumerations.h | 1 + .../CommandObjectBreakpointCommand.cpp | 1 + lldb/source/Commands/CommandObjectScript.cpp | 5 + .../CommandObjectWatchpointCommand.cpp | 1 + lldb/source/Interpreter/OptionArgParser.cpp | 2 + lldb/source/Interpreter/ScriptInterpreter.cpp | 4 + .../Plugins/ScriptInterpreter/CMakeLists.txt | 2 + .../ScriptInterpreter/Swift/CMakeLists.txt | 9 + .../Swift/ScriptInterpreterSwift.cpp | 140 ++++++++ .../Swift/ScriptInterpreterSwift.h | 77 ++++ .../Swift/SwiftInterpreter.cpp | 333 ++++++++++++++++++ .../Swift/SwiftInterpreter.h | 54 +++ .../TypeSystem/Swift/SwiftASTContext.h | 1 + .../Swift/Inputs/sblist.swift | 24 ++ .../Swift/Inputs/sbtarget.swift | 12 + .../ScriptInterpreter/Swift/sanity_check.test | 12 + 16 files changed, 678 insertions(+) create mode 100644 lldb/source/Plugins/ScriptInterpreter/Swift/CMakeLists.txt create mode 100644 lldb/source/Plugins/ScriptInterpreter/Swift/ScriptInterpreterSwift.cpp create mode 100644 lldb/source/Plugins/ScriptInterpreter/Swift/ScriptInterpreterSwift.h create mode 100644 lldb/source/Plugins/ScriptInterpreter/Swift/SwiftInterpreter.cpp create mode 100644 lldb/source/Plugins/ScriptInterpreter/Swift/SwiftInterpreter.h create mode 100644 lldb/test/Shell/ScriptInterpreter/Swift/Inputs/sblist.swift create mode 100644 lldb/test/Shell/ScriptInterpreter/Swift/Inputs/sbtarget.swift create mode 100644 lldb/test/Shell/ScriptInterpreter/Swift/sanity_check.test diff --git a/lldb/include/lldb/lldb-enumerations.h b/lldb/include/lldb/lldb-enumerations.h index 1bdf250e533c3..b313dedabcbd4 100644 --- a/lldb/include/lldb/lldb-enumerations.h +++ b/lldb/include/lldb/lldb-enumerations.h @@ -217,6 +217,7 @@ enum ScriptLanguage { eScriptLanguageNone = 0, eScriptLanguagePython, eScriptLanguageLua, + eScriptLanguageSwift, eScriptLanguageUnknown, eScriptLanguageDefault = eScriptLanguagePython }; diff --git a/lldb/source/Commands/CommandObjectBreakpointCommand.cpp b/lldb/source/Commands/CommandObjectBreakpointCommand.cpp index 26d35c82f57d0..03e4e52d67b6e 100644 --- a/lldb/source/Commands/CommandObjectBreakpointCommand.cpp +++ b/lldb/source/Commands/CommandObjectBreakpointCommand.cpp @@ -305,6 +305,7 @@ are no syntax errors may indicate that a function was declared but never called. switch (m_script_language) { case eScriptLanguagePython: case eScriptLanguageLua: + case eScriptLanguageSwift: m_use_script_language = true; break; case eScriptLanguageNone: diff --git a/lldb/source/Commands/CommandObjectScript.cpp b/lldb/source/Commands/CommandObjectScript.cpp index f53d6540bc049..76a718b674434 100644 --- a/lldb/source/Commands/CommandObjectScript.cpp +++ b/lldb/source/Commands/CommandObjectScript.cpp @@ -31,6 +31,11 @@ static constexpr OptionEnumValueElement g_script_option_enumeration[] = { "lua", "Lua", }, + { + eScriptLanguageSwift, + "swift", + "Swift", + }, { eScriptLanguageNone, "default", diff --git a/lldb/source/Commands/CommandObjectWatchpointCommand.cpp b/lldb/source/Commands/CommandObjectWatchpointCommand.cpp index 1f4e953663857..a27967b98d154 100644 --- a/lldb/source/Commands/CommandObjectWatchpointCommand.cpp +++ b/lldb/source/Commands/CommandObjectWatchpointCommand.cpp @@ -337,6 +337,7 @@ are no syntax errors may indicate that a function was declared but never called. switch (m_script_language) { case eScriptLanguagePython: case eScriptLanguageLua: + case eScriptLanguageSwift: m_use_script_language = true; break; case eScriptLanguageNone: diff --git a/lldb/source/Interpreter/OptionArgParser.cpp b/lldb/source/Interpreter/OptionArgParser.cpp index 93b01abde4bb9..63a5190f2adc2 100644 --- a/lldb/source/Interpreter/OptionArgParser.cpp +++ b/lldb/source/Interpreter/OptionArgParser.cpp @@ -129,6 +129,8 @@ lldb::ScriptLanguage OptionArgParser::ToScriptLanguage( return eScriptLanguagePython; if (s.equals_insensitive("lua")) return eScriptLanguageLua; + if (s.equals_insensitive("swift")) + return eScriptLanguageSwift; if (s.equals_insensitive("default")) return eScriptLanguageDefault; if (s.equals_insensitive("none")) diff --git a/lldb/source/Interpreter/ScriptInterpreter.cpp b/lldb/source/Interpreter/ScriptInterpreter.cpp index f26474836a68c..e7b1e9602621d 100644 --- a/lldb/source/Interpreter/ScriptInterpreter.cpp +++ b/lldb/source/Interpreter/ScriptInterpreter.cpp @@ -64,6 +64,8 @@ std::string ScriptInterpreter::LanguageToString(lldb::ScriptLanguage language) { return "Python"; case eScriptLanguageLua: return "Lua"; + case eScriptLanguageSwift: + return "Swift"; case eScriptLanguageUnknown: return "Unknown"; } @@ -91,6 +93,8 @@ ScriptInterpreter::StringToLanguage(const llvm::StringRef &language) { return eScriptLanguagePython; if (language.equals_insensitive(LanguageToString(eScriptLanguageLua))) return eScriptLanguageLua; + if (language.equals_insensitive(LanguageToString(eScriptLanguageSwift))) + return eScriptLanguageSwift; return eScriptLanguageUnknown; } diff --git a/lldb/source/Plugins/ScriptInterpreter/CMakeLists.txt b/lldb/source/Plugins/ScriptInterpreter/CMakeLists.txt index fa1c72a32fe13..d22e33171d3dd 100644 --- a/lldb/source/Plugins/ScriptInterpreter/CMakeLists.txt +++ b/lldb/source/Plugins/ScriptInterpreter/CMakeLists.txt @@ -6,3 +6,5 @@ endif() if (LLDB_ENABLE_LUA) add_subdirectory(Lua) endif() + +add_subdirectory(Swift) diff --git a/lldb/source/Plugins/ScriptInterpreter/Swift/CMakeLists.txt b/lldb/source/Plugins/ScriptInterpreter/Swift/CMakeLists.txt new file mode 100644 index 0000000000000..2737170b6467e --- /dev/null +++ b/lldb/source/Plugins/ScriptInterpreter/Swift/CMakeLists.txt @@ -0,0 +1,9 @@ +add_lldb_library(lldbPluginScriptInterpreterSwift PLUGIN + ScriptInterpreterSwift.cpp + SwiftInterpreter.cpp + + LINK_LIBS + lldbCore + lldbInterpreter + LLVMOrcJIT + ) diff --git a/lldb/source/Plugins/ScriptInterpreter/Swift/ScriptInterpreterSwift.cpp b/lldb/source/Plugins/ScriptInterpreter/Swift/ScriptInterpreterSwift.cpp new file mode 100644 index 0000000000000..d2df4206408f6 --- /dev/null +++ b/lldb/source/Plugins/ScriptInterpreter/Swift/ScriptInterpreterSwift.cpp @@ -0,0 +1,140 @@ +//===-- ScriptInterpreterSwift.cpp ----------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "ScriptInterpreterSwift.h" +#include "SwiftInterpreter.h" + +#include "lldb/Core/Debugger.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Interpreter/CommandReturnObject.h" + +#include "llvm/Support/FormatAdapters.h" + +#include +#include + +using namespace lldb; +using namespace lldb_private; + +LLDB_PLUGIN_DEFINE(ScriptInterpreterSwift) + +ScriptInterpreterSwift::ScriptInterpreterSwift(Debugger &Debugger) + : ScriptInterpreter(Debugger, eScriptLanguageSwift) {} + +ScriptInterpreterSwift::~ScriptInterpreterSwift() {} + +bool ScriptInterpreterSwift::ExecuteOneLine( + llvm::StringRef Command, CommandReturnObject *Result, + const ExecuteScriptOptions &Options) { + if (Command.empty()) { + if (Result) + Result->AppendError("empty command passed to swift\n"); + return false; + } + + llvm::Expected> + IORedirectOrError = ScriptInterpreterIORedirect::Create( + Options.GetEnableIO(), m_debugger, Result); + if (!IORedirectOrError) { + if (Result) + Result->AppendErrorWithFormatv( + "failed to redirect I/O: {0}\n", + llvm::fmt_consume(IORedirectOrError.takeError())); + else + llvm::consumeError(IORedirectOrError.takeError()); + return false; + } + + ScriptInterpreterIORedirect &IORedirect = **IORedirectOrError; + + if (auto Error = m_swift->executeOneLine(Command, m_debugger)) { + Result->AppendErrorWithFormatv( + "Swift failed attempting to evaluate '{0}': {1}\n", Command, + llvm::toString(std::move(Error))); + return false; + } + + IORedirect.Flush(); + return true; +} + +void ScriptInterpreterSwift::ExecuteInterpreterLoop() { + // At the moment, the only time the debugger does not have an input file + // handle is when this is called directly from lua, in which case it is + // both dangerous and unnecessary (not to mention confusing) to try to embed + // a running interpreter loop inside the already running lua interpreter + // loop, so we won't do it. + if (!m_debugger.GetInputFile().IsValid()) + return; + + // FIXME: unimplemented +} + +bool ScriptInterpreterSwift::LoadScriptingModule( + const char *Filename, const LoadScriptOptions &options, + lldb_private::Status &Error, StructuredData::ObjectSP *Module_sp, + FileSpec ExtraSearchDir) { + + FileSystem::Instance().Collect(Filename); + if (llvm::Error E = m_swift->loadScriptFile(Filename)) { + Error.SetErrorStringWithFormatv("swift failed to import '{0}': {1}\n", + Filename, llvm::toString(std::move(E))); + return false; + } + return true; +} + +void ScriptInterpreterSwift::Initialize() { + static llvm::once_flag g_onceFlag; + + llvm::call_once(g_onceFlag, []() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), + lldb::eScriptLanguageSwift, CreateInstance); + }); +} + +void ScriptInterpreterSwift::Terminate() {} + +llvm::Error ScriptInterpreterSwift::EnterSession(user_id_t DebuggerId) { + if (m_sessionIsActive) + return llvm::Error::success(); + + m_sessionIsActive = true; + + return llvm::Error::success(); +} + +llvm::Error ScriptInterpreterSwift::LeaveSession() { + if (!m_sessionIsActive) + return llvm::Error::success(); + + m_sessionIsActive = false; + + return llvm::Error::success(); +} + +lldb::ScriptInterpreterSP +ScriptInterpreterSwift::CreateInstance(Debugger &Debugger) { + return std::make_shared(Debugger); +} + +lldb_private::ConstString ScriptInterpreterSwift::GetPluginNameStatic() { + static ConstString g_name("script-swift"); + return g_name; +} + +const char *ScriptInterpreterSwift::GetPluginDescriptionStatic() { + return "Swift script interpreter"; +} + +lldb_private::ConstString ScriptInterpreterSwift::GetPluginName() { + return GetPluginNameStatic(); +} + +uint32_t ScriptInterpreterSwift::GetPluginVersion() { return 1; } diff --git a/lldb/source/Plugins/ScriptInterpreter/Swift/ScriptInterpreterSwift.h b/lldb/source/Plugins/ScriptInterpreter/Swift/ScriptInterpreterSwift.h new file mode 100644 index 0000000000000..2ba1db1eec433 --- /dev/null +++ b/lldb/source/Plugins/ScriptInterpreter/Swift/ScriptInterpreterSwift.h @@ -0,0 +1,77 @@ +//===-- ScriptInterpreterSwift.h --------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ScriptInterpreterSwift_h_ +#define liblldb_ScriptInterpreterSwift_h_ + +#include "lldb/Interpreter/ScriptInterpreter.h" +#include "lldb/Utility/Status.h" +#include "lldb/lldb-enumerations.h" + +namespace lldb_private { + +class SwiftInterpreter; + +class ScriptInterpreterSwift : public ScriptInterpreter { +public: + class CommandDataSwift : public BreakpointOptions::CommandData { + public: + CommandDataSwift() : BreakpointOptions::CommandData() { + interpreter = lldb::eScriptLanguageSwift; + } + }; + + ScriptInterpreterSwift(Debugger &debugger); + + ~ScriptInterpreterSwift() override; + + bool ExecuteOneLine( + llvm::StringRef command, CommandReturnObject *result, + const ExecuteScriptOptions &options = ExecuteScriptOptions()) override; + + void ExecuteInterpreterLoop() override; + + bool LoadScriptingModule(const char *filename, + const LoadScriptOptions &options, + lldb_private::Status &error, + StructuredData::ObjectSP *module_sp = nullptr, + FileSpec extra_search_dir = {}) override; + + // Static Functions + static void Initialize(); + + static void Terminate(); + + static lldb::ScriptInterpreterSP CreateInstance(Debugger &debugger); + + static lldb_private::ConstString GetPluginNameStatic(); + + static const char *GetPluginDescriptionStatic(); + + static bool BreakpointCallbackFunction(void *baton, + StoppointCallbackContext *context, + lldb::user_id_t break_id, + lldb::user_id_t break_loc_id); + + // PluginInterface protocol + lldb_private::ConstString GetPluginName() override; + + uint32_t GetPluginVersion() override; + + llvm::Error EnterSession(lldb::user_id_t debugger_id); + llvm::Error LeaveSession(); + +private: + bool m_sessionIsActive = false; + std::unique_ptr m_swift = + std::make_unique(); +}; + +} // namespace lldb_private + +#endif // liblldb_ScriptInterpreterSwift_h_ diff --git a/lldb/source/Plugins/ScriptInterpreter/Swift/SwiftInterpreter.cpp b/lldb/source/Plugins/ScriptInterpreter/Swift/SwiftInterpreter.cpp new file mode 100644 index 0000000000000..c2fdcb7aa4e2b --- /dev/null +++ b/lldb/source/Plugins/ScriptInterpreter/Swift/SwiftInterpreter.cpp @@ -0,0 +1,333 @@ +//===-- SwiftInterpreter.cpp ----------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "SwiftInterpreter.h" + +#include "Plugins/ExpressionParser/Swift/SwiftHost.h" +#include "Plugins/Platform/MacOSX/PlatformDarwin.h" +#include "Plugins/TypeSystem/Swift/SwiftASTContext.h" +#include "lldb/API/SBDebugger.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Expression/IRExecutionUnit.h" +#include "lldb/Host/HostInfo.h" + +#include "llvm/ExecutionEngine/Orc/Mangling.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/ScopedPrinter.h" + +#include "swift/AST/IRGenRequests.h" +#include "swift/Frontend/ModuleInterfaceLoader.h" +#include "swift/SIL/TypeLowering.h" +#include "swift/SILOptimizer/PassManager/Passes.h" +#include "swift/Serialization/SerializedModuleLoader.h" +#include "swift/Subsystems.h" + +#include + +using namespace lldb; +using namespace lldb_private; + +SwiftInterpreter::SwiftInterpreter() { + // Make sure the Swift runtime is loaded. + static auto swiftCore_handle = + dlopen("/usr/lib/swift/libswiftCore.dylib", RTLD_LAZY); + assert(swiftCore_handle); +} + +Error SwiftInterpreter::initJIT() { + llvm::orc::LLJITBuilder builder; + + auto maybe_jit = builder.create(); + if (!maybe_jit) + return maybe_jit.takeError(); + m_jit = std::move(*maybe_jit); + + return Error::success(); +} + +Expected SwiftInterpreter::createJITDylib() { + int dylib_id = m_current_id++; + + auto jit_dylib_name = + llvm::to_string(llvm::formatv("__lldb_swift_script_dylib_{0}", dylib_id)); + auto maybe_dylib = m_jit->createJITDylib(jit_dylib_name); + if (!maybe_dylib) + return maybe_dylib.takeError(); + + // Make all the previous JIT dylibs available. + // FIXME: MatchAllSymbols shouldn't be necessary but some examples break + // without this. + for (auto *previous_dylib : m_dylibs) + maybe_dylib->addToLinkOrder( + *previous_dylib, llvm::orc::JITDylibLookupFlags::MatchAllSymbols); + + // Add a generator so that the script can use symbols from the main + // executable image. This could be restricted to something which just provides + // the SB symbols and the swift runtime symbols. + auto maybe_searcher = + llvm::orc::DynamicLibrarySearchGenerator::GetForCurrentProcess( + m_jit->getDataLayout().getGlobalPrefix()); + if (!maybe_searcher) + return maybe_searcher.takeError(); + maybe_dylib->addGenerator(std::move(*maybe_searcher)); + + m_dylibs.emplace_back(&*maybe_dylib); + return *m_dylibs.back(); +} + +Error SwiftInterpreter::initCompiler() { + // Create CompilerInstance with C++ interop. + m_compiler_invocation = std::make_unique(); + auto triple = + SwiftASTContext::GetSwiftFriendlyTriple(HostInfoBase::GetTargetTriple()); + m_compiler_invocation->setTargetTriple(triple); + m_compiler_invocation->getFrontendOptions().ModuleName = "lldb"; + + swift::IRGenOptions &irgen_opts = m_compiler_invocation->getIRGenOptions(); + irgen_opts.OutputKind = swift::IRGenOutputKind::Module; + irgen_opts.UseJIT = true; + swift::LangOptions &lang_opts = m_compiler_invocation->getLangOptions(); + lang_opts.EnableObjCInterop = true; + lang_opts.EnableCXXInterop = true; + + std::string sdk_path = + HostInfo::GetXcodeSDKPath(XcodeSDK::GetAnyMacOS()).str(); + std::string stdlib_os_dir = SwiftASTContext::GetSwiftStdlibOSDir( + triple, HostInfo::GetArchitecture().GetTriple()); + auto resource_dir = SwiftASTContext::GetResourceDir( + sdk_path, stdlib_os_dir, GetSwiftResourceDir().GetPath(), + HostInfo::GetXcodeContentsDirectory().GetPath(), + PlatformDarwin::GetCurrentToolchainDirectory().GetPath(), + PlatformDarwin::GetCurrentCommandLineToolsDirectory().GetPath()); + + // Setup paths for the ClangImporter to find the SDK. The SB headers + // depend on some SDK headers. + swift::ClangImporterOptions &clang_importer_options = + m_compiler_invocation->getClangImporterOptions(); + clang_importer_options.ExtraArgs.push_back("-isysroot"); + clang_importer_options.ExtraArgs.push_back(sdk_path); + + llvm::errs() << "resource_dir is " << resource_dir << "\n"; + // Allow the compiler to find the Swift standard library. + m_compiler_invocation->setRuntimeResourcePath(resource_dir); + + // Finally create the CompilerInstance. + m_compiler_instance = std::make_unique(); + + // Display diagnostics to stderr. + m_diagnostic_consumer = std::make_unique(); + m_compiler_instance->addDiagnosticConsumer(m_diagnostic_consumer.get()); + + if (m_compiler_instance->setup(*m_compiler_invocation)) { + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "Failed to setup CompilerInstance"); + } + + // Allow the compiler to find the LLDB framework. + // FIXME: It's unclear whether this is correct after LLDB is installed. + auto &ast_context = m_compiler_instance->getASTContext(); + ast_context.addSearchPath(resource_dir + "/../../..", true /* isFramework */, + false /* isSystem */); + return Error::success(); +} + +Error SwiftInterpreter::initialize() { + if (m_inited) + return Error::success(); + + if (auto error = initJIT()) + return error; + if (auto error = initCompiler()) + return error; + m_inited = true; + return Error::success(); +} + +Error SwiftInterpreter::loadScriptFile(StringRef filename) { + FileSpec file(filename); + if (!FileSystem::Instance().Exists(file)) + return llvm::make_error("invalid path", + llvm::inconvertibleErrorCode()); + + ConstString module_extension = file.GetFileNameExtension(); + if (module_extension != ".swift") + return llvm::make_error("invalid extension", + llvm::inconvertibleErrorCode()); + + auto maybe_mem_buffer = llvm::MemoryBuffer::getFile(filename); + if (!maybe_mem_buffer) + return llvm::make_error("Error reading script file", + maybe_mem_buffer.getError()); + + return executeMemoryBuffer(std::move(*maybe_mem_buffer)); +} + +Error SwiftInterpreter::executeOneLine(StringRef line, Debugger &debugger) { + // Provide lldb.{debugger,target,process} in the interactive interpreter + // FIXME: the `var debugger = debugger` are to workaround Swift C++ interop + // shortcomings. They should get removed. + auto script_template = R"( +import LLDB + +public extension lldb { + static var debugger: lldb.SBDebugger { + get { UnsafePointer(bitPattern:0x%llx)!.pointee } + } + // GetSelectedTarget() is not const and lldb.debugger is immutable so + // we need to copy it. + static var target: lldb.SBTarget { + get { var debugger = debugger; return debugger.GetSelectedTarget() } + } + // GetProcess() is not const and lldb.target is immutable so we need to + // copy it. + static var process: lldb.SBProcess { + get { var target = target; return target.GetProcess() } + } +} + +#sourceLocation(file: "", line: 1) +%s +)"; + + SBDebugger sb_debugger(debugger.shared_from_this()); + + std::string script = llvm::to_string( + llvm::format(script_template, &sb_debugger, line.data())); + std::unique_ptr expr_buffer( + llvm::MemoryBuffer::getMemBufferCopy(script, + "")); + + return executeMemoryBuffer(std::move(expr_buffer)); +} + +/// JIT and run the top-level code provided in the MemoryBuffer. +/// This will create a new Swift module and a new JITDylib and make sure +/// previous definitions are available in its context. +Error SwiftInterpreter::executeMemoryBuffer( + std::unique_ptr code) { + if (auto error = initialize()) + return error; + + auto &ast_context = m_compiler_instance->getASTContext(); + auto &source_manager = m_compiler_instance->getSourceMgr(); + + // Make sure previous errors do not prevent us from executing. + ast_context.Diags.resetHadAnyError(); + + unsigned buffer_id = source_manager.addNewSourceBuffer(std::move(code)); + + // Make sure previous modules are implicitly imported. + swift::ImplicitImportInfo import_info; + import_info.StdlibKind = swift::ImplicitStdlibKind::Stdlib; + for (auto module : m_modules) + import_info.AdditionalImports.push_back(module); + + // Create our new module. + auto module_name = llvm::to_string( + llvm::formatv("__lldb_swift_script_module_{0}", m_current_id)); + auto module_id = ast_context.getIdentifier(module_name); + auto *module = swift::ModuleDecl::create(module_id, ast_context, import_info); + + // This puts the compiler in "script" mode. + swift::SourceFileKind source_file_kind = swift::SourceFileKind::Main; + + // Create the SourceSile for our MemoryBuffer. + swift::SourceFile *source_file = new (ast_context) + swift::SourceFile(*module, source_file_kind, buffer_id, + swift::SourceFile::ParsingFlags::DisableDelayedBodies); + module->addFile(*source_file); + + // And compile it... + swift::performImportResolution(*source_file); + if (ast_context.hadError()) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "Failed to perform import resolution"); + + swift::bindExtensions(*module); + if (ast_context.hadError()) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "Failed to bind extensions"); + + swift::performTypeChecking(*source_file); + if (ast_context.hadError()) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "Failed to type check"); + + // Generate SIL. + auto sil_types = std::make_unique(*module); + std::unique_ptr sil_module = swift::performASTLowering( + *source_file, *sil_types, m_compiler_invocation->getSILOptions()); + + runSILDiagnosticPasses(*sil_module); + if (ast_context.hadError()) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "Error during SIL diagnostic passes"); + runSILLoweringPasses(*sil_module); + if (ast_context.hadError()) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "Error during SIL lowering"); + + // Generate LLVM IR. + std::unique_ptr llvm_module; + std::unique_ptr llvm_context; + { + std::lock_guard context_locker( + IRExecutionUnit::GetLLVMGlobalContextMutex()); + + const auto &irgen_opts = m_compiler_invocation->getIRGenOptions(); + + auto gen_module = swift::performIRGeneration( + module, irgen_opts, m_compiler_invocation->getTBDGenOptions(), + std::move(sil_module), "lldb_module", + swift::PrimarySpecificPaths("", ""), + llvm::ArrayRef()); + + if (!gen_module) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "Error generating LLVM IR"); + + swift::performLLVMOptimizations(irgen_opts, gen_module.getModule(), + gen_module.getTargetMachine()); + + auto context_and_module = std::move(gen_module).release(); + llvm_context.reset(context_and_module.first); + llvm_module.reset(context_and_module.second); + } + + // Create a new JITDylib and add the IR to it, compiling it in the process. + auto maybe_dylib = createJITDylib(); + if (!maybe_dylib) + return maybe_dylib.takeError(); + auto &dylib = *maybe_dylib; + + auto add_ir_error = m_jit->addIRModule( + dylib, llvm::orc::ThreadSafeModule(std::move(llvm_module), + std::move(llvm_context))); + if (add_ir_error) + return add_ir_error; + + // Given the compiler is in "scripting" mode, it will generate a `main` symbol + // as an entry point. + auto maybe_symbol = m_jit->lookup(dylib, llvm::to_string("main")); + if (!maybe_symbol) + return maybe_symbol.takeError(); + + // Invoke the entry point. + void (*script)(void) = (void (*)(void))maybe_symbol->getAddress(); + script(); + + m_modules.push_back(swift::AttributedImport( + swift::ImportedModule(module))); + + // Remove the entry point so that it doesn't conflict with future ones. + auto remove_error = dylib.remove({m_jit->mangleAndIntern("main")}); + if (remove_error) + return remove_error; + + return Error::success(); +} diff --git a/lldb/source/Plugins/ScriptInterpreter/Swift/SwiftInterpreter.h b/lldb/source/Plugins/ScriptInterpreter/Swift/SwiftInterpreter.h new file mode 100644 index 0000000000000..b9f1717df22e3 --- /dev/null +++ b/lldb/source/Plugins/ScriptInterpreter/Swift/SwiftInterpreter.h @@ -0,0 +1,54 @@ +//===-- SwiftInterpreter.h --------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_SwiftInterpreter_h_ +#define liblldb_SwiftInterpreter_h_ + +#include "llvm/ExecutionEngine/Orc/LLJIT.h" +#include "llvm/Support/Error.h" + +#include "swift/Frontend/Frontend.h" +#include "swift/Frontend/PrintingDiagnosticConsumer.h" + +using llvm::Error; +using llvm::Expected; + +namespace lldb_private { +class Debugger; +} + +namespace lldb_private { +class SwiftInterpreter { +public: + SwiftInterpreter(); + + Error executeOneLine(llvm::StringRef command, Debugger &debugger); + Error loadScriptFile(llvm::StringRef filename); + +private: + Error initJIT(); + Error initCompiler(); + Error initialize(); + + Expected createJITDylib(); + Error executeMemoryBuffer(std::unique_ptr code); + + bool m_inited = false; + int m_current_id = 0; + + std::unique_ptr m_jit = {}; + std::unique_ptr m_compiler_invocation = {}; + std::unique_ptr m_compiler_instance = {}; + std::vector> m_modules = {}; + + std::unique_ptr m_diagnostic_consumer = {}; + std::vector m_dylibs; +}; +} // namespace lldb_private + +#endif diff --git a/lldb/source/Plugins/TypeSystem/Swift/SwiftASTContext.h b/lldb/source/Plugins/TypeSystem/Swift/SwiftASTContext.h index 88d8bc8fac2b8..3bb87229a2e21 100644 --- a/lldb/source/Plugins/TypeSystem/Swift/SwiftASTContext.h +++ b/lldb/source/Plugins/TypeSystem/Swift/SwiftASTContext.h @@ -910,6 +910,7 @@ class SwiftASTContext : public TypeSystemSwift { SwiftEnumDescriptor *GetCachedEnumInfo(lldb::opaque_compiler_type_t type); friend class CompilerType; + friend class SwiftInterpreter; void ApplyDiagnosticOptions(); diff --git a/lldb/test/Shell/ScriptInterpreter/Swift/Inputs/sblist.swift b/lldb/test/Shell/ScriptInterpreter/Swift/Inputs/sblist.swift new file mode 100644 index 0000000000000..e19b13b2fb41b --- /dev/null +++ b/lldb/test/Shell/ScriptInterpreter/Swift/Inputs/sblist.swift @@ -0,0 +1,24 @@ +public struct GenericSBList : RandomAccessCollection { + let getElement: (Int) -> ElementType + public var startIndex: Int + public var endIndex: Int + + public init(count: Int, getElement: @escaping (Int) -> ElementType) { + startIndex = 0 + endIndex = count + self.getElement = getElement + } + + public init(slice: Range, of other: Self) { + startIndex = slice.lowerBound + endIndex = slice.upperBound + getElement = other.getElement + } + + @inline(__always) public func formIndex(after i: inout Int) { i += 1 } + @inline(__always) public func formIndex(before i: inout Int) { i -= 1 } + @inline(__always) public func index(i: Int, offsetBy: Int) -> Int { return i+offsetBy } + + @inline(__always) public subscript(i: Int) -> ElementType { getElement(i) } + @inline(__always) public subscript(range: Range) -> Self { Self(slice: range, of: self) } +} diff --git a/lldb/test/Shell/ScriptInterpreter/Swift/Inputs/sbtarget.swift b/lldb/test/Shell/ScriptInterpreter/Swift/Inputs/sbtarget.swift new file mode 100644 index 0000000000000..b834352201638 --- /dev/null +++ b/lldb/test/Shell/ScriptInterpreter/Swift/Inputs/sbtarget.swift @@ -0,0 +1,12 @@ +import LLDB + +public extension lldb.SBTarget { + + var modules: GenericSBList { + get { + GenericSBList(count: Int(self.GetNumModules())) { + var me = self; return me.GetModuleAtIndex(UInt32($0)) + } + } + } +} diff --git a/lldb/test/Shell/ScriptInterpreter/Swift/sanity_check.test b/lldb/test/Shell/ScriptInterpreter/Swift/sanity_check.test new file mode 100644 index 0000000000000..71d10cc8d3e4b --- /dev/null +++ b/lldb/test/Shell/ScriptInterpreter/Swift/sanity_check.test @@ -0,0 +1,12 @@ +# RUN: echo "int main() { return 0; }" | %clang_host -x c - -o %t +# RUN: %lldb -s %s --script-language swift -b %t 2>&1 | FileCheck %s +script -- print("hello \(40+2)") +# CHECK: hello 42 +command script import /Users/friss/dev/tmp/sblist.swift +# CHECK-NEXT: (lldb) command script import /Users/friss/dev/tmp/sblist.swift +command script import /Users/friss/dev/tmp/sbtarget.swift +# CHECK-NEXT: (lldb) command script import /Users/friss/dev/tmp/sbtarget.swift +script -- for module in lldb.target.modules { print("\(String(cString: module.GetUUIDString())) \(String(cString: module.GetFileSpec().GetFilename()))") } +# CHECK-NEXT: (lldb) script -- for module in lldb.target.modules { print("\(String(cString: module.GetUUIDString())) \(String(cString: module.GetFileSpec().GetFilename()))") } +# CHECK-NEXT: {{[-0-9A-F]+}} sanity_check.test.tmp +# CHECK-NEXT: {{[-0-9A-F]+}} dyld From db8771d865da1b94ef7f2cf75e09fd9cd48d3a44 Mon Sep 17 00:00:00 2001 From: Fred Riss Date: Thu, 11 Nov 2021 10:58:26 -0800 Subject: [PATCH 4/4] [LLDB] Fix ScriptInterpreter/Swift/sanity_check.test --- .../ScriptInterpreter/Swift/sanity_check.test | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/lldb/test/Shell/ScriptInterpreter/Swift/sanity_check.test b/lldb/test/Shell/ScriptInterpreter/Swift/sanity_check.test index 71d10cc8d3e4b..0852a9559535b 100644 --- a/lldb/test/Shell/ScriptInterpreter/Swift/sanity_check.test +++ b/lldb/test/Shell/ScriptInterpreter/Swift/sanity_check.test @@ -1,12 +1,19 @@ +# REQUIRES: system-darwin # RUN: echo "int main() { return 0; }" | %clang_host -x c - -o %t -# RUN: %lldb -s %s --script-language swift -b %t 2>&1 | FileCheck %s -script -- print("hello \(40+2)") +# RUN: %lldb -s %s --script-language swift \ +# RUN: -o "script -- print(\"hello \\(40+2)\")" \ +# RUN: -o "command script import %S/Inputs/sblist.swift" \ +# RUN: -o "command script import %S/Inputs/sbtarget.swift" \ +# RUN: -o "script -- \ +# RUN: for module in lldb.target.modules { \ +# RUN: print(\"\\(String(cString: module.GetUUIDString())) \ +# RUN: \\(String(cString: module.GetFileSpec().GetFilename()))\") \ +# RUN: }" \ +# RUN: -b %t 2>&1 | FileCheck %s + # CHECK: hello 42 -command script import /Users/friss/dev/tmp/sblist.swift -# CHECK-NEXT: (lldb) command script import /Users/friss/dev/tmp/sblist.swift -command script import /Users/friss/dev/tmp/sbtarget.swift -# CHECK-NEXT: (lldb) command script import /Users/friss/dev/tmp/sbtarget.swift -script -- for module in lldb.target.modules { print("\(String(cString: module.GetUUIDString())) \(String(cString: module.GetFileSpec().GetFilename()))") } +# CHECK-NEXT: (lldb) command script import {{.*}}/Inputs/sblist.swift +# CHECK-NEXT: (lldb) command script import {{.*}}/Inputs/sbtarget.swift # CHECK-NEXT: (lldb) script -- for module in lldb.target.modules { print("\(String(cString: module.GetUUIDString())) \(String(cString: module.GetFileSpec().GetFilename()))") } # CHECK-NEXT: {{[-0-9A-F]+}} sanity_check.test.tmp # CHECK-NEXT: {{[-0-9A-F]+}} dyld