diff --git a/include/swift/Subsystems.h b/include/swift/Subsystems.h index 38b095284c5..7e5da20a7e4 100644 --- a/include/swift/Subsystems.h +++ b/include/swift/Subsystems.h @@ -40,7 +40,8 @@ namespace swift { /// parsing after the next stmt-brace-item with side-effects. Returns /// the number of bytes parsed from the given buffer. bool parseIntoTranslationUnit(TranslationUnit *TU, unsigned BufferID, - unsigned *BufferOffset = 0); + unsigned *BufferOffset = 0, + unsigned BufferEndOffset = 0); /// performNameBinding - Once parsing is complete, this walks the AST to /// resolve names and do other top-level validation. StartElem indicates diff --git a/lib/IRGen/GenDecl.cpp b/lib/IRGen/GenDecl.cpp index dcbd61bb5a5..9fb09503502 100644 --- a/lib/IRGen/GenDecl.cpp +++ b/lib/IRGen/GenDecl.cpp @@ -48,7 +48,8 @@ static bool isTrivialGlobalInit(llvm::Function *fn) { } /// Emit all the top-level code in the translation unit. -void IRGenModule::emitTranslationUnit(TranslationUnit *tunit) { +void IRGenModule::emitTranslationUnit(TranslationUnit *tunit, + unsigned StartElem) { Type emptyTuple = TupleType::getEmpty(Context); FunctionType *unitToUnit = FunctionType::get(emptyTuple, emptyTuple, Context); Pattern *params[] = { @@ -72,7 +73,7 @@ void IRGenModule::emitTranslationUnit(TranslationUnit *tunit) { IRGenFunction(*this, unitToUnit, params, ExplosionKind::Minimal, /*uncurry*/ 0, fn) - .emitGlobalTopLevel(tunit->Body); + .emitGlobalTopLevel(tunit->Body, StartElem); // We don't need global init to call main(). if (tunit->IsMainModule) @@ -105,9 +106,10 @@ void IRGenModule::emitTranslationUnit(TranslationUnit *tunit) { "llvm.global_ctors"); } -void IRGenFunction::emitGlobalTopLevel(BraceStmt *body) { - for (auto elt : body->getElements()) { +void IRGenFunction::emitGlobalTopLevel(BraceStmt *body, unsigned StartElem) { + for (unsigned i = StartElem, e = body->getNumElements(); i != e; ++i) { assert(Builder.hasValidIP()); + auto elt = body->getElement(i); if (Decl *D = elt.dyn_cast()) { emitGlobalDecl(D); } else if (Expr *E = elt.dyn_cast()) { diff --git a/lib/IRGen/IRGen.cpp b/lib/IRGen/IRGen.cpp index 431d50e562e..f3e0218d094 100644 --- a/lib/IRGen/IRGen.cpp +++ b/lib/IRGen/IRGen.cpp @@ -102,30 +102,34 @@ void swift::performIRGeneration(Options &Opts, llvm::Module *Module, // Emit the translation unit. IRGenModule IRM(TU->Ctx, Opts, *Module, *TargetData); - IRM.emitTranslationUnit(TU); + IRM.emitTranslationUnit(TU, StartElem); // Bail out if there are any errors. if (TU->Ctx.hadError()) return; - // Try to open the output file. Clobbering an existing file is fine. - // Open in binary mode if we're doing binary output. - unsigned OSFlags = 0; - if (isBinaryOutput(Opts.OutputKind)) - OSFlags |= raw_fd_ostream::F_Binary; - raw_fd_ostream RawOS(Opts.OutputFilename.c_str(), Error, OSFlags); - if (RawOS.has_error()) { - TU->Ctx.Diags.diagnose(SourceLoc(), diag::error_opening_output, - Opts.OutputFilename, Error); - return; + llvm::OwningPtr RawOS; + formatted_raw_ostream FormattedOS; + if (!Opts.OutputFilename.empty()) { + // Try to open the output file. Clobbering an existing file is fine. + // Open in binary mode if we're doing binary output. + unsigned OSFlags = 0; + if (isBinaryOutput(Opts.OutputKind)) + OSFlags |= raw_fd_ostream::F_Binary; + RawOS.reset(new raw_fd_ostream(Opts.OutputFilename.c_str(), + Error, OSFlags)); + if (RawOS->has_error()) { + TU->Ctx.Diags.diagnose(SourceLoc(), diag::error_opening_output, + Opts.OutputFilename, Error); + return; + } + + // Most output kinds want a formatted output stream. It's not clear + // why writing an object file does. + if (Opts.OutputKind != OutputKind::LLVMBitcode) + FormattedOS.setStream(*RawOS, formatted_raw_ostream::PRESERVE_STREAM); } - // Most output kinds want a formatted output stream. It's not clear - // why writing an object file does. - formatted_raw_ostream FormattedOS; - if (Opts.OutputKind != OutputKind::LLVMBitcode) - FormattedOS.setStream(RawOS, formatted_raw_ostream::PRESERVE_STREAM); - - // Okay, set up a pipeline. + // Set up a pipeline. PassManagerBuilder PMBuilder; PMBuilder.OptLevel = Opts.OptLevel; @@ -156,7 +160,7 @@ void swift::performIRGeneration(Options &Opts, llvm::Module *Module, ModulePasses.add(createPrintModulePass(&FormattedOS)); break; case OutputKind::LLVMBitcode: - ModulePasses.add(createBitcodeWriterPass(RawOS)); + ModulePasses.add(createBitcodeWriterPass(*RawOS)); break; case OutputKind::NativeAssembly: case OutputKind::ObjectFile: { diff --git a/lib/IRGen/IRGenFunction.h b/lib/IRGen/IRGenFunction.h index 67930ad03cc..163ebb748f8 100644 --- a/lib/IRGen/IRGenFunction.h +++ b/lib/IRGen/IRGenFunction.h @@ -315,7 +315,7 @@ private: //--- Global context emission -------------------------------------------------- public: - void emitGlobalTopLevel(BraceStmt *S); + void emitGlobalTopLevel(BraceStmt *S, unsigned StartElem); private: void emitGlobalDecl(Decl *D); }; diff --git a/lib/IRGen/IRGenModule.h b/lib/IRGen/IRGenModule.h index 8a0f610bf4b..97bb01b694a 100644 --- a/lib/IRGen/IRGenModule.h +++ b/lib/IRGen/IRGenModule.h @@ -138,7 +138,7 @@ public: llvm::LLVMContext &getLLVMContext() const { return LLVMContext; } - void emitTranslationUnit(TranslationUnit *TU); + void emitTranslationUnit(TranslationUnit *TU, unsigned StartElem); void emitOneOfType(OneOfType *type); void emitExtension(ExtensionDecl *D); diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index e7221202db6..23aa97fa553 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -44,9 +44,11 @@ namespace { /// parseTranslationUnit - Entrypoint for the parser. bool swift::parseIntoTranslationUnit(TranslationUnit *TU, unsigned BufferID, - unsigned *BufferOffset) { + unsigned *BufferOffset, + unsigned BufferEndOffset) { Parser P(BufferID, TU->getComponent(), TU->Ctx, - BufferOffset ? *BufferOffset : 0, TU->IsMainModule); + BufferOffset ? *BufferOffset : 0, BufferEndOffset, + TU->IsMainModule); PrettyStackTraceParser stackTrace(P); P.parseTranslationUnit(TU); if (BufferOffset) @@ -62,9 +64,9 @@ bool swift::parseIntoTranslationUnit(TranslationUnit *TU, /// ComputeLexStart - Compute the start of lexing; if there's an offset, take /// that into account. If there's a #! line in a main module, ignore it. static StringRef ComputeLexStart(StringRef File, unsigned Offset, - bool IsMainModule) { - if (Offset) - return File.substr(Offset); + unsigned EndOffset, bool IsMainModule) { + if (Offset || EndOffset) + return File.slice(Offset, EndOffset); if (IsMainModule && File.startswith("#!")) { StringRef::size_type Pos = File.find_first_of("\n\r"); @@ -77,11 +79,12 @@ static StringRef ComputeLexStart(StringRef File, unsigned Offset, Parser::Parser(unsigned BufferID, swift::Component *Comp, ASTContext &Context, - unsigned Offset, bool IsMainModule) + unsigned Offset, unsigned EndOffset, bool IsMainModule) : SourceMgr(Context.SourceMgr), Diags(Context.Diags), Buffer(SourceMgr.getMemoryBuffer(BufferID)), - L(*new Lexer(ComputeLexStart(Buffer->getBuffer(), Offset, IsMainModule), + L(*new Lexer(ComputeLexStart(Buffer->getBuffer(), Offset, EndOffset, + IsMainModule), SourceMgr, &Diags)), Component(Comp), Context(Context), diff --git a/lib/Parse/Parser.h b/lib/Parse/Parser.h index f09c5b5fac6..923f1402652 100644 --- a/lib/Parse/Parser.h +++ b/lib/Parse/Parser.h @@ -91,7 +91,7 @@ public: Parser(unsigned BufferID, swift::Component *Component, ASTContext &Ctx, - unsigned Offset, bool IsMainModule); + unsigned Offset, unsigned EndOffset, bool IsMainModule); ~Parser(); //===--------------------------------------------------------------------===// diff --git a/tools/swift/Frontend.cpp b/tools/swift/Frontend.cpp index 8569f28fae3..bd17b5fb09c 100644 --- a/tools/swift/Frontend.cpp +++ b/tools/swift/Frontend.cpp @@ -74,12 +74,14 @@ swift::buildSingleTranslationUnit(ASTContext &Context, unsigned BufferID, return TU; } -void swift::appendToMainTranslationUnit(TranslationUnit *TU, unsigned BufferID) { - unsigned BufferOffset = 0; - unsigned CurTUElem = TU->Body->getNumElements(); +void swift::appendToMainTranslationUnit(TranslationUnit *TU, unsigned BufferID, + unsigned &BufferOffset, + unsigned BufferEndOffset) { + unsigned CurTUElem = TU->Body ? TU->Body->getNumElements() : 0; bool CompleteParse; do { - CompleteParse = parseIntoTranslationUnit(TU, BufferID, &BufferOffset); + CompleteParse = parseIntoTranslationUnit(TU, BufferID, &BufferOffset, + BufferEndOffset); performNameBinding(TU, CurTUElem); performTypeChecking(TU, CurTUElem); CurTUElem = TU->Body->getNumElements(); diff --git a/tools/swift/Frontend.h b/tools/swift/Frontend.h index 8fd9ce80e57..9bbf8571113 100644 --- a/tools/swift/Frontend.h +++ b/tools/swift/Frontend.h @@ -24,5 +24,7 @@ namespace swift { bool ParseOnly, bool IsMainModule); - void appendToMainTranslationUnit(TranslationUnit *TU, unsigned BufferID); + void appendToMainTranslationUnit(TranslationUnit *TU, unsigned BufferID, + unsigned &BufferOffset, + unsigned BufferEndOffset); } diff --git a/tools/swift/Immediate.cpp b/tools/swift/Immediate.cpp index 1c08b651782..e706bee3b93 100644 --- a/tools/swift/Immediate.cpp +++ b/tools/swift/Immediate.cpp @@ -16,38 +16,54 @@ //===----------------------------------------------------------------------===// #include "Immediate.h" +#include "Frontend.h" #include "swift/Subsystems.h" #include "swift/IRGen/Options.h" #include "swift/AST/ASTContext.h" #include "swift/AST/Component.h" #include "swift/AST/Diagnostics.h" #include "swift/AST/Module.h" +#include "swift/AST/Stmt.h" #include "swift/Basic/DiagnosticConsumer.h" #include "llvm/ADT/SmallPtrSet.h" -#include "llvm/ExecutionEngine/MCJIT.h" +#include "llvm/ExecutionEngine/JIT.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Host.h" +#include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Support/Signals.h" +#include "llvm/Support/SourceMgr.h" #include "llvm/Support/system_error.h" #include "llvm/Support/TargetSelect.h" #include "llvm/Linker.h" #include "llvm/LLVMContext.h" #include "llvm/Module.h" +#include #include +static void LoadSwiftRuntime() { + // FIXME: Need error-checking. + llvm::sys::Path LibPath = + llvm::sys::Path::GetMainExecutable(0, (void*)&swift::RunImmediately); + LibPath.eraseComponent(); + LibPath.eraseComponent(); + LibPath.appendComponent("lib"); + LibPath.appendComponent("libswift_abi.dylib"); + dlopen(LibPath.c_str(), 0); +} + void swift::RunImmediately(TranslationUnit *TU) { ASTContext &Context = TU->Ctx; irgen::Options Options; - Options.OutputFilename = TU->Name.str(); + Options.OutputFilename = ""; Options.Triple = llvm::sys::getDefaultTargetTriple(); Options.OptLevel = 0; Options.OutputKind = irgen::OutputKind::Module; // IRGen the main module. llvm::LLVMContext LLVMContext; - llvm::Module Module(Options.OutputFilename, LLVMContext); + llvm::Module Module(TU->Name.str(), LLVMContext); performCaptureAnalysis(TU); performIRGeneration(Options, &Module, TU); @@ -82,15 +98,7 @@ void swift::RunImmediately(TranslationUnit *TU) { } } - // Load the swift runtime. - // FIXME: Need error-checking. - llvm::sys::Path LibPath = - llvm::sys::Path::GetMainExecutable(0, (void*)&RunImmediately); - LibPath.eraseComponent(); - LibPath.eraseComponent(); - LibPath.appendComponent("lib"); - LibPath.appendComponent("libswift_abi.dylib"); - dlopen(LibPath.c_str(), 0); + LoadSwiftRuntime(); // Run the generated program. @@ -101,12 +109,133 @@ void swift::RunImmediately(TranslationUnit *TU) { std::string ErrorMsg; builder.setErrorStr(&ErrorMsg); builder.setEngineKind(llvm::EngineKind::JIT); - builder.setUseMCJIT(true); llvm::ExecutionEngine *EE = builder.create(); EE->runFunctionAsMain(EntryFn, std::vector(), 0); } +struct InitEditLine { + EditLine *e; + InitEditLine() { + e = el_init("swift", stdin, stdout, stderr); + el_set(e, EL_EDITOR, "emacs"); + char *(*PromptFn)(EditLine *) = + [](EditLine *e) { return (char*)"swift> "; }; + el_set(e, EL_PROMPT, PromptFn); + } + ~InitEditLine() { + el_end(e); + } + operator EditLine*() { return e; } +}; + +void swift::REPL(ASTContext &Context) { + // FIXME: We should do something a bit more elaborate than + // "allocate a 1MB buffer and hope it's enough". + llvm::MemoryBuffer *Buffer = + llvm::MemoryBuffer::getNewMemBuffer(1 << 20, ""); + + Component *Comp = new (Context.Allocate(1)) Component(); + unsigned BufferID = + Context.SourceMgr.AddNewSourceBuffer(Buffer, llvm::SMLoc()); + Identifier ID = Context.getIdentifier("REPL"); + TranslationUnit *TU = new (Context) TranslationUnit(ID, Comp, Context, + /*IsMainModule=*/true); + + llvm::SmallPtrSet ImportedModules; + llvm::LLVMContext LLVMContext; + llvm::Module Module("REPL", LLVMContext); + + LoadSwiftRuntime(); + + irgen::Options Options; + Options.OutputFilename = ""; + Options.Triple = llvm::sys::getDefaultTargetTriple(); + Options.OptLevel = 0; + Options.OutputKind = irgen::OutputKind::Module; + + char* CurBuffer = const_cast(Buffer->getBufferStart()); + unsigned CurBufferOffset = 0; + unsigned CurBufferEndOffset = 0; + + InitEditLine e; + + while (1) { + unsigned CurTUElem = TU->Body ? TU->Body->getNumElements() : 0; + + int LineCount; + const char* Line = el_gets(e, &LineCount); + if (!Line) + return; + + // FIXME: Should continue reading lines when there is an unbalanced + // brace/paren/bracket. + + strcpy(CurBuffer, Line); + CurBuffer += strlen(Line); + *CurBuffer++ = '\n'; + CurBufferEndOffset += strlen(Line) + 1; + + swift::appendToMainTranslationUnit(TU, BufferID, CurBufferOffset, + CurBufferEndOffset); + + // FIXME: Better error recovery would be really nice here. + if (Context.hadError()) + return; + + // IRGen the main module. + performCaptureAnalysis(TU, CurTUElem); + performIRGeneration(Options, &Module, TU, CurTUElem); + + if (Context.hadError()) + return; + + // IRGen the modules this module depends on. + for (auto ModPair : TU->getImportedModules()) { + if (isa(ModPair.second)) + continue; + + TranslationUnit *SubTU = cast(ModPair.second); + if (!ImportedModules.insert(SubTU)) + continue; + + // FIXME: Need to check whether this is actually safe in general. + llvm::Module SubModule(SubTU->Name.str(), LLVMContext); + performCaptureAnalysis(SubTU); + performIRGeneration(Options, &SubModule, SubTU); + + if (Context.hadError()) + return; + + std::string ErrorMessage; + if (llvm::Linker::LinkModules(&Module, &SubModule, + llvm::Linker::DestroySource, + &ErrorMessage)) { + llvm::errs() << "Error linking swift modules\n"; + llvm::errs() << ErrorMessage << "\n"; + return; + } + } + + // The way we do this is really ugly... we should be able to improve this. + llvm::Function *EntryFn = Module.getFunction("main"); + + { + llvm::EngineBuilder builder(&Module); + std::string ErrorMsg; + builder.setErrorStr(&ErrorMsg); + builder.setEngineKind(llvm::EngineKind::JIT); + + llvm::ExecutionEngine *EE = builder.create(); + + EE->runFunctionAsMain(EntryFn, std::vector(), 0); + } + + EntryFn->eraseFromParent(); + } +} + + // FIXME: We shouldn't be writing implemenetations for functions in the swift // module in C, and this isn't really an ideal place to put those // implementations. @@ -118,7 +247,7 @@ extern "C" void _TSs5printFT3valNSs6Double_T_(double l) { printf("%f", l); } -extern "C" void __TSs9printCharFT9characterNSs5Int64_T_(int64_t l) { +extern "C" void _TSs9printCharFT9characterNSs5Int64_T_(int64_t l) { printf("%c", (char)l); } @@ -130,6 +259,6 @@ extern "C" bool _TNSs4Bool13getLogicValuefRS_FT_i1(bool* b) { return *b; } -extern "C" void _TSs4exitFT8exitCodeNSs5int64_T_(int64_t l) { +extern "C" void _TSs4exitFT8exitCodeNSs5Int64_T_(int64_t l) { exit(l); } diff --git a/tools/swift/Immediate.h b/tools/swift/Immediate.h index b16a650c24e..4146df592c7 100644 --- a/tools/swift/Immediate.h +++ b/tools/swift/Immediate.h @@ -18,7 +18,9 @@ #include namespace swift { + class ASTContext; class TranslationUnit; void RunImmediately(TranslationUnit *TU); + void REPL(ASTContext &Context); } diff --git a/tools/swift/Makefile b/tools/swift/Makefile index 9e46d5c8848..686f887e308 100644 --- a/tools/swift/Makefile +++ b/tools/swift/Makefile @@ -20,10 +20,11 @@ TOOLNAME = swift include $(SWIFT_LEVEL)/../../Makefile.config # $(TARGETS_TO_BUILD) and ipo are necessary for code generation. bitwriter is -# necessary to write out a .bc file. mcjit, jit, and linker are used for +# necessary to write out a .bc file. jit and linker are used for # JIT execution. -LINK_COMPONENTS := $(TARGETS_TO_BUILD) bitwriter ipo mcjit jit linker +LINK_COMPONENTS := $(TARGETS_TO_BUILD) bitwriter ipo jit linker USEDLIBS = swiftIRGen.a swiftParse.a swiftSema.a swiftAST.a swiftBasic.a +LLVMLibsOptions := $(LLVMLibsOptions) -ledit include $(SWIFT_LEVEL)/Makefile