Skip to content

Commit

Permalink
Add support for WASM intrinsic globals. (STACKED on wasm-module, see …
Browse files Browse the repository at this point in the history
…commit messages) (envoyproxy#36)

* Add support for WASM intrinsic globals.
  • Loading branch information
jplevyak authored Mar 6, 2019
1 parent 12290b3 commit 21be692
Show file tree
Hide file tree
Showing 8 changed files with 12,037 additions and 14 deletions.
19 changes: 17 additions & 2 deletions source/extensions/common/wasm/wasm.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include <stdio.h>

#include <limits>
#include <memory>
#include <string>

Expand Down Expand Up @@ -951,7 +952,7 @@ Wasm::Wasm(absl::string_view vm, absl::string_view id, absl::string_view initial
id_ = std::string(id);
}

void Wasm::registerFunctions() {
void Wasm::registerCallbacks() {
#define _REGISTER(_fn) registerCallback(wasm_vm_.get(), "envoy", #_fn, &_fn##Handler);
if (is_emscripten_) {
_REGISTER(getTotalMemory);
Expand Down Expand Up @@ -1014,6 +1015,15 @@ void Wasm::registerFunctions() {
#undef _REGISTER_PROXY
}

void Wasm::establishEnvironment() {
if (is_emscripten_) {
wasm_vm_->makeModule("global");
emscripten_NaN_ = makeGlobal(wasm_vm_.get(), "global", "NaN", std::nan("0"));
emscripten_Infinity_ =
makeGlobal(wasm_vm_.get(), "global", "Infinity", std::numeric_limits<double>::infinity());
}
}

void Wasm::getFunctions() {
#define _GET_PROXY(_fn) getFunction(wasm_vm_.get(), "_proxy_" #_fn, &_fn##_);
_GET_PROXY(onStart);
Expand Down Expand Up @@ -1062,10 +1072,15 @@ bool Wasm::initialize(const std::string& code, absl::string_view name, bool allo
start = decodeVarint(start, end, &emscripten_memory_size_);
decodeVarint(start, end, &emscripten_table_size_);
}
registerFunctions();
registerCallbacks();
establishEnvironment();
wasm_vm_->link(name, is_emscripten_);
general_context_ = createContext();
wasm_vm_->start(general_context_.get());
if (is_emscripten_) {
ASSERT(std::isnan(emscripten_NaN_->get()));
ASSERT(std::isinf(emscripten_Infinity_->get()));
}
code_ = code;
allow_precompiled_ = allow_precompiled;
getFunctions();
Expand Down
32 changes: 28 additions & 4 deletions source/extensions/common/wasm/wasm.h
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,12 @@ struct AsyncClientHandler : public Http::AsyncClient::Callbacks {
Http::AsyncClient::Request* request;
};

template <typename T> struct Global {
virtual ~Global() {}
virtual T get() PURE;
virtual void set(const T& t) PURE;
};

// Wasm execution instance. Manages the Envoy side of the Wasm interface.
class Wasm : public Envoy::Server::Wasm,
public AccessLog::Instance,
Expand Down Expand Up @@ -310,8 +316,9 @@ class Wasm : public Envoy::Server::Wasm,
private:
friend class Context;

void registerFunctions(); // Register functions called out from WASM.
void getFunctions(); // Get functions call into WASM.
void registerCallbacks(); // Register functions called out from WASM.
void establishEnvironment(); // Language specific enviroments.
void getFunctions(); // Get functions call into WASM.

Upstream::ClusterManager& cluster_manager_;
Event::Dispatcher& dispatcher_;
Expand Down Expand Up @@ -360,6 +367,9 @@ class Wasm : public Envoy::Server::Wasm,
uint32_t emscripten_abi_minor_version_ = 0;
uint32_t emscripten_memory_size_ = 0;
uint32_t emscripten_table_size_ = 0;

std::unique_ptr<Global<double>> emscripten_NaN_;
std::unique_ptr<Global<double>> emscripten_Infinity_;
};

inline WasmVm* Context::wasmVm() const { return wasm_->wasmVm(); }
Expand Down Expand Up @@ -514,21 +524,35 @@ template <typename R, typename... Args>
void getFunctionWavm(WasmVm* vm, absl::string_view functionName,
std::function<R(Context*, Args...)>*);

template <typename T>
std::unique_ptr<Global<T>> makeGlobalWavm(WasmVm* vm, absl::string_view moduleName,
absl::string_view name, T initialValue);

template <typename R, typename... Args>
void registerCallback(WasmVm* vm, absl::string_view moduleName, absl::string_view functionName,
R (*f)(Args...)) {
if (vm->vm() == WasmVmNames::get().Wavm) {
registerCallbackWavm(vm, moduleName, functionName, f);
} else {
throw WasmVmException("unsupoorted wasm vm");
throw WasmVmException("unsupported wasm vm");
}
}

template <typename F> void getFunction(WasmVm* vm, absl::string_view functionName, F* function) {
if (vm->vm() == WasmVmNames::get().Wavm) {
getFunctionWavm(vm, functionName, function);
} else {
throw WasmVmException("unsupoorted wasm vm");
throw WasmVmException("unsupported wasm vm");
}
}

template <typename T>
std::unique_ptr<Global<T>> makeGlobal(WasmVm* vm, absl::string_view moduleName,
absl::string_view name, T initialValue) {
if (vm->vm() == WasmVmNames::get().Wavm) {
return makeGlobalWavm(vm, moduleName, name, initialValue);
} else {
throw WasmVmException("unsupported wasm vm");
}
}

Expand Down
83 changes: 76 additions & 7 deletions source/extensions/common/wasm/wavm/wavm.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <atomic>
#include <fstream>
#include <memory>
#include <unordered_map>
#include <utility>
#include <vector>

Expand Down Expand Up @@ -46,8 +47,6 @@
using namespace WAVM;
using namespace WAVM::IR;

DECLARE_INTRINSIC_MODULE(env);

namespace Envoy {
namespace Extensions {
namespace Common {
Expand All @@ -56,6 +55,9 @@ namespace Wasm {
extern thread_local Envoy::Extensions::Common::Wasm::Context* current_context_;

namespace Wavm {

struct Wavm;

namespace {

using Context = Common::Wasm::Context; // Shadowing WAVM::Runtime::Context.
Expand Down Expand Up @@ -176,6 +178,28 @@ template <typename F> EnvoyHandlerBase* MakeEnvoyHandler(F handler) {
return new EnvoyHandler<F>(handler);
}

struct WavmGlobalBase {
WAVM::Runtime::Global* global_ = nullptr;
};

template <typename T> struct WavmGlobal : Global<T>, Intrinsics::GenericGlobal<T>, WavmGlobalBase {
WavmGlobal(Common::Wasm::Wavm::Wavm* wavm, Intrinsics::Module& module, const std::string& name,
T value)
: Intrinsics::GenericGlobal<T>(module, name.c_str(), value), wavm_(wavm) {}
virtual ~WavmGlobal() {}

T get() override;
void set(const T& t) override;

Common::Wasm::Wavm::Wavm* wavm_;
};

struct PairHash {
template <typename T, typename U> std::size_t operator()(const std::pair<T, U>& x) const {
return std::hash<T>()(x.first) + std::hash<U>()(x.second);
}
};

struct Wavm : public WasmVm {
Wavm() = default;
~Wavm() override;
Expand All @@ -193,8 +217,7 @@ struct Wavm : public WasmVm {
void makeModule(absl::string_view name) override;
absl::string_view getUserSection(absl::string_view name, bool* present) override;

void GetFunctions();
void RegisterCallbacks();
void getInstantiatedGlobals();

bool hasInstantiatedModule_ = false;
IR::Module irModule_;
Expand All @@ -208,6 +231,9 @@ struct Wavm : public WasmVm {
absl::node_hash_map<std::string, WAVM::Runtime::GCPointer<WAVM::Runtime::ModuleInstance>>
intrinsicModuleInstances_;
std::vector<std::unique_ptr<Intrinsics::Function>> envoyFunctions_;
// The values of this map are owned by the Wasm owning this Wavm.
std::unordered_map<std::pair<std::string, std::string>, WavmGlobalBase*, PairHash>
intrinsicGlobals_;
};

Wavm::~Wavm() {
Expand Down Expand Up @@ -281,12 +307,25 @@ void Wavm::link(absl::string_view name, bool needs_emscripten) {
emscriptenInstance_ = Emscripten::instantiate(compartment_, irModule_);
rootResolver.moduleNameToInstanceMap().set("env", emscriptenInstance_->env);
rootResolver.moduleNameToInstanceMap().set("asm2wasm", emscriptenInstance_->asm2wasm);
rootResolver.moduleNameToInstanceMap().set("global", emscriptenInstance_->global);
}
WAVM::Runtime::LinkResult linkResult = linkModule(irModule_, rootResolver);
moduleInstance_ = instantiateModule(compartment_, module_, std::move(linkResult.resolvedImports),
std::string(name));
memory_ = getDefaultMemory(moduleInstance_);
getInstantiatedGlobals();
}

void Wavm::getInstantiatedGlobals() {
for (auto& p : intrinsicGlobals_) {
auto o =
WAVM::Runtime::getInstanceExport(intrinsicModuleInstances_[p.first.first], p.first.second);
auto g = WAVM::Runtime::as<WAVM::Runtime::Global>(o);
if (!g) {
throw WasmVmException(
fmt::format("Unable to resolve intrinsic global {} {}", p.first.first, p.first.second));
}
p.second->global_ = g;
}
}

void Wavm::makeModule(absl::string_view name) {
Expand Down Expand Up @@ -499,9 +538,9 @@ void getFunctionWavmReturn(WasmVm* vm, absl::string_view functionName,
};
}

// NB: Unfortunately 'void' is not treated like every othefunction type in C++. In
// NB: Unfortunately 'void' is not treated like every other function type in C++. In
// particular it is not possible to specialize a template based on 'void'. Instead
// we use 'Void' for template matching. Note that the template implemenation above
// we use 'Void' for template matching. Note that the template implementation above
// which matchers on 'bool' does not use 'Void' in the implemenation.
template <typename R, typename... Args>
void getFunctionWavm(WasmVm* vm, absl::string_view functionName,
Expand Down Expand Up @@ -578,6 +617,36 @@ template void getFunctionWavm<uint32_t, uint32_t, uint32_t, uint32_t, uint32_t,
std::function<uint32_t(Context*, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t,
uint32_t, uint32_t, uint32_t, uint32_t)>*);

template <typename T> T getValue(IR::Value) {}
template <> int32_t getValue(IR::Value v) { return v.i32; }
template <> uint32_t getValue(IR::Value v) { return v.u32; }
template <> int64_t getValue(IR::Value v) { return v.i64; }
template <> uint64_t getValue(IR::Value v) { return v.u64; }
template <> float getValue(IR::Value v) { return v.f32; }
template <> double getValue(IR::Value v) { return v.f64; }

template <typename T> T WavmGlobal<T>::get() {
return getValue<T>(getGlobalValue(wavm_->context_, global_));
}

template <typename T> void WavmGlobal<T>::set(const T& t) {
setGlobalValue(wavm_->context_, global_, IR::Value(t));
}

template <typename T>
std::unique_ptr<Global<T>> makeGlobalWavm(WasmVm* vm, absl::string_view moduleName,
absl::string_view name, T initialValue) {
auto wavm = static_cast<Common::Wasm::Wavm::Wavm*>(vm);
auto g = std::make_unique<WavmGlobal<T>>(wavm, wavm->intrinsicModules_[moduleName],
std::string(name), initialValue);
wavm->intrinsicGlobals_[std::make_pair(std::string(moduleName), std::string(name))] = g.get();
return g;
}

template std::unique_ptr<Global<double>> makeGlobalWavm(WasmVm* vm, absl::string_view moduleName,
absl::string_view name,
double initialValue);

} // namespace Wasm
} // namespace Common
} // namespace Extensions
Expand Down
2 changes: 1 addition & 1 deletion test/extensions/wasm/test_data/Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
NO_CONTEXT = true

all: logging.wasm bad_signature.wasm segv.wasm
all: logging.wasm bad_signature.wasm segv.wasm emscript.wasm

include ../../../../api/wasm/cpp/Makefile.base
17 changes: 17 additions & 0 deletions test/extensions/wasm/test_data/emscript.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// NOLINT(namespace-envoy)
#include <cmath>
#include <limits>
#include <string>

#include "proxy_wasm_intrinsics.h"

float gNan = std::nan("1");
float gInfinity = INFINITY;

extern "C" EMSCRIPTEN_KEEPALIVE void proxy_onStart() {
logInfo(std::string("NaN ") + std::to_string(gNan));
// For some reason these return "inf nan":
// logWarn("inf " + std::to_string(gInfinity));
// logInfo("inf " + std::to_string(1.0/0.0));
logWarn(std::string("inf ") + (std::isinf(gInfinity) ? "inf" : "nan"));
}
Binary file added test/extensions/wasm/test_data/emscript.wasm
Binary file not shown.
Loading

0 comments on commit 21be692

Please sign in to comment.