Skip to content

Commit e1e3b1e

Browse files
argentitevgvassilev
authored andcommitted
[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
1 parent d999ce0 commit e1e3b1e

File tree

6 files changed

+179
-4
lines changed

6 files changed

+179
-4
lines changed

clang/lib/Interpreter/CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ set(LLVM_LINK_COMPONENTS
1212
TargetParser
1313
)
1414

15+
if (EMSCRIPTEN AND "lld" IN_LIST LLVM_ENABLE_PROJECTS)
16+
set(WASM_SRC Wasm.cpp)
17+
endif()
18+
1519
add_clang_library(clangInterpreter
1620
DeviceOffload.cpp
1721
CodeCompletion.cpp
@@ -20,6 +24,8 @@ add_clang_library(clangInterpreter
2024
Interpreter.cpp
2125
InterpreterUtils.cpp
2226
Value.cpp
27+
${WASM_SRC}
28+
PARTIAL_SOURCES_INTENDED
2329

2430
DEPENDS
2531
intrinsics_gen

clang/lib/Interpreter/IncrementalExecutor.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ LLVM_ATTRIBUTE_USED void linkComponents() {
3636
}
3737

3838
namespace clang {
39+
IncrementalExecutor::IncrementalExecutor(llvm::orc::ThreadSafeContext &TSC)
40+
: TSCtx(TSC) {}
3941

4042
llvm::Expected<std::unique_ptr<llvm::orc::LLJITBuilder>>
4143
IncrementalExecutor::createDefaultJITBuilder(

clang/lib/Interpreter/IncrementalExecutor.h

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,16 +43,19 @@ class IncrementalExecutor {
4343
llvm::DenseMap<const PartialTranslationUnit *, llvm::orc::ResourceTrackerSP>
4444
ResourceTrackers;
4545

46+
protected:
47+
IncrementalExecutor(llvm::orc::ThreadSafeContext &TSC);
48+
4649
public:
4750
enum SymbolNameKind { IRName, LinkerName };
4851

4952
IncrementalExecutor(llvm::orc::ThreadSafeContext &TSC,
5053
llvm::orc::LLJITBuilder &JITBuilder, llvm::Error &Err);
51-
~IncrementalExecutor();
54+
virtual ~IncrementalExecutor();
5255

53-
llvm::Error addModule(PartialTranslationUnit &PTU);
54-
llvm::Error removeModule(PartialTranslationUnit &PTU);
55-
llvm::Error runCtors() const;
56+
virtual llvm::Error addModule(PartialTranslationUnit &PTU);
57+
virtual llvm::Error removeModule(PartialTranslationUnit &PTU);
58+
virtual llvm::Error runCtors() const;
5659
llvm::Error cleanUp();
5760
llvm::Expected<llvm::orc::ExecutorAddr>
5861
getSymbolAddress(llvm::StringRef Name, SymbolNameKind NameKind) const;

clang/lib/Interpreter/Interpreter.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
#include "IncrementalExecutor.h"
1616
#include "IncrementalParser.h"
1717
#include "InterpreterUtils.h"
18+
#ifdef __EMSCRIPTEN__
19+
#include "Wasm.h"
20+
#endif // __EMSCRIPTEN__
1821

1922
#include "clang/AST/ASTContext.h"
2023
#include "clang/AST/Mangle.h"
@@ -183,6 +186,12 @@ IncrementalCompilerBuilder::CreateCpp() {
183186
std::vector<const char *> Argv;
184187
Argv.reserve(5 + 1 + UserArgs.size());
185188
Argv.push_back("-xc++");
189+
#ifdef __EMSCRIPTEN__
190+
Argv.push_back("-target");
191+
Argv.push_back("wasm32-unknown-emscripten");
192+
Argv.push_back("-pie");
193+
Argv.push_back("-shared");
194+
#endif
186195
Argv.insert(Argv.end(), UserArgs.begin(), UserArgs.end());
187196

188197
std::string TT = TargetTriple ? *TargetTriple : llvm::sys::getProcessTriple();
@@ -423,8 +432,12 @@ llvm::Error Interpreter::CreateExecutor() {
423432
}
424433

425434
llvm::Error Err = llvm::Error::success();
435+
#ifdef __EMSCRIPTEN__
436+
auto Executor = std::make_unique<WasmIncrementalExecutor>(*TSCtx);
437+
#else
426438
auto Executor =
427439
std::make_unique<IncrementalExecutor>(*TSCtx, *JITBuilder, Err);
440+
#endif
428441
if (!Err)
429442
IncrExecutor = std::move(Executor);
430443

clang/lib/Interpreter/Wasm.cpp

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
//===----------------- Wasm.cpp - Wasm Interpreter --------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// This file implements interpreter support for code execution in WebAssembly.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#include "Wasm.h"
14+
#include "IncrementalExecutor.h"
15+
16+
#include <llvm/IR/LegacyPassManager.h>
17+
#include <llvm/IR/Module.h>
18+
#include <llvm/MC/TargetRegistry.h>
19+
#include <llvm/Target/TargetMachine.h>
20+
21+
#include <clang/Interpreter/Interpreter.h>
22+
23+
#include <string>
24+
25+
namespace lld {
26+
namespace wasm {
27+
bool link(llvm::ArrayRef<const char *> args, llvm::raw_ostream &stdoutOS,
28+
llvm::raw_ostream &stderrOS, bool exitEarly, bool disableOutput);
29+
} // namespace wasm
30+
} // namespace lld
31+
32+
#include <dlfcn.h>
33+
34+
namespace clang {
35+
36+
WasmIncrementalExecutor::WasmIncrementalExecutor(
37+
llvm::orc::ThreadSafeContext &TSC)
38+
: IncrementalExecutor(TSC) {}
39+
40+
llvm::Error WasmIncrementalExecutor::addModule(PartialTranslationUnit &PTU) {
41+
std::string ErrorString;
42+
43+
const llvm::Target *Target = llvm::TargetRegistry::lookupTarget(
44+
PTU.TheModule->getTargetTriple(), ErrorString);
45+
if (!Target) {
46+
return llvm::make_error<llvm::StringError>("Failed to create Wasm Target: ",
47+
llvm::inconvertibleErrorCode());
48+
}
49+
50+
llvm::TargetOptions TO = llvm::TargetOptions();
51+
llvm::TargetMachine *TargetMachine = Target->createTargetMachine(
52+
PTU.TheModule->getTargetTriple(), "", "", TO, llvm::Reloc::Model::PIC_);
53+
PTU.TheModule->setDataLayout(TargetMachine->createDataLayout());
54+
std::string OutputFileName = PTU.TheModule->getName().str() + ".wasm";
55+
56+
std::error_code Error;
57+
llvm::raw_fd_ostream OutputFile(llvm::StringRef(OutputFileName), Error);
58+
59+
llvm::legacy::PassManager PM;
60+
if (TargetMachine->addPassesToEmitFile(PM, OutputFile, nullptr,
61+
llvm::CodeGenFileType::ObjectFile)) {
62+
return llvm::make_error<llvm::StringError>(
63+
"Wasm backend cannot produce object.", llvm::inconvertibleErrorCode());
64+
}
65+
66+
if (!PM.run(*PTU.TheModule)) {
67+
68+
return llvm::make_error<llvm::StringError>("Failed to emit Wasm object.",
69+
llvm::inconvertibleErrorCode());
70+
}
71+
72+
OutputFile.close();
73+
74+
std::vector<const char *> LinkerArgs = {"wasm-ld",
75+
"-pie",
76+
"--import-memory",
77+
"--no-entry",
78+
"--export-all",
79+
"--experimental-pic",
80+
"--no-export-dynamic",
81+
"--stack-first",
82+
OutputFileName.c_str(),
83+
"-o",
84+
OutputFileName.c_str()};
85+
int Result =
86+
lld::wasm::link(LinkerArgs, llvm::outs(), llvm::errs(), false, false);
87+
if (!Result)
88+
return llvm::make_error<llvm::StringError>(
89+
"Failed to link incremental module", llvm::inconvertibleErrorCode());
90+
91+
void *LoadedLibModule =
92+
dlopen(OutputFileName.c_str(), RTLD_NOW | RTLD_GLOBAL);
93+
if (LoadedLibModule == nullptr) {
94+
llvm::errs() << dlerror() << '\n';
95+
return llvm::make_error<llvm::StringError>(
96+
"Failed to load incremental module", llvm::inconvertibleErrorCode());
97+
}
98+
99+
return llvm::Error::success();
100+
}
101+
102+
llvm::Error WasmIncrementalExecutor::removeModule(PartialTranslationUnit &PTU) {
103+
return llvm::make_error<llvm::StringError>("Not implemented yet",
104+
llvm::inconvertibleErrorCode());
105+
}
106+
107+
llvm::Error WasmIncrementalExecutor::runCtors() const {
108+
// This seems to be automatically done when using dlopen()
109+
return llvm::Error::success();
110+
}
111+
112+
WasmIncrementalExecutor::~WasmIncrementalExecutor() = default;
113+
114+
} // namespace clang

clang/lib/Interpreter/Wasm.h

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
//===------------------ Wasm.h - Wasm Interpreter ---------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// This file implements interpreter support for code execution in WebAssembly.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef LLVM_CLANG_LIB_INTERPRETER_WASM_H
14+
#define LLVM_CLANG_LIB_INTERPRETER_WASM_H
15+
16+
#ifndef __EMSCRIPTEN__
17+
#error "This requires emscripten."
18+
#endif // __EMSCRIPTEN__
19+
20+
#include "IncrementalExecutor.h"
21+
22+
namespace clang {
23+
24+
class WasmIncrementalExecutor : public IncrementalExecutor {
25+
public:
26+
WasmIncrementalExecutor(llvm::orc::ThreadSafeContext &TSC);
27+
28+
llvm::Error addModule(PartialTranslationUnit &PTU) override;
29+
llvm::Error removeModule(PartialTranslationUnit &PTU) override;
30+
llvm::Error runCtors() const override;
31+
32+
~WasmIncrementalExecutor() override;
33+
};
34+
35+
} // namespace clang
36+
37+
#endif // LLVM_CLANG_LIB_INTERPRETER_WASM_H

0 commit comments

Comments
 (0)