//===--- Immediate.cpp - the swift immediate mode -------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// // // This is the implementation of the swift interpreter, which takes a // source file and JITs it. // //===----------------------------------------------------------------------===// #define DEBUG_TYPE "swift-immediate" #include "swift/Immediate/Immediate.h" #include "ImmediateImpl.h" #include "swift/Subsystems.h" #include "swift/AST/ASTContext.h" #include "swift/AST/DiagnosticsFrontend.h" #include "swift/AST/IRGenOptions.h" #include "swift/AST/Module.h" #include "swift/Basic/LLVM.h" #include "swift/Basic/LLVMContext.h" #include "swift/Frontend/Frontend.h" #include "swift/SILOptimizer/PassManager/Passes.h" #include "llvm/ADT/SmallString.h" #include "llvm/Config/config.h" #include "llvm/ExecutionEngine/MCJIT.h" #include "llvm/IR/DiagnosticPrinter.h" #include "llvm/IR/DiagnosticInfo.h" #include "llvm/IR/LLVMContext.h" #include "llvm/Linker/Linker.h" #include "llvm/Transforms/IPO.h" #include "llvm/Transforms/IPO/PassManagerBuilder.h" #include "llvm/Support/Path.h" #if defined(_WIN32) #define WIN32_LEAN_AND_MEAN #define NOMINMAX #include #else #include #endif using namespace swift; using namespace swift::immediate; static void *loadRuntimeLib(StringRef runtimeLibPathWithName) { #if defined(_WIN32) return LoadLibraryA(runtimeLibPathWithName.str().c_str()); #else return dlopen(runtimeLibPathWithName.str().c_str(), RTLD_LAZY | RTLD_GLOBAL); #endif } static void *loadRuntimeLib(StringRef sharedLibName, StringRef runtimeLibPath) { // FIXME: Need error-checking. llvm::SmallString<128> Path = runtimeLibPath; llvm::sys::path::append(Path, sharedLibName); return loadRuntimeLib(Path); } void *swift::immediate::loadSwiftRuntime(StringRef runtimeLibPath) { return loadRuntimeLib("libswiftCore" LTDL_SHLIB_EXT, runtimeLibPath); } static bool tryLoadLibrary(LinkLibrary linkLib, SearchPathOptions searchPathOpts) { llvm::SmallString<128> path = linkLib.getName(); // If we have an absolute or relative path, just try to load it now. if (llvm::sys::path::has_parent_path(path.str())) { return loadRuntimeLib(path); } bool success = false; switch (linkLib.getKind()) { case LibraryKind::Library: { llvm::SmallString<32> stem; if (llvm::sys::path::has_extension(path.str())) { stem = std::move(path); } else { // FIXME: Try the appropriate extension for the current platform? stem = "lib"; stem += path; stem += LTDL_SHLIB_EXT; } // Try user-provided library search paths first. for (auto &libDir : searchPathOpts.LibrarySearchPaths) { path = libDir; llvm::sys::path::append(path, stem.str()); success = loadRuntimeLib(path); if (success) break; } // Let loadRuntimeLib determine the best search paths. if (!success) success = loadRuntimeLib(stem); // If that fails, try our runtime library path. if (!success) success = loadRuntimeLib(stem, searchPathOpts.RuntimeLibraryPath); break; } case LibraryKind::Framework: { // If we have a framework, mangle the name to point to the framework // binary. llvm::SmallString<64> frameworkPart{std::move(path)}; frameworkPart += ".framework"; llvm::sys::path::append(frameworkPart, linkLib.getName()); // Try user-provided framework search paths first; frameworks contain // binaries as well as modules. for (auto &frameworkDir : searchPathOpts.FrameworkSearchPaths) { path = frameworkDir; llvm::sys::path::append(path, frameworkPart.str()); success = loadRuntimeLib(path); if (success) break; } // If that fails, let loadRuntimeLib search for system frameworks. if (!success) success = loadRuntimeLib(frameworkPart); break; } } return success; } bool swift::immediate::tryLoadLibraries(ArrayRef LinkLibraries, SearchPathOptions SearchPathOpts, DiagnosticEngine &Diags) { SmallVector LoadedLibraries; LoadedLibraries.append(LinkLibraries.size(), false); // Libraries are not sorted in the topological order of dependencies, and we // don't know the dependencies in advance. Try to load all libraries until // we stop making progress. bool HadProgress; do { HadProgress = false; for (unsigned i = 0; i != LinkLibraries.size(); ++i) { if (!LoadedLibraries[i] && tryLoadLibrary(LinkLibraries[i], SearchPathOpts)) { LoadedLibraries[i] = true; HadProgress = true; } } } while (HadProgress); return std::all_of(LoadedLibraries.begin(), LoadedLibraries.end(), [](bool Value) { return Value; }); } static void linkerDiagnosticHandlerNoCtx(const llvm::DiagnosticInfo &DI) { if (DI.getSeverity() != llvm::DS_Error) return; std::string MsgStorage; { llvm::raw_string_ostream Stream(MsgStorage); llvm::DiagnosticPrinterRawOStream DP(Stream); DI.print(DP); } llvm::errs() << "Error linking swift modules\n"; llvm::errs() << MsgStorage << "\n"; } static void linkerDiagnosticHandler(const llvm::DiagnosticInfo &DI, void *Context) { // This assert self documents our precondition that Context is always // nullptr. It seems that parts of LLVM are using the flexibility of having a // context. We don't really care about this. assert(Context == nullptr && "We assume Context is always a nullptr"); return linkerDiagnosticHandlerNoCtx(DI); } bool swift::immediate::linkLLVMModules(llvm::Module *Module, std::unique_ptr SubModule // TODO: reactivate the linker mode if it is // supported in llvm again. Otherwise remove the // commented code completely. /*, llvm::Linker::LinkerMode LinkerMode */) { llvm::LLVMContext &Ctx = SubModule->getContext(); auto OldHandler = Ctx.getDiagnosticHandler(); void *OldDiagnosticContext = Ctx.getDiagnosticContext(); Ctx.setDiagnosticHandler(linkerDiagnosticHandler, nullptr); bool Failed = llvm::Linker::linkModules(*Module, std::move(SubModule)); Ctx.setDiagnosticHandler(OldHandler, OldDiagnosticContext); return !Failed; } bool swift::immediate::IRGenImportedModules( CompilerInstance &CI, llvm::Module &Module, llvm::SmallPtrSet &ImportedModules, SmallVectorImpl &InitFns, IRGenOptions &IRGenOpts, const SILOptions &SILOpts) { swift::Module *M = CI.getMainModule(); // Perform autolinking. SmallVector AllLinkLibraries(IRGenOpts.LinkLibraries); auto addLinkLibrary = [&](LinkLibrary linkLib) { AllLinkLibraries.push_back(linkLib); }; M->forAllVisibleModules({}, /*includePrivateTopLevel=*/true, [&](Module::ImportedModule import) { import.second->collectLinkLibraries(addLinkLibrary); }); // Hack to handle thunks eagerly synthesized by the Clang importer. swift::Module *prev = nullptr; for (auto external : CI.getASTContext().ExternalDefinitions) { swift::Module *next = external->getModuleContext(); if (next == prev) continue; next->collectLinkLibraries(addLinkLibrary); prev = next; } tryLoadLibraries(AllLinkLibraries, CI.getASTContext().SearchPathOpts, CI.getDiags()); ImportedModules.insert(M); if (!CI.hasSourceImport()) return false; // IRGen the modules this module depends on. This is only really necessary // for imported source, but that's a very convenient thing to do in -i mode. // FIXME: Crawling all loaded modules is a hack. // FIXME: And re-doing SILGen, SIL-linking, SIL diagnostics, and IRGen is // expensive, because it's not properly being limited to new things right now. bool hadError = false; for (auto &entry : CI.getASTContext().LoadedModules) { swift::Module *import = entry.second; if (!ImportedModules.insert(import).second) continue; std::unique_ptr SILMod = performSILGeneration(import, CI.getSILOptions()); performSILLinking(SILMod.get()); if (runSILDiagnosticPasses(*SILMod)) { hadError = true; break; } // FIXME: We shouldn't need to use the global context here, but // something is persisting across calls to performIRGeneration. auto SubModule = performIRGeneration(IRGenOpts, import, std::move(SILMod), import->getName().str(), getGlobalLLVMContext()); if (CI.getASTContext().hadError()) { hadError = true; break; } if (!linkLLVMModules(&Module, std::move(SubModule) // TODO: reactivate the linker mode if it is // supported in llvm again. Otherwise remove the // commented code completely. /*, llvm::Linker::DestroySource */)) { hadError = true; break; } // FIXME: This is an ugly hack; need to figure out how this should // actually work. SmallVector NameBuf; StringRef InitFnName = (import->getName().str() + ".init").toStringRef(NameBuf); llvm::Function *InitFn = Module.getFunction(InitFnName); if (InitFn) InitFns.push_back(InitFn); } return hadError; } int swift::RunImmediately(CompilerInstance &CI, const ProcessCmdLine &CmdLine, IRGenOptions &IRGenOpts, const SILOptions &SILOpts) { ASTContext &Context = CI.getASTContext(); // IRGen the main module. auto *swiftModule = CI.getMainModule(); // FIXME: We shouldn't need to use the global context here, but // something is persisting across calls to performIRGeneration. auto ModuleOwner = performIRGeneration(IRGenOpts, swiftModule, CI.takeSILModule(), swiftModule->getName().str(), getGlobalLLVMContext()); auto *Module = ModuleOwner.get(); if (Context.hadError()) return -1; // Load libSwiftCore to setup process arguments. // // This must be done here, before any library loading has been done, to avoid // racing with the static initializers in user code. auto stdlib = loadSwiftRuntime(Context.SearchPathOpts.RuntimeLibraryPath); if (!stdlib) { CI.getDiags().diagnose(SourceLoc(), diag::error_immediate_mode_missing_stdlib); return -1; } // Setup interpreted process arguments. using ArgOverride = void (*)(const char **, int); #if defined(_WIN32) auto module = static_cast(stdlib); auto emplaceProcessArgs = reinterpret_cast( GetProcAddress(module, "_swift_stdlib_overrideUnsafeArgvArgc")); if (emplaceProcessArgs == nullptr) return -1; #else auto emplaceProcessArgs = (ArgOverride)dlsym(stdlib, "_swift_stdlib_overrideUnsafeArgvArgc"); if (dlerror()) return -1; #endif SmallVector argBuf; for (size_t i = 0; i < CmdLine.size(); ++i) { argBuf.push_back(CmdLine[i].c_str()); } argBuf.push_back(nullptr); (*emplaceProcessArgs)(argBuf.data(), CmdLine.size()); SmallVector InitFns; llvm::SmallPtrSet ImportedModules; if (IRGenImportedModules(CI, *Module, ImportedModules, InitFns, IRGenOpts, SILOpts)) return -1; llvm::PassManagerBuilder PMBuilder; PMBuilder.OptLevel = 2; PMBuilder.Inliner = llvm::createFunctionInliningPass(200); // Build the ExecutionEngine. llvm::EngineBuilder builder(std::move(ModuleOwner)); std::string ErrorMsg; llvm::TargetOptions TargetOpt; std::string CPU; std::vector Features; std::tie(TargetOpt, CPU, Features) = getIRTargetOptions(IRGenOpts, swiftModule->getASTContext()); builder.setRelocationModel(llvm::Reloc::PIC_); builder.setTargetOptions(TargetOpt); builder.setMCPU(CPU); builder.setMAttrs(Features); builder.setErrorStr(&ErrorMsg); builder.setEngineKind(llvm::EngineKind::JIT); llvm::ExecutionEngine *EE = builder.create(); if (!EE) { llvm::errs() << "Error loading JIT: " << ErrorMsg; return -1; } DEBUG(llvm::dbgs() << "Module to be executed:\n"; Module->dump()); EE->finalizeObject(); // Run the generated program. for (auto InitFn : InitFns) { DEBUG(llvm::dbgs() << "Running initialization function " << InitFn->getName() << '\n'); EE->runFunctionAsMain(InitFn, CmdLine, nullptr); } DEBUG(llvm::dbgs() << "Running static constructors\n"); EE->runStaticConstructorsDestructors(false); DEBUG(llvm::dbgs() << "Running main\n"); llvm::Function *EntryFn = Module->getFunction("main"); return EE->runFunctionAsMain(EntryFn, CmdLine, nullptr); }