Implement basic REPL under swift -repl. Known demo-blockers: need error recovery, need better brace/paren handling, need to implement the "print" part of REPL.

Swift SVN r1452
This commit is contained in:
Eli Friedman
2012-04-18 00:52:11 +00:00
parent ee0e03adc1
commit dc213bca76
12 changed files with 202 additions and 56 deletions

View File

@@ -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

View File

@@ -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<Decl*>()) {
emitGlobalDecl(D);
} else if (Expr *E = elt.dyn_cast<Expr*>()) {

View File

@@ -102,18 +102,22 @@ 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;
llvm::OwningPtr<raw_fd_ostream> 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;
raw_fd_ostream RawOS(Opts.OutputFilename.c_str(), Error, OSFlags);
if (RawOS.has_error()) {
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;
@@ -121,11 +125,11 @@ void swift::performIRGeneration(Options &Opts, llvm::Module *Module,
// 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);
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: {

View File

@@ -315,7 +315,7 @@ private:
//--- Global context emission --------------------------------------------------
public:
void emitGlobalTopLevel(BraceStmt *S);
void emitGlobalTopLevel(BraceStmt *S, unsigned StartElem);
private:
void emitGlobalDecl(Decl *D);
};

View File

@@ -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);

View File

@@ -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),

View File

@@ -91,7 +91,7 @@ public:
Parser(unsigned BufferID, swift::Component *Component, ASTContext &Ctx,
unsigned Offset, bool IsMainModule);
unsigned Offset, unsigned EndOffset, bool IsMainModule);
~Parser();
//===--------------------------------------------------------------------===//

View File

@@ -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();

View File

@@ -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);
}

View File

@@ -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 <histedit.h>
#include <dlfcn.h>
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<std::string>(), 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, "<REPL Buffer>");
Component *Comp = new (Context.Allocate<Component>(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<TranslationUnit*, 8> 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<char*>(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<BuiltinModule>(ModPair.second))
continue;
TranslationUnit *SubTU = cast<TranslationUnit>(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<std::string>(), 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);
}

View File

@@ -18,7 +18,9 @@
#include <string>
namespace swift {
class ASTContext;
class TranslationUnit;
void RunImmediately(TranslationUnit *TU);
void REPL(ASTContext &Context);
}

View File

@@ -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