From 887b59b7bea4d795ebe30655ac051f7872aa6a44 Mon Sep 17 00:00:00 2001
From: Zack Corr <zack@z0w0.me>
Date: Tue, 11 Sep 2012 16:05:51 +1000
Subject: [PATCH 1/5] jit: Separate JIT execution into two functions and load
 crates before main lookup

---
 src/rustc/back/link.rs       | 38 +++++++++++++++--
 src/rustc/lib/llvm.rs        | 20 +++++----
 src/rustllvm/RustWrapper.cpp | 81 +++++++++++++++++++++++++++---------
 src/rustllvm/rustllvm.def.in |  5 ++-
 4 files changed, 111 insertions(+), 33 deletions(-)

diff --git a/src/rustc/back/link.rs b/src/rustc/back/link.rs
index 4bbd51524c4..751f0369141 100644
--- a/src/rustc/back/link.rs
+++ b/src/rustc/back/link.rs
@@ -74,14 +74,44 @@ mod jit {
             m: ModuleRef,
             opt: c_int,
             stacks: bool) unsafe {
-        let ptr = llvm::LLVMRustJIT(rusti::morestack_addr(),
-                                    pm, m, opt, stacks);
+        let manager = llvm::LLVMRustPrepareJIT(rusti::morestack_addr());
 
-        if ptr::is_null(ptr) {
+        // We need to tell JIT where to resolve all linked
+        // symbols from. The equivalent of -lstd, -lcore, etc.
+        // By default the JIT will resolve symbols from the std and
+        // core linked into rustc. We don't want that,
+        // incase the user wants to use an older std library.
+
+        let cstore = sess.cstore;
+        for cstore::get_used_crate_files(cstore).each |cratepath| {
+            let path = cratepath.to_str();
+
+            debug!("linking: %s", path);
+
+            let _: () = str::as_c_str(
+                path,
+                |buf_t| {
+                    if !llvm::LLVMRustLoadCrate(manager, buf_t) {
+                        llvm_err(sess, ~"Could not link");
+                    }
+                    debug!("linked: %s", path);
+                });
+        }
+
+        // The execute function will return a void pointer
+        // to the _rust_main function. We can do closure
+        // magic here to turn it straight into a callable rust
+        // closure. It will also cleanup the memory manager
+        // for us.
+
+        let entry = llvm::LLVMRustExecuteJIT(manager,
+                                      pm, m, opt, stacks);
+
+        if ptr::is_null(entry) {
             llvm_err(sess, ~"Could not JIT");
         } else {
             let closure = Closure {
-                code: ptr,
+                code: entry,
                 env: ptr::null()
             };
             let func: fn(~[~str]) = cast::transmute(move closure);
diff --git a/src/rustc/lib/llvm.rs b/src/rustc/lib/llvm.rs
index 031ce219308..c16fe31f933 100644
--- a/src/rustc/lib/llvm.rs
+++ b/src/rustc/lib/llvm.rs
@@ -990,15 +990,19 @@ extern mod llvm {
         call. */
     fn LLVMRustGetLastError() -> *c_char;
 
-    /** Load a shared library to resolve symbols against. */
-    fn LLVMRustLoadLibrary(Filename: *c_char) -> bool;
+    /** Prepare the JIT. Returns a memory manager that can load crates. */
+    fn LLVMRustPrepareJIT(__morestack: *()) -> *();
 
-    /** Create and execute the JIT engine. */
-    fn LLVMRustJIT(__morestack: *(),
-                   PM: PassManagerRef,
-                   M: ModuleRef,
-                   OptLevel: c_int,
-                   EnableSegmentedStacks: bool) -> *();
+    /** Load a crate into the memory manager. */
+    fn LLVMRustLoadCrate(MM: *(),
+                         Filename: *c_char) -> bool;
+
+    /** Execute the JIT engine. */
+    fn LLVMRustExecuteJIT(MM: *(),
+                          PM: PassManagerRef,
+                          M: ModuleRef,
+                          OptLevel: c_int,
+                          EnableSegmentedStacks: bool) -> *();
 
     /** Parses the bitcode in the given memory buffer. */
     fn LLVMRustParseBitcode(MemBuf: MemoryBufferRef) -> ModuleRef;
diff --git a/src/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp
index 6ea433e6f1f..2def9c1c030 100644
--- a/src/rustllvm/RustWrapper.cpp
+++ b/src/rustllvm/RustWrapper.cpp
@@ -20,6 +20,7 @@
 #include "llvm/Transforms/Scalar.h"
 #include "llvm/Transforms/IPO.h"
 #include "llvm/ADT/Triple.h"
+#include "llvm/ADT/DenseSet.h"
 #include "llvm/Assembly/Parser.h"
 #include "llvm/Assembly/PrintModulePass.h"
 #include "llvm/Support/FormattedStream.h"
@@ -42,7 +43,6 @@
 #include "llvm-c/Core.h"
 #include "llvm-c/BitReader.h"
 #include "llvm-c/Object.h"
-#include <cstdlib>
 
 // Used by RustMCJITMemoryManager::getPointerToNamedFunction()
 // to get around glibc issues. See the function for more information.
@@ -53,6 +53,7 @@
 #endif
 
 using namespace llvm;
+using namespace llvm::sys;
 
 static const char *LLVMRustError;
 
@@ -100,18 +101,6 @@ void LLVMRustInitializeTargets() {
   LLVMInitializeX86AsmParser();
 }
 
-extern "C" bool
-LLVMRustLoadLibrary(const char* file) {
-  std::string err;
-
-  if(llvm::sys::DynamicLibrary::LoadLibraryPermanently(file, &err)) {
-    LLVMRustError = err.c_str();
-    return false;
-  }
-
-  return true;
-}
-
 // Custom memory manager for MCJITting. It needs special features
 // that the generic JIT memory manager doesn't entail. Based on
 // code from LLI, change where needed for Rust.
@@ -121,10 +110,13 @@ public:
   SmallVector<sys::MemoryBlock, 16> AllocatedCodeMem;
   SmallVector<sys::MemoryBlock, 16> FreeCodeMem;
   void* __morestack;
+  DenseSet<DynamicLibrary*> crates;
 
   RustMCJITMemoryManager(void* sym) : __morestack(sym) { }
   ~RustMCJITMemoryManager();
 
+  bool loadCrate(const char*, std::string*);
+
   virtual uint8_t *allocateCodeSection(uintptr_t Size, unsigned Alignment,
                                        unsigned SectionID);
 
@@ -197,6 +189,19 @@ public:
   }
 };
 
+bool RustMCJITMemoryManager::loadCrate(const char* file, std::string* err) {
+  DynamicLibrary crate = DynamicLibrary::getPermanentLibrary(file,
+                                                             err);
+
+  if(crate.isValid()) {
+    crates.insert(&crate);
+
+    return true;
+  }
+
+  return false;
+}
+
 uint8_t *RustMCJITMemoryManager::allocateDataSection(uintptr_t Size,
                                                     unsigned Alignment,
                                                     unsigned SectionID) {
@@ -276,6 +281,19 @@ void *RustMCJITMemoryManager::getPointerToNamedFunction(const std::string &Name,
   if (Name == "__morestack") return &__morestack;
 
   const char *NameStr = Name.c_str();
+
+  // Look through loaded crates for symbols.
+
+  for (DenseSet<DynamicLibrary*>::iterator I = crates.begin(),
+       E = crates.end(); I != E; ++I) {
+    void *Ptr = (*I)->getAddressOfSymbol(NameStr);
+
+    if (Ptr) return Ptr;
+  }
+
+  // Fallback to using any symbols LLVM has loaded (generally
+  // from the main program).
+
   void *Ptr = sys::DynamicLibrary::SearchForAddressOfSymbol(NameStr);
   if (Ptr) return Ptr;
 
@@ -293,11 +311,34 @@ RustMCJITMemoryManager::~RustMCJITMemoryManager() {
 }
 
 extern "C" void*
-LLVMRustJIT(void* __morestack,
-            LLVMPassManagerRef PMR,
-            LLVMModuleRef M,
-            CodeGenOpt::Level OptLevel,
-            bool EnableSegmentedStacks) {
+LLVMRustPrepareJIT(void* __morestack) {
+  // An execution engine will take ownership of this later
+  // and clean it up for us.
+
+  return (void*) new RustMCJITMemoryManager(__morestack);
+}
+
+extern "C" bool
+LLVMRustLoadCrate(void* mem, const char* crate) {
+  RustMCJITMemoryManager* manager = (RustMCJITMemoryManager*) mem;
+  std::string Err;
+
+  assert(manager);
+
+  if(!manager->loadCrate(crate, &Err)) {
+    LLVMRustError = Err.c_str();
+    return false;
+  }
+
+  return true;
+}
+
+extern "C" void*
+LLVMRustExecuteJIT(void* mem,
+                   LLVMPassManagerRef PMR,
+                   LLVMModuleRef M,
+                   CodeGenOpt::Level OptLevel,
+                   bool EnableSegmentedStacks) {
 
   InitializeNativeTarget();
   InitializeNativeTargetAsmPrinter();
@@ -308,6 +349,9 @@ LLVMRustJIT(void* __morestack,
   Options.NoFramePointerElim = true;
   Options.EnableSegmentedStacks = EnableSegmentedStacks;
   PassManager *PM = unwrap<PassManager>(PMR);
+  RustMCJITMemoryManager* MM = (RustMCJITMemoryManager*) mem;
+
+  assert(MM);
 
   PM->add(createBasicAliasAnalysisPass());
   PM->add(createInstructionCombiningPass());
@@ -318,7 +362,6 @@ LLVMRustJIT(void* __morestack,
   PM->add(createPromoteMemoryToRegisterPass());
   PM->run(*unwrap(M));
 
-  RustMCJITMemoryManager* MM = new RustMCJITMemoryManager(__morestack);
   ExecutionEngine* EE = EngineBuilder(unwrap(M))
     .setTargetOptions(Options)
     .setJITMemoryManager(MM)
diff --git a/src/rustllvm/rustllvm.def.in b/src/rustllvm/rustllvm.def.in
index 1de1e3ba58f..36833e5175e 100644
--- a/src/rustllvm/rustllvm.def.in
+++ b/src/rustllvm/rustllvm.def.in
@@ -4,8 +4,9 @@ LLVMRustWriteOutputFile
 LLVMRustGetLastError
 LLVMRustConstSmallInt
 LLVMRustConstInt
-LLVMRustLoadLibrary
-LLVMRustJIT
+LLVMRustLoadCrate
+LLVMRustPrepareJIT
+LLVMRustExecuteJIT
 LLVMRustParseBitcode
 LLVMRustParseAssemblyFile
 LLVMRustPrintPassTimings

From ebe6b2d15c3d007df93601fde99bb06f96c480c1 Mon Sep 17 00:00:00 2001
From: Zack Corr <zack@z0w0.me>
Date: Thu, 27 Sep 2012 12:57:19 +1000
Subject: [PATCH 2/5] jit: Enable exception handling

---
 src/rustllvm/RustWrapper.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp
index 2def9c1c030..d336b43313d 100644
--- a/src/rustllvm/RustWrapper.cpp
+++ b/src/rustllvm/RustWrapper.cpp
@@ -345,6 +345,7 @@ LLVMRustExecuteJIT(void* mem,
 
   std::string Err;
   TargetOptions Options;
+  Options.JITExceptionHandling = true;
   Options.JITEmitDebugInfo = true;
   Options.NoFramePointerElim = true;
   Options.EnableSegmentedStacks = EnableSegmentedStacks;

From ca4455666eaf09debf6fe769ba75f90453832981 Mon Sep 17 00:00:00 2001
From: Zack Corr <zack@z0w0.me>
Date: Thu, 27 Sep 2012 16:07:33 +1000
Subject: [PATCH 3/5] jit: Initialize native parser so rust-repl works

---
 src/rustllvm/RustWrapper.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp
index d336b43313d..72a97953a94 100644
--- a/src/rustllvm/RustWrapper.cpp
+++ b/src/rustllvm/RustWrapper.cpp
@@ -342,6 +342,7 @@ LLVMRustExecuteJIT(void* mem,
 
   InitializeNativeTarget();
   InitializeNativeTargetAsmPrinter();
+  InitializeNativeTargetAsmParser();
 
   std::string Err;
   TargetOptions Options;

From b3f418c10ed88163f6d1c6774b51eb69def521f8 Mon Sep 17 00:00:00 2001
From: Zack Corr <zack@z0w0.me>
Date: Fri, 28 Sep 2012 18:05:49 +1000
Subject: [PATCH 4/5] jit: Remove old crate loading code and don't search
 through loaded crates (use llvm default instead)

---
 src/rustc/back/link.rs       | 25 ++-----------------------
 src/rustllvm/RustWrapper.cpp | 13 ++-----------
 2 files changed, 4 insertions(+), 34 deletions(-)

diff --git a/src/rustc/back/link.rs b/src/rustc/back/link.rs
index 751f0369141..27b566ac28a 100644
--- a/src/rustc/back/link.rs
+++ b/src/rustc/back/link.rs
@@ -105,7 +105,7 @@ mod jit {
         // for us.
 
         let entry = llvm::LLVMRustExecuteJIT(manager,
-                                      pm, m, opt, stacks);
+                                             pm, m, opt, stacks);
 
         if ptr::is_null(entry) {
             llvm_err(sess, ~"Could not JIT");
@@ -223,30 +223,9 @@ mod write {
                 // JIT execution takes ownership of the module,
                 // so don't dispose and return.
 
-                // We need to tell LLVM where to resolve all linked
-                // symbols from. The equivalent of -lstd, -lcore, etc.
-                // By default the JIT will resolve symbols from the std and
-                // core linked into rustc. We don't want that,
-                // incase the user wants to use an older std library.
-                /*let cstore = sess.cstore;
-                for cstore::get_used_crate_files(cstore).each |cratepath| {
-                    debug!{"linking: %s", cratepath};
-
-                    let _: () = str::as_c_str(
-                        cratepath,
-                        |buf_t| {
-                            if !llvm::LLVMRustLoadLibrary(buf_t) {
-                                llvm_err(sess, ~"Could not link");
-                            }
-                            debug!{"linked: %s", cratepath};
-                        });
-                }*/
-
                 jit::exec(sess, pm.llpm, llmod, CodeGenOptLevel, true);
 
-                if sess.time_llvm_passes() {
-                    llvm::LLVMRustPrintPassTimings();
-                }
+                if sess.time_llvm_passes() { llvm::LLVMRustPrintPassTimings(); }
                 return;
             }
 
diff --git a/src/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp
index 72a97953a94..fc2049507ee 100644
--- a/src/rustllvm/RustWrapper.cpp
+++ b/src/rustllvm/RustWrapper.cpp
@@ -282,17 +282,7 @@ void *RustMCJITMemoryManager::getPointerToNamedFunction(const std::string &Name,
 
   const char *NameStr = Name.c_str();
 
-  // Look through loaded crates for symbols.
-
-  for (DenseSet<DynamicLibrary*>::iterator I = crates.begin(),
-       E = crates.end(); I != E; ++I) {
-    void *Ptr = (*I)->getAddressOfSymbol(NameStr);
-
-    if (Ptr) return Ptr;
-  }
-
-  // Fallback to using any symbols LLVM has loaded (generally
-  // from the main program).
+  // Look through loaded crates and main for symbols.
 
   void *Ptr = sys::DynamicLibrary::SearchForAddressOfSymbol(NameStr);
   if (Ptr) return Ptr;
@@ -365,6 +355,7 @@ LLVMRustExecuteJIT(void* mem,
   PM->run(*unwrap(M));
 
   ExecutionEngine* EE = EngineBuilder(unwrap(M))
+    .setErrorStr(&Err)
     .setTargetOptions(Options)
     .setJITMemoryManager(MM)
     .setOptLevel(OptLevel)

From 00b2086374af127a2ba4fca37bce20c80d2b0dd2 Mon Sep 17 00:00:00 2001
From: Zack Corr <zack@z0w0.me>
Date: Fri, 28 Sep 2012 19:46:40 +1000
Subject: [PATCH 5/5] jit: Change expected main signature

---
 src/rustc/back/link.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/rustc/back/link.rs b/src/rustc/back/link.rs
index 27b566ac28a..592e4ecea35 100644
--- a/src/rustc/back/link.rs
+++ b/src/rustc/back/link.rs
@@ -114,7 +114,7 @@ mod jit {
                 code: entry,
                 env: ptr::null()
             };
-            let func: fn(~[~str]) = cast::transmute(move closure);
+            let func: fn(++argv: ~[~str]) = cast::transmute(move closure);
 
             func(~[sess.opts.binary]);
         }