Skip to content

Commit 4434cee

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 2685256 commit 4434cee

File tree

6 files changed

+161
-4
lines changed

6 files changed

+161
-4
lines changed

clang/lib/Interpreter/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ add_clang_library(clangInterpreter
2020
Interpreter.cpp
2121
InterpreterUtils.cpp
2222
Value.cpp
23+
WASM.cpp
2324

2425
DEPENDS
2526
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: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "IncrementalExecutor.h"
1616
#include "IncrementalParser.h"
1717
#include "InterpreterUtils.h"
18+
#include "WASM.h"
1819

1920
#include "clang/AST/ASTContext.h"
2021
#include "clang/AST/Mangle.h"
@@ -183,6 +184,12 @@ IncrementalCompilerBuilder::CreateCpp() {
183184
std::vector<const char *> Argv;
184185
Argv.reserve(5 + 1 + UserArgs.size());
185186
Argv.push_back("-xc++");
187+
#ifdef __EMSCRIPTEN__
188+
Argv.push_back("-target");
189+
Argv.push_back("wasm32-unknown-emscripten");
190+
Argv.push_back("-pie");
191+
Argv.push_back("-shared");
192+
#endif
186193
Argv.insert(Argv.end(), UserArgs.begin(), UserArgs.end());
187194

188195
std::string TT = TargetTriple ? *TargetTriple : llvm::sys::getProcessTriple();
@@ -400,7 +407,11 @@ llvm::Error Interpreter::CreateExecutor() {
400407
if (!JB)
401408
return JB.takeError();
402409
llvm::Error Err = llvm::Error::success();
410+
#ifdef __EMSCRIPTEN__
411+
auto Executor = std::make_unique<WASMIncrementalExecutor>(*TSCtx, **JB, Err);
412+
#else
403413
auto Executor = std::make_unique<IncrementalExecutor>(*TSCtx, **JB, Err);
414+
#endif
404415
if (!Err)
405416
IncrExecutor = std::move(Executor);
406417

clang/lib/Interpreter/WASM.cpp

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
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 <dlfcn.h>
24+
25+
namespace clang {
26+
27+
WASMIncrementalExecutor::WASMIncrementalExecutor(
28+
llvm::orc::ThreadSafeContext &TSC)
29+
: IncrementalExecutor(TSC) {}
30+
31+
llvm::Error WASMIncrementalExecutor::addModule(PartialTranslationUnit &PTU) {
32+
PTU.TheModule->dump();
33+
34+
std::string ErrorString;
35+
36+
const llvm::Target *Target = llvm::TargetRegistry::lookupTarget(
37+
PTU.TheModule->getTargetTriple(), ErrorString);
38+
if (!Target) {
39+
return llvm::make_error<llvm::StringError>("Failed to create WASM Target: ",
40+
llvm::inconvertibleErrorCode());
41+
}
42+
43+
llvm::TargetOptions TO = llvm::TargetOptions();
44+
llvm::TargetMachine *TargetMachine = Target->createTargetMachine(
45+
PTU.TheModule->getTargetTriple(), "", "", TO, llvm::Reloc::Model::PIC_);
46+
PTU.TheModule->setDataLayout(TargetMachine->createDataLayout());
47+
std::string OutputFileName = PTU.TheModule->getName().str() + ".wasm";
48+
49+
std::error_code Error;
50+
llvm::raw_fd_ostream OutputFile(llvm::StringRef(OutputFileName), Error);
51+
52+
llvm::legacy::PassManager PM;
53+
if (TargetMachine->addPassesToEmitFile(PM, OutputFile, nullptr,
54+
llvm::CGFT_ObjectFile)) {
55+
return llvm::make_error<llvm::StringError>(
56+
"WASM backend cannot produce object.", llvm::inconvertibleErrorCode());
57+
}
58+
59+
if (!PM.run(*PTU.TheModule)) {
60+
61+
return llvm::make_error<llvm::StringError>("Failed to emit WASM object.",
62+
llvm::inconvertibleErrorCode());
63+
}
64+
65+
OutputFile.close();
66+
67+
std::vector<const char *> LinkerArgs = {"wasm-ld",
68+
"-pie",
69+
"--import-memory",
70+
"--no-entry",
71+
"--export-all",
72+
"--experimental-pic",
73+
"--no-export-dynamic",
74+
"--stack-first",
75+
OutputFileName.c_str(),
76+
"-o",
77+
OutputFileName.c_str()};
78+
int Result =
79+
lld::wasm::link(LinkerArgs, llvm::outs(), llvm::errs(), false, false);
80+
if (!Result)
81+
return llvm::make_error<llvm::StringError>(
82+
"Failed to link incremental module", llvm::inconvertibleErrorCode());
83+
84+
void *LoadedLibModule =
85+
dlopen(OutputFileName.c_str(), RTLD_NOW | RTLD_GLOBAL);
86+
if (LoadedLibModule == nullptr) {
87+
llvm::errs() << dlerror() << '\n';
88+
return llvm::make_error<llvm::StringError>(
89+
"Failed to load incremental module", llvm::inconvertibleErrorCode());
90+
}
91+
92+
return llvm::Error::success();
93+
}
94+
95+
llvm::Error WASMIncrementalExecutor::removeModule(PartialTranslationUnit &PTU) {
96+
return llvm::make_error<llvm::StringError>("Not implemented yet",
97+
llvm::inconvertibleErrorCode());
98+
}
99+
100+
llvm::Error WASMIncrementalExecutor::runCtors() const {
101+
// This seems to be automatically done when using dlopen()
102+
return llvm::Error::success();
103+
}
104+
105+
WASMIncrementalExecutor::~WASMIncrementalExecutor() = default;
106+
107+
} // namespace clang

clang/lib/Interpreter/WASM.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
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+
#include "IncrementalExecutor.h"
17+
18+
namespace clang {
19+
20+
class WASMIncrementalExecutor : public IncrementalExecutor {
21+
public:
22+
WASMIncrementalExecutor(llvm::orc::ThreadSafeContext &TSC);
23+
24+
llvm::Error addModule(PartialTranslationUnit &PTU) override;
25+
llvm::Error removeModule(PartialTranslationUnit &PTU) override;
26+
llvm::Error runCtors() const override;
27+
28+
~WASMIncrementalExecutor() override;
29+
};
30+
31+
} // namespace clang
32+
33+
#endif // LLVM_CLANG_LIB_INTERPRETER_WASM_H

0 commit comments

Comments
 (0)