From 75f6dbd62c683c5f9d204f5acedccdd6b0c76bad Mon Sep 17 00:00:00 2001 From: Steven Wu Date: Thu, 11 Dec 2025 12:37:01 -0800 Subject: [PATCH] [swift][caching] Load swift module from CAS Switch loading swift module from CAS, instead of loading from file system. This allows more reliable loading and allows full distributed caching support that do not relies on the file system path. --- .../TypeSystem/Swift/SwiftASTContext.cpp | 83 ++++++++++++------- .../lang/swift/clangimporter/caching/Makefile | 5 ++ lldb/test/Shell/Swift/caching.test | 25 ++---- 3 files changed, 64 insertions(+), 49 deletions(-) diff --git a/lldb/source/Plugins/TypeSystem/Swift/SwiftASTContext.cpp b/lldb/source/Plugins/TypeSystem/Swift/SwiftASTContext.cpp index 2ea221d266a4a..84ddc1932ae79 100644 --- a/lldb/source/Plugins/TypeSystem/Swift/SwiftASTContext.cpp +++ b/lldb/source/Plugins/TypeSystem/Swift/SwiftASTContext.cpp @@ -1923,8 +1923,8 @@ void SwiftASTContext::AddExtraClangCC1Args( const std::vector> framework_search_paths, std::vector &dest) { clang::CompilerInvocation invocation; - std::vector default_paths = {"/usr/include", - "/user/local/include"}; + std::vector default_paths = { + "/usr/include", "/usr/local/include", "/usr/lib/swift/shims"}; llvm::SmallVector clangArgs; clangArgs.reserve(source.size() + module_search_paths.size() * 2 + framework_search_paths.size() * 2 + @@ -1980,34 +1980,42 @@ void SwiftASTContext::AddExtraClangCC1Args( invocation.getHeaderSearchOpts().ModuleCachePath = GetCompilerInvocation().getClangModuleCachePath().str(); - bool use_cas_module = m_cas && m_action_cache; - if (use_cas_module) { - // Load from CAS. - invocation.getCASOpts().CASPath = GetCASOptions().CASOpts.CASPath; - invocation.getCASOpts().PluginPath = GetCASOptions().CASOpts.PluginPath; - invocation.getCASOpts().PluginOptions = - GetCASOptions().CASOpts.PluginOptions; - - use_cas_module = llvm::all_of( - invocation.getFrontendOpts().ModuleCacheKeys, [&](const auto &entry) { - bool exist = IsModuleAvailableInCAS(entry.second); - if (!exist) - HEALTH_LOG_PRINTF("module '%s' cannot be load " - "from CAS using key: %s, fallback to " - "load from file system", - entry.first.c_str(), entry.second.c_str()); - return exist; - }); - } - - if (!use_cas_module) { - // Clear module cache key and other CAS options to load modules from disk - // directly. + auto ClearCASOptions = [&]() { invocation.getFrontendOpts().ModuleCacheKeys.clear(); invocation.getCASOpts() = clang::CASOptions(); - - // Ignore CAS info inside modules when loading. invocation.getFrontendOpts().ModuleLoadIgnoreCAS = true; + }; + + bool use_cas_modules = m_cas && m_action_cache && + !invocation.getFrontendOpts().ModuleCacheKeys.empty(); + if (use_cas_modules) { + if (llvm::all_of(invocation.getFrontendOpts().ModuleCacheKeys, + [&](const auto &entry) { + if (!IsModuleAvailableInCAS(entry.second)) { + std::string warn; + llvm::raw_string_ostream(warn) + << "Failed to load module '" << entry.first + << "' from CAS using key: " << entry.second; + AddDiagnostic(eSeverityWarning, warn); + return false; + } + return true; + })) { + // Load from CAS. + invocation.getCASOpts().CASPath = GetCASOptions().CASOpts.CASPath; + invocation.getCASOpts().PluginPath = GetCASOptions().CASOpts.PluginPath; + invocation.getCASOpts().PluginOptions = + GetCASOptions().CASOpts.PluginOptions; + } else { + // Failed to load from CAS. + ClearCASOptions(); + invocation.getHeaderSearchOpts().PrebuiltModuleFiles.clear(); + invocation.getFrontendOpts().ModuleFiles.clear(); + } + } else { + // Clear module cache key and other CAS options to load modules from disk + // directly. + ClearCASOptions(); // Remove non-existing modules in a systematic way. auto CheckFileExists = [&](const std::string &file) -> bool { @@ -3851,12 +3859,27 @@ ThreadSafeASTContext SwiftASTContext::GetASTContext() { // 2. Create the explicit swift module loader. if (props.GetUseSwiftExplicitModuleLoader()) { auto &search_path_opts = GetCompilerInvocation().getSearchPathOptions(); - std::unique_ptr esml_up = - swift::ExplicitSwiftModuleLoader::create( - *m_ast_context_up, m_dependency_tracker.get(), loading_mode, + std::unique_ptr esml_up = nullptr; + if (m_cas && m_action_cache) { + if (auto casid = m_cas->parseID(search_path_opts.ExplicitSwiftModuleMapPath)) { + esml_up = swift::ExplicitCASModuleLoader::create( + *m_ast_context_up, *m_cas, *m_action_cache, + m_dependency_tracker.get(), loading_mode, search_path_opts.ExplicitSwiftModuleMapPath, search_path_opts.ExplicitSwiftModuleInputs, /*IgnoreSwiftSourceInfo*/ false); + } else { + // Not CASID, ignore error and treat as a file based moduel map. + llvm::consumeError(casid.takeError()); + } + } + if (!esml_up) { + esml_up = swift::ExplicitSwiftModuleLoader::create( + *m_ast_context_up, m_dependency_tracker.get(), loading_mode, + search_path_opts.ExplicitSwiftModuleMapPath, + search_path_opts.ExplicitSwiftModuleInputs, + /*IgnoreSwiftSourceInfo*/ false); + } if (esml_up) { m_explicit_swift_module_loader = static_cast(esml_up.get()); diff --git a/lldb/test/API/lang/swift/clangimporter/caching/Makefile b/lldb/test/API/lang/swift/clangimporter/caching/Makefile index a0efe4cc196aa..322a8c253d2ec 100644 --- a/lldb/test/API/lang/swift/clangimporter/caching/Makefile +++ b/lldb/test/API/lang/swift/clangimporter/caching/Makefile @@ -2,4 +2,9 @@ SWIFT_SOURCES := main.swift SWIFT_ENABLE_EXPLICIT_MODULES := YES SWIFTFLAGS_EXTRAS = -I$(SRCDIR) -I/TEST_DIR -F/FRAMEWORK_DIR -cache-compile-job -cas-path $(BUILDDIR)/cas +all: a.out cas-config + include Makefile.rules + +cas-config: + echo "{\"CASPath\": \"$(BUILDDIR)/cas\"}" > .cas-config diff --git a/lldb/test/Shell/Swift/caching.test b/lldb/test/Shell/Swift/caching.test index 2e0eaa5b4a670..9ea3005b1a0b9 100644 --- a/lldb/test/Shell/Swift/caching.test +++ b/lldb/test/Shell/Swift/caching.test @@ -8,18 +8,15 @@ # RUN: -cache-compile-job -cas-path %t/cas -explicit-module-build \ # RUN: -module-name main -o %t/main -# RUN: %lldb %t/main -s %t/lldb.script 2>&1 | FileCheck %s +# RUN: sed "s|DIR|%/t|g" %t/lldb.script.template > %t/lldb.script +# RUN: %lldb %t/main -s %t/lldb.script 2>&1 | FileCheck %s --check-prefix=CAS-LOAD --check-prefix=CHECK -## Setup CAS and try loading from CAS -# RUN: sed "s|DIR|%/t|g" %t/lldb.script.template > %t/lldb_2.script -# RUN: %lldb %t/main -s %t/lldb_2.script 2>&1 | FileCheck %s --check-prefix=CAS-LOAD --check-prefix=CHECK - -## Check fallback to file system. +## Check warning message # RUN: rm -rf %t/cas -# RUN: %lldb %t/main -s %t/lldb_2.script 2>&1 | FileCheck %s --check-prefix=CAS-FALLBACK --check-prefix=CHECK +# RUN: %lldb %t/main -s %t/lldb.script 2>&1 | FileCheck %s --check-prefix=CAS-FALLBACK --check-prefix=CHECK -# CAS-FALLBACK: operator()() -- module '{{.*}}' cannot be load from CAS using key -# CAS-FALLBACK-SAME: fallback to load from file system +# CAS-FALLBACK: warning: Failed to load module +# CAS-FALLBACK-SAME: from CAS using key: # CAS-LOAD: ConfigureCASStorage() -- Setup CAS from module list properties with cas path # CHECK: LogConfiguration() -- Extra clang arguments # CHECK-COUNT-1: LogConfiguration() -- -triple @@ -32,16 +29,6 @@ func test() { } test() -//--- lldb.script -# Force loading from interface to simulate no binary module available. -settings set symbols.swift-module-loading-mode prefer-interface -log enable lldb types -b test -run -# Create a SwiftASTContext -expr 1 -quit - //--- lldb.script.template # Force loading from interface to simulate no binary module available. settings set symbols.swift-module-loading-mode prefer-interface