[clang] [clang-repl] Support wasm execution (PR #86402)

Vassil Vassilev via cfe-commits cfe-commits at lists.llvm.org
Sat Mar 23 09:11:44 PDT 2024


https://github.com/vgvassilev created https://github.com/llvm/llvm-project/pull/86402

This commit introduces support for running clang-repl and executing C++ code interactively inside a Javascript engine using WebAssembly when built with Emscripten. This is achieved by producing WASM "shared libraries" that can be loaded by the Emscripten runtime using dlopen()

More discussion is available in https://reviews.llvm.org/D158140


>From 8286544c2993b0ba7c4f97dcb62c8503a02d84b0 Mon Sep 17 00:00:00 2001
From: Anubhab Ghosh <anubhabghosh.me at gmail.com>
Date: Sat, 23 Mar 2024 15:13:57 +0000
Subject: [PATCH 1/2] [clang-repl] Support wasm execution.

This commit introduces support for running clang-repl and executing C++ code
interactively inside a Javascript engine using WebAssembly when built with
Emscripten. This is achieved by producing WASM "shared libraries" that can be
loaded by the Emscripten runtime using dlopen()

More discussion is available in https://reviews.llvm.org/D158140
---
 clang/lib/Interpreter/CMakeLists.txt          |   1 +
 clang/lib/Interpreter/IncrementalExecutor.cpp |   2 +
 clang/lib/Interpreter/IncrementalExecutor.h   |  11 +-
 clang/lib/Interpreter/Interpreter.cpp         |  15 ++-
 clang/lib/Interpreter/WASM.cpp                | 107 ++++++++++++++++++
 clang/lib/Interpreter/WASM.h                  |  33 ++++++
 6 files changed, 163 insertions(+), 6 deletions(-)
 create mode 100644 clang/lib/Interpreter/WASM.cpp
 create mode 100644 clang/lib/Interpreter/WASM.h

diff --git a/clang/lib/Interpreter/CMakeLists.txt b/clang/lib/Interpreter/CMakeLists.txt
index 9065f998f73c47..a8a287edf5b049 100644
--- a/clang/lib/Interpreter/CMakeLists.txt
+++ b/clang/lib/Interpreter/CMakeLists.txt
@@ -20,6 +20,7 @@ add_clang_library(clangInterpreter
   Interpreter.cpp
   InterpreterUtils.cpp
   Value.cpp
+  WASM.cpp
 
   DEPENDS
   intrinsics_gen
diff --git a/clang/lib/Interpreter/IncrementalExecutor.cpp b/clang/lib/Interpreter/IncrementalExecutor.cpp
index 40bcef94797d43..5180905c9e6d24 100644
--- a/clang/lib/Interpreter/IncrementalExecutor.cpp
+++ b/clang/lib/Interpreter/IncrementalExecutor.cpp
@@ -35,6 +35,8 @@ LLVM_ATTRIBUTE_USED void linkComponents() {
 }
 
 namespace clang {
+IncrementalExecutor::IncrementalExecutor(llvm::orc::ThreadSafeContext &TSC)
+    : TSCtx(TSC) {}
 
 IncrementalExecutor::IncrementalExecutor(llvm::orc::ThreadSafeContext &TSC,
                                          llvm::Error &Err,
diff --git a/clang/lib/Interpreter/IncrementalExecutor.h b/clang/lib/Interpreter/IncrementalExecutor.h
index dd0a210a061415..958be66efc3281 100644
--- a/clang/lib/Interpreter/IncrementalExecutor.h
+++ b/clang/lib/Interpreter/IncrementalExecutor.h
@@ -41,16 +41,19 @@ class IncrementalExecutor {
   llvm::DenseMap<const PartialTranslationUnit *, llvm::orc::ResourceTrackerSP>
       ResourceTrackers;
 
+protected:
+  IncrementalExecutor(llvm::orc::ThreadSafeContext &TSC);
+
 public:
   enum SymbolNameKind { IRName, LinkerName };
 
   IncrementalExecutor(llvm::orc::ThreadSafeContext &TSC, llvm::Error &Err,
                       const clang::TargetInfo &TI);
-  ~IncrementalExecutor();
+  virtual ~IncrementalExecutor();
 
-  llvm::Error addModule(PartialTranslationUnit &PTU);
-  llvm::Error removeModule(PartialTranslationUnit &PTU);
-  llvm::Error runCtors() const;
+  virtual llvm::Error addModule(PartialTranslationUnit &PTU);
+  virtual llvm::Error removeModule(PartialTranslationUnit &PTU);
+  virtual llvm::Error runCtors() const;
   llvm::Error cleanUp();
   llvm::Expected<llvm::orc::ExecutorAddr>
   getSymbolAddress(llvm::StringRef Name, SymbolNameKind NameKind) const;
diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp
index 7fa52f2f15fc49..b97ef0c5ddef26 100644
--- a/clang/lib/Interpreter/Interpreter.cpp
+++ b/clang/lib/Interpreter/Interpreter.cpp
@@ -15,6 +15,7 @@
 #include "IncrementalExecutor.h"
 #include "IncrementalParser.h"
 #include "InterpreterUtils.h"
+#include "WASM.h"
 
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/Mangle.h"
@@ -183,6 +184,12 @@ IncrementalCompilerBuilder::CreateCpp() {
   std::vector<const char *> Argv;
   Argv.reserve(5 + 1 + UserArgs.size());
   Argv.push_back("-xc++");
+#ifdef __EMSCRIPTEN__
+  Argv.push_back("-target");
+  Argv.push_back("wasm32-unknown-emscripten");
+  Argv.push_back("-pie");
+  Argv.push_back("-shared");
+#endif
   Argv.insert(Argv.end(), UserArgs.begin(), UserArgs.end());
 
   std::string TT = TargetTriple ? *TargetTriple : llvm::sys::getProcessTriple();
@@ -373,14 +380,18 @@ Interpreter::Parse(llvm::StringRef Code) {
 }
 
 llvm::Error Interpreter::CreateExecutor() {
-  const clang::TargetInfo &TI =
-      getCompilerInstance()->getASTContext().getTargetInfo();
   if (IncrExecutor)
     return llvm::make_error<llvm::StringError>("Operation failed. "
                                                "Execution engine exists",
                                                std::error_code());
   llvm::Error Err = llvm::Error::success();
+  const clang::TargetInfo &TI =
+      getCompilerInstance()->getASTContext().getTargetInfo();
+#ifdef __EMSCRIPTEN__
+  auto Executor = std::make_unique<WASMIncrementalExecutor>(*TSCtx, Err, TI);
+#else
   auto Executor = std::make_unique<IncrementalExecutor>(*TSCtx, Err, TI);
+#endif
   if (!Err)
     IncrExecutor = std::move(Executor);
 
diff --git a/clang/lib/Interpreter/WASM.cpp b/clang/lib/Interpreter/WASM.cpp
new file mode 100644
index 00000000000000..d21d0ada1eafac
--- /dev/null
+++ b/clang/lib/Interpreter/WASM.cpp
@@ -0,0 +1,107 @@
+//===----------------- WASM.cpp - WASM Interpreter --------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements interpreter support for code execution in WebAssembly.
+//
+//===----------------------------------------------------------------------===//
+
+#include "WASM.h"
+#include "IncrementalExecutor.h"
+
+#include <llvm/IR/LegacyPassManager.h>
+#include <llvm/IR/Module.h>
+#include <llvm/MC/TargetRegistry.h>
+#include <llvm/Target/TargetMachine.h>
+
+#include <clang/Interpreter/Interpreter.h>
+
+#include <dlfcn.h>
+
+namespace clang {
+
+WASMIncrementalExecutor::WASMIncrementalExecutor(
+    llvm::orc::ThreadSafeContext &TSC)
+    : IncrementalExecutor(TSC) {}
+
+llvm::Error WASMIncrementalExecutor::addModule(PartialTranslationUnit &PTU) {
+  PTU.TheModule->dump();
+
+  std::string ErrorString;
+
+  const llvm::Target *Target = llvm::TargetRegistry::lookupTarget(
+      PTU.TheModule->getTargetTriple(), ErrorString);
+  if (!Target) {
+    return llvm::make_error<llvm::StringError>("Failed to create WASM Target: ",
+                                               llvm::inconvertibleErrorCode());
+  }
+
+  llvm::TargetOptions TO = llvm::TargetOptions();
+  llvm::TargetMachine *TargetMachine = Target->createTargetMachine(
+      PTU.TheModule->getTargetTriple(), "", "", TO, llvm::Reloc::Model::PIC_);
+  PTU.TheModule->setDataLayout(TargetMachine->createDataLayout());
+  std::string OutputFileName = PTU.TheModule->getName().str() + ".wasm";
+
+  std::error_code Error;
+  llvm::raw_fd_ostream OutputFile(llvm::StringRef(OutputFileName), Error);
+
+  llvm::legacy::PassManager PM;
+  if (TargetMachine->addPassesToEmitFile(PM, OutputFile, nullptr,
+                                         llvm::CGFT_ObjectFile)) {
+    return llvm::make_error<llvm::StringError>(
+        "WASM backend cannot produce object.", llvm::inconvertibleErrorCode());
+  }
+
+  if (!PM.run(*PTU.TheModule)) {
+
+    return llvm::make_error<llvm::StringError>("Failed to emit WASM object.",
+                                               llvm::inconvertibleErrorCode());
+  }
+
+  OutputFile.close();
+
+  std::vector<const char *> LinkerArgs = {"wasm-ld",
+                                          "-pie",
+                                          "--import-memory",
+                                          "--no-entry",
+                                          "--export-all",
+                                          "--experimental-pic",
+                                          "--no-export-dynamic",
+                                          "--stack-first",
+                                          OutputFileName.c_str(),
+                                          "-o",
+                                          OutputFileName.c_str()};
+  int Result =
+      lld::wasm::link(LinkerArgs, llvm::outs(), llvm::errs(), false, false);
+  if (!Result)
+    return llvm::make_error<llvm::StringError>(
+        "Failed to link incremental module", llvm::inconvertibleErrorCode());
+
+  void *LoadedLibModule =
+      dlopen(OutputFileName.c_str(), RTLD_NOW | RTLD_GLOBAL);
+  if (LoadedLibModule == nullptr) {
+    llvm::errs() << dlerror() << '\n';
+    return llvm::make_error<llvm::StringError>(
+        "Failed to load incremental module", llvm::inconvertibleErrorCode());
+  }
+
+  return llvm::Error::success();
+}
+
+llvm::Error WASMIncrementalExecutor::removeModule(PartialTranslationUnit &PTU) {
+  return llvm::make_error<llvm::StringError>("Not implemented yet",
+                                             llvm::inconvertibleErrorCode());
+}
+
+llvm::Error WASMIncrementalExecutor::runCtors() const {
+  // This seems to be automatically done when using dlopen()
+  return llvm::Error::success();
+}
+
+WASMIncrementalExecutor::~WASMIncrementalExecutor() = default;
+
+} // namespace clang
diff --git a/clang/lib/Interpreter/WASM.h b/clang/lib/Interpreter/WASM.h
new file mode 100644
index 00000000000000..07719fd5fe4f41
--- /dev/null
+++ b/clang/lib/Interpreter/WASM.h
@@ -0,0 +1,33 @@
+//===------------------ WASM.h - WASM Interpreter ---------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements interpreter support for code execution in WebAssembly.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_LIB_INTERPRETER_WASM_H
+#define LLVM_CLANG_LIB_INTERPRETER_WASM_H
+
+#include "IncrementalExecutor.h"
+
+namespace clang {
+
+class WASMIncrementalExecutor : public IncrementalExecutor {
+public:
+  WASMIncrementalExecutor(llvm::orc::ThreadSafeContext &TSC);
+
+  llvm::Error addModule(PartialTranslationUnit &PTU) override;
+  llvm::Error removeModule(PartialTranslationUnit &PTU) override;
+  llvm::Error runCtors() const override;
+
+  ~WASMIncrementalExecutor() override;
+};
+
+} // namespace clang
+
+#endif // LLVM_CLANG_LIB_INTERPRETER_WASM_H

>From 7773706116f195f300c8e0bb5af1b432153310b9 Mon Sep 17 00:00:00 2001
From: Vassil Vassilev <v.g.vassilev at gmail.com>
Date: Sat, 23 Mar 2024 15:32:53 +0000
Subject: [PATCH 2/2] Address comments

---
 clang/lib/Interpreter/CMakeLists.txt         |  2 +-
 clang/lib/Interpreter/Interpreter.cpp        |  4 ++--
 clang/lib/Interpreter/{WASM.cpp => Wasm.cpp} | 24 ++++++++++++--------
 clang/lib/Interpreter/{WASM.h => Wasm.h}     |  8 +++----
 4 files changed, 21 insertions(+), 17 deletions(-)
 rename clang/lib/Interpreter/{WASM.cpp => Wasm.cpp} (87%)
 rename clang/lib/Interpreter/{WASM.h => Wasm.h} (79%)

diff --git a/clang/lib/Interpreter/CMakeLists.txt b/clang/lib/Interpreter/CMakeLists.txt
index a8a287edf5b049..9d302d995801d5 100644
--- a/clang/lib/Interpreter/CMakeLists.txt
+++ b/clang/lib/Interpreter/CMakeLists.txt
@@ -20,7 +20,7 @@ add_clang_library(clangInterpreter
   Interpreter.cpp
   InterpreterUtils.cpp
   Value.cpp
-  WASM.cpp
+  Wasm.cpp
 
   DEPENDS
   intrinsics_gen
diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp
index b97ef0c5ddef26..18c76685636855 100644
--- a/clang/lib/Interpreter/Interpreter.cpp
+++ b/clang/lib/Interpreter/Interpreter.cpp
@@ -15,7 +15,7 @@
 #include "IncrementalExecutor.h"
 #include "IncrementalParser.h"
 #include "InterpreterUtils.h"
-#include "WASM.h"
+#include "Wasm.h"
 
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/Mangle.h"
@@ -388,7 +388,7 @@ llvm::Error Interpreter::CreateExecutor() {
   const clang::TargetInfo &TI =
       getCompilerInstance()->getASTContext().getTargetInfo();
 #ifdef __EMSCRIPTEN__
-  auto Executor = std::make_unique<WASMIncrementalExecutor>(*TSCtx, Err, TI);
+  auto Executor = std::make_unique<WasmIncrementalExecutor>(*TSCtx, Err, TI);
 #else
   auto Executor = std::make_unique<IncrementalExecutor>(*TSCtx, Err, TI);
 #endif
diff --git a/clang/lib/Interpreter/WASM.cpp b/clang/lib/Interpreter/Wasm.cpp
similarity index 87%
rename from clang/lib/Interpreter/WASM.cpp
rename to clang/lib/Interpreter/Wasm.cpp
index d21d0ada1eafac..cb455f111ea888 100644
--- a/clang/lib/Interpreter/WASM.cpp
+++ b/clang/lib/Interpreter/Wasm.cpp
@@ -1,4 +1,4 @@
-//===----------------- WASM.cpp - WASM Interpreter --------------*- C++ -*-===//
+//===----------------- Wasm.cpp - Wasm Interpreter --------------*- C++ -*-===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -10,7 +10,9 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "WASM.h"
+#ifdef __EMSCRIPTEN__
+
+#include "Wasm.h"
 #include "IncrementalExecutor.h"
 
 #include <llvm/IR/LegacyPassManager.h>
@@ -24,11 +26,11 @@
 
 namespace clang {
 
-WASMIncrementalExecutor::WASMIncrementalExecutor(
+WasmIncrementalExecutor::WasmIncrementalExecutor(
     llvm::orc::ThreadSafeContext &TSC)
     : IncrementalExecutor(TSC) {}
 
-llvm::Error WASMIncrementalExecutor::addModule(PartialTranslationUnit &PTU) {
+llvm::Error WasmIncrementalExecutor::addModule(PartialTranslationUnit &PTU) {
   PTU.TheModule->dump();
 
   std::string ErrorString;
@@ -36,7 +38,7 @@ llvm::Error WASMIncrementalExecutor::addModule(PartialTranslationUnit &PTU) {
   const llvm::Target *Target = llvm::TargetRegistry::lookupTarget(
       PTU.TheModule->getTargetTriple(), ErrorString);
   if (!Target) {
-    return llvm::make_error<llvm::StringError>("Failed to create WASM Target: ",
+    return llvm::make_error<llvm::StringError>("Failed to create Wasm Target: ",
                                                llvm::inconvertibleErrorCode());
   }
 
@@ -53,12 +55,12 @@ llvm::Error WASMIncrementalExecutor::addModule(PartialTranslationUnit &PTU) {
   if (TargetMachine->addPassesToEmitFile(PM, OutputFile, nullptr,
                                          llvm::CGFT_ObjectFile)) {
     return llvm::make_error<llvm::StringError>(
-        "WASM backend cannot produce object.", llvm::inconvertibleErrorCode());
+        "Wasm backend cannot produce object.", llvm::inconvertibleErrorCode());
   }
 
   if (!PM.run(*PTU.TheModule)) {
 
-    return llvm::make_error<llvm::StringError>("Failed to emit WASM object.",
+    return llvm::make_error<llvm::StringError>("Failed to emit Wasm object.",
                                                llvm::inconvertibleErrorCode());
   }
 
@@ -92,16 +94,18 @@ llvm::Error WASMIncrementalExecutor::addModule(PartialTranslationUnit &PTU) {
   return llvm::Error::success();
 }
 
-llvm::Error WASMIncrementalExecutor::removeModule(PartialTranslationUnit &PTU) {
+llvm::Error WasmIncrementalExecutor::removeModule(PartialTranslationUnit &PTU) {
   return llvm::make_error<llvm::StringError>("Not implemented yet",
                                              llvm::inconvertibleErrorCode());
 }
 
-llvm::Error WASMIncrementalExecutor::runCtors() const {
+llvm::Error WasmIncrementalExecutor::runCtors() const {
   // This seems to be automatically done when using dlopen()
   return llvm::Error::success();
 }
 
-WASMIncrementalExecutor::~WASMIncrementalExecutor() = default;
+WasmIncrementalExecutor::~WasmIncrementalExecutor() = default;
 
 } // namespace clang
+
+#endif __EMSCRIPTEN__
diff --git a/clang/lib/Interpreter/WASM.h b/clang/lib/Interpreter/Wasm.h
similarity index 79%
rename from clang/lib/Interpreter/WASM.h
rename to clang/lib/Interpreter/Wasm.h
index 07719fd5fe4f41..98acd4c14e240b 100644
--- a/clang/lib/Interpreter/WASM.h
+++ b/clang/lib/Interpreter/Wasm.h
@@ -1,4 +1,4 @@
-//===------------------ WASM.h - WASM Interpreter ---------------*- C++ -*-===//
+//===------------------ Wasm.h - Wasm Interpreter ---------------*- C++ -*-===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -17,15 +17,15 @@
 
 namespace clang {
 
-class WASMIncrementalExecutor : public IncrementalExecutor {
+class WasmIncrementalExecutor : public IncrementalExecutor {
 public:
-  WASMIncrementalExecutor(llvm::orc::ThreadSafeContext &TSC);
+  WasmIncrementalExecutor(llvm::orc::ThreadSafeContext &TSC);
 
   llvm::Error addModule(PartialTranslationUnit &PTU) override;
   llvm::Error removeModule(PartialTranslationUnit &PTU) override;
   llvm::Error runCtors() const override;
 
-  ~WASMIncrementalExecutor() override;
+  ~WasmIncrementalExecutor() override;
 };
 
 } // namespace clang



More information about the cfe-commits mailing list