mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
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:
@@ -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
|
||||
|
||||
@@ -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*>()) {
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -315,7 +315,7 @@ private:
|
||||
|
||||
//--- Global context emission --------------------------------------------------
|
||||
public:
|
||||
void emitGlobalTopLevel(BraceStmt *S);
|
||||
void emitGlobalTopLevel(BraceStmt *S, unsigned StartElem);
|
||||
private:
|
||||
void emitGlobalDecl(Decl *D);
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -91,7 +91,7 @@ public:
|
||||
|
||||
|
||||
Parser(unsigned BufferID, swift::Component *Component, ASTContext &Ctx,
|
||||
unsigned Offset, bool IsMainModule);
|
||||
unsigned Offset, unsigned EndOffset, bool IsMainModule);
|
||||
~Parser();
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -18,7 +18,9 @@
|
||||
#include <string>
|
||||
|
||||
namespace swift {
|
||||
class ASTContext;
|
||||
class TranslationUnit;
|
||||
|
||||
void RunImmediately(TranslationUnit *TU);
|
||||
void REPL(ASTContext &Context);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user