mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Correct format: //===--- Name of file - Description ----------------------------*- Lang -*-===//
346 lines
12 KiB
C++
346 lines
12 KiB
C++
//===--- 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 http://swift.org/LICENSE.txt for license information
|
|
// See http://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/Frontend/Frontend.h"
|
|
#include "swift/SILOptimizer/PassManager/Passes.h"
|
|
#include "swift/Basic/LLVM.h"
|
|
#include "llvm/ADT/SmallString.h"
|
|
#include "llvm/Config/config.h"
|
|
#include "llvm/ExecutionEngine/MCJIT.h"
|
|
#include "llvm/IR/DiagnosticPrinter.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"
|
|
|
|
#include <dlfcn.h>
|
|
|
|
using namespace swift;
|
|
using namespace swift::immediate;
|
|
|
|
static bool loadRuntimeLib(StringRef sharedLibName, StringRef runtimeLibPath) {
|
|
// FIXME: Need error-checking.
|
|
llvm::SmallString<128> Path = runtimeLibPath;
|
|
llvm::sys::path::append(Path, sharedLibName);
|
|
return dlopen(Path.c_str(), RTLD_LAZY | RTLD_GLOBAL);
|
|
}
|
|
|
|
bool 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 dlopen(path.c_str(), RTLD_LAZY | RTLD_GLOBAL);
|
|
}
|
|
|
|
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 = dlopen(path.c_str(), RTLD_LAZY | RTLD_GLOBAL);
|
|
if (success)
|
|
break;
|
|
}
|
|
|
|
// Let dlopen determine the best search paths.
|
|
if (!success)
|
|
success = dlopen(stem.c_str(), RTLD_LAZY | RTLD_GLOBAL);
|
|
|
|
// 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 = dlopen(path.c_str(), RTLD_LAZY | RTLD_GLOBAL);
|
|
if (success)
|
|
break;
|
|
}
|
|
|
|
// If that fails, let dlopen search for system frameworks.
|
|
if (!success)
|
|
success = dlopen(frameworkPart.c_str(), RTLD_LAZY | RTLD_GLOBAL);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
bool swift::immediate::tryLoadLibraries(ArrayRef<LinkLibrary> LinkLibraries,
|
|
SearchPathOptions SearchPathOpts,
|
|
DiagnosticEngine &Diags) {
|
|
SmallVector<bool, 4> 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 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");
|
|
|
|
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";
|
|
}
|
|
|
|
bool swift::immediate::linkLLVMModules(llvm::Module *Module,
|
|
llvm::Module *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);
|
|
// TODO: reactivate the linker mode if it is
|
|
// supported in llvm again. Otherwise remove the
|
|
// commented code completely.
|
|
bool Failed = llvm::Linker::LinkModules(Module, SubModule /*, LinkerMode*/);
|
|
Ctx.setDiagnosticHandler(OldHandler, OldDiagnosticContext);
|
|
|
|
return !Failed;
|
|
}
|
|
|
|
bool swift::immediate::IRGenImportedModules(
|
|
CompilerInstance &CI,
|
|
llvm::Module &Module,
|
|
llvm::SmallPtrSet<swift::Module *, 8> &ImportedModules,
|
|
SmallVectorImpl<llvm::Function*> &InitFns,
|
|
IRGenOptions &IRGenOpts,
|
|
const SILOptions &SILOpts) {
|
|
swift::Module *M = CI.getMainModule();
|
|
|
|
// Perform autolinking.
|
|
SmallVector<LinkLibrary, 4> 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<SILModule> 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, SILMod.get(),
|
|
import->getName().str(),
|
|
llvm::getGlobalContext());
|
|
|
|
if (CI.getASTContext().hadError()) {
|
|
hadError = true;
|
|
break;
|
|
}
|
|
|
|
if (!linkLLVMModules(&Module, SubModule.get()
|
|
// 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<char, 20> 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.getSILModule(), swiftModule->getName().str(),
|
|
llvm::getGlobalContext());
|
|
auto *Module = ModuleOwner.get();
|
|
|
|
if (Context.hadError())
|
|
return -1;
|
|
|
|
SmallVector<llvm::Function*, 8> InitFns;
|
|
llvm::SmallPtrSet<swift::Module *, 8> ImportedModules;
|
|
if (IRGenImportedModules(CI, *Module, ImportedModules, InitFns,
|
|
IRGenOpts, SILOpts))
|
|
return -1;
|
|
|
|
llvm::PassManagerBuilder PMBuilder;
|
|
PMBuilder.OptLevel = 2;
|
|
PMBuilder.Inliner = llvm::createFunctionInliningPass(200);
|
|
|
|
if (!loadSwiftRuntime(Context.SearchPathOpts.RuntimeLibraryPath)) {
|
|
CI.getDiags().diagnose(SourceLoc(),
|
|
diag::error_immediate_mode_missing_stdlib);
|
|
return -1;
|
|
}
|
|
|
|
// Build the ExecutionEngine.
|
|
llvm::EngineBuilder builder(std::move(ModuleOwner));
|
|
std::string ErrorMsg;
|
|
llvm::TargetOptions TargetOpt;
|
|
std::string CPU;
|
|
std::vector<std::string> 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, 0);
|
|
}
|
|
|
|
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, 0);
|
|
}
|