[clang-repl] Add a command to load dynamic libraries
authorAnubhab Ghosh <anubhabghosh.me@gmail.com>
Sun, 15 Jan 2023 15:10:44 +0000 (20:40 +0530)
committerAnubhab Ghosh <anubhabghosh.me@gmail.com>
Wed, 29 Mar 2023 02:34:50 +0000 (08:04 +0530)
This commit adds the %lib <file> command to load a dynamic library to be
used by the currently running interpreted code.
For example `%lib libSDL2.so`.

Differential Revision: https://reviews.llvm.org/D141824

clang/include/clang/Interpreter/Interpreter.h
clang/lib/Interpreter/IncrementalExecutor.h
clang/lib/Interpreter/Interpreter.cpp
clang/test/Interpreter/Inputs/dynamic-library-test.cpp [new file with mode: 0644]
clang/test/Interpreter/dynamic-library.cpp [new file with mode: 0644]
clang/tools/clang-repl/ClangRepl.cpp

index fd22af9..b20d77e 100644 (file)
@@ -28,7 +28,7 @@ namespace llvm {
 namespace orc {
 class LLJIT;
 class ThreadSafeContext;
-}
+} // namespace orc
 } // namespace llvm
 
 namespace clang {
@@ -52,12 +52,15 @@ class Interpreter {
 
   Interpreter(std::unique_ptr<CompilerInstance> CI, llvm::Error &Err);
 
+  llvm::Error CreateExecutor();
+
 public:
   ~Interpreter();
   static llvm::Expected<std::unique_ptr<Interpreter>>
   create(std::unique_ptr<CompilerInstance> CI);
   const CompilerInstance *getCompilerInstance() const;
-  const llvm::orc::LLJIT *getExecutionEngine() const;
+  llvm::Expected<llvm::orc::LLJIT &> getExecutionEngine();
+
   llvm::Expected<PartialTranslationUnit &> Parse(llvm::StringRef Code);
   llvm::Error Execute(PartialTranslationUnit &T);
   llvm::Error ParseAndExecute(llvm::StringRef Code) {
@@ -72,6 +75,9 @@ public:
   /// Undo N previous incremental inputs.
   llvm::Error Undo(unsigned N = 1);
 
+  /// Link a dynamic library
+  llvm::Error LoadDynamicLibrary(const char *name);
+
   /// \returns the \c JITTargetAddress of a \c GlobalDecl. This interface uses
   /// the CodeGenModule's internal mangling cache to avoid recomputing the
   /// mangled name.
index 54d37c7..f7922ec 100644 (file)
@@ -53,7 +53,8 @@ public:
   llvm::Error cleanUp();
   llvm::Expected<llvm::JITTargetAddress>
   getSymbolAddress(llvm::StringRef Name, SymbolNameKind NameKind) const;
-  llvm::orc::LLJIT *getExecutionEngine() const { return Jit.get(); }
+
+  llvm::orc::LLJIT &GetExecutionEngine() { return *Jit; }
 };
 
 } // end namespace clang
index 3f0842c..76d5f16 100644 (file)
@@ -29,6 +29,7 @@
 #include "clang/Frontend/TextDiagnosticBuffer.h"
 #include "clang/Lex/PreprocessorOptions.h"
 
+#include "llvm/ExecutionEngine/Orc/LLJIT.h"
 #include "llvm/IR/Module.h"
 #include "llvm/Support/Errc.h"
 #include "llvm/TargetParser/Host.h"
@@ -203,10 +204,13 @@ const CompilerInstance *Interpreter::getCompilerInstance() const {
   return IncrParser->getCI();
 }
 
-const llvm::orc::LLJIT *Interpreter::getExecutionEngine() const {
-  if (IncrExecutor)
-    return IncrExecutor->getExecutionEngine();
-  return nullptr;
+llvm::Expected<llvm::orc::LLJIT &> Interpreter::getExecutionEngine() {
+  if (!IncrExecutor) {
+    if (auto Err = CreateExecutor())
+      return Err;
+  }
+
+  return IncrExecutor->GetExecutionEngine();
 }
 
 llvm::Expected<PartialTranslationUnit &>
@@ -214,14 +218,21 @@ Interpreter::Parse(llvm::StringRef Code) {
   return IncrParser->Parse(Code);
 }
 
+llvm::Error Interpreter::CreateExecutor() {
+  const clang::TargetInfo &TI =
+      getCompilerInstance()->getASTContext().getTargetInfo();
+  llvm::Error Err = llvm::Error::success();
+  auto Executor = std::make_unique<IncrementalExecutor>(*TSCtx, Err, TI);
+  if (!Err)
+    IncrExecutor = std::move(Executor);
+
+  return Err;
+}
+
 llvm::Error Interpreter::Execute(PartialTranslationUnit &T) {
   assert(T.TheModule);
   if (!IncrExecutor) {
-    const clang::TargetInfo &TI =
-        getCompilerInstance()->getASTContext().getTargetInfo();
-    llvm::Error Err = llvm::Error::success();
-    IncrExecutor = std::make_unique<IncrementalExecutor>(*TSCtx, Err, TI);
-
+    auto Err = CreateExecutor();
     if (Err)
       return Err;
   }
@@ -283,3 +294,19 @@ llvm::Error Interpreter::Undo(unsigned N) {
   }
   return llvm::Error::success();
 }
+
+llvm::Error Interpreter::LoadDynamicLibrary(const char *name) {
+  auto EE = getExecutionEngine();
+  if (!EE)
+    return EE.takeError();
+
+  auto &DL = EE->getDataLayout();
+
+  if (auto DLSG = llvm::orc::DynamicLibrarySearchGenerator::Load(
+          name, DL.getGlobalPrefix()))
+    EE->getMainJITDylib().addGenerator(std::move(*DLSG));
+  else
+    return DLSG.takeError();
+
+  return llvm::Error::success();
+}
diff --git a/clang/test/Interpreter/Inputs/dynamic-library-test.cpp b/clang/test/Interpreter/Inputs/dynamic-library-test.cpp
new file mode 100644 (file)
index 0000000..1f143ba
--- /dev/null
@@ -0,0 +1,6 @@
+int ultimate_answer = 0;
+
+int calculate_answer() {
+  ultimate_answer = 42;
+  return 5;
+}
diff --git a/clang/test/Interpreter/dynamic-library.cpp b/clang/test/Interpreter/dynamic-library.cpp
new file mode 100644 (file)
index 0000000..794cccc
--- /dev/null
@@ -0,0 +1,19 @@
+// REQUIRES: host-supports-jit, system-linux
+
+// RUN: %clang -xc++ -o %T/libdynamic-library-test.so -fPIC -shared -DLIBRARY %S/Inputs/dynamic-library-test.cpp
+// RUN: cat %s | env LD_LIBRARY_PATH=%T:$LD_LIBRARY_PATH clang-repl | FileCheck %s
+
+#include <cstdio>
+
+extern int ultimate_answer;
+int calculate_answer();
+
+%lib libdynamic-library-test.so
+
+printf("Return value: %d\n", calculate_answer());
+// CHECK: Return value: 5
+
+printf("Variable: %d\n", ultimate_answer);
+// CHECK-NEXT: Variable: 42
+
+%quit
index 401a31d..33faf3f 100644 (file)
@@ -123,6 +123,13 @@ int main(int argc, const char **argv) {
         }
         continue;
       }
+      if (Line->rfind("%lib ", 0) == 0) {
+        if (auto Err = Interp->LoadDynamicLibrary(Line->data() + 5)) {
+          llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: ");
+          HasError = true;
+        }
+        continue;
+      }
 
       if (auto Err = Interp->ParseAndExecute(*Line)) {
         llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: ");