Skip to content

Commit

Permalink
wasi: fast calls
Browse files Browse the repository at this point in the history
  • Loading branch information
devsnek committed Jul 5, 2022
1 parent 7233b18 commit b3f59cf
Show file tree
Hide file tree
Showing 28 changed files with 189 additions and 79 deletions.
236 changes: 162 additions & 74 deletions src/node_wasi.cc
Expand Up @@ -72,18 +72,28 @@ inline void Debug(WASI* wasi, Args&&... args) {
} \
} while (0)

#define CHECK_BOUNDS_OR_RETURN2(mem_size, offset, buf_size) \
do { \
if (!uvwasi_serdes_check_bounds((offset), (mem_size), (buf_size))) { \
return UVWASI_EOVERFLOW; \
} \
} while (0)

using v8::Array;
using v8::BackingStore;
using v8::BigInt;
using v8::CFunction;
using v8::Context;
using v8::Exception;
using v8::FastApiCallbackOptions;
using v8::FunctionCallbackInfo;
using v8::FunctionTemplate;
using v8::Integer;
using v8::Isolate;
using v8::Local;
using v8::MaybeLocal;
using v8::Object;
using v8::Signature;
using v8::String;
using v8::Uint32;
using v8::Value;
Expand Down Expand Up @@ -248,46 +258,149 @@ void WASI::New(const FunctionCallbackInfo<Value>& args) {
}
}

template <auto F, typename R, typename... Args>
void WASI::WasiFunction<F, R, Args...>::SetFunction(
Environment* env, const char* name, Local<FunctionTemplate> tmpl) {
auto c_function = CFunction::Make(FastCallback);
Local<FunctionTemplate> t =
v8::FunctionTemplate::New(env->isolate(),
SlowCallback,
Local<Value>(),
Local<Signature>(),
sizeof...(Args),
v8::ConstructorBehavior::kThrow,
v8::SideEffectType::kHasSideEffect,
&c_function);
const v8::NewStringType type = v8::NewStringType::kInternalized;
v8::Local<v8::String> name_string =
v8::String::NewFromUtf8(env->isolate(), name, type).ToLocalChecked();
tmpl->PrototypeTemplate()->Set(name_string, t);
t->SetClassName(name_string);
}

template <auto F, typename R, typename... Args>
R WASI::WasiFunction<F, R, Args...>::FastCallback(
Local<Object> receiver, Args... args, FastApiCallbackOptions& options) {
WASI* wasi = reinterpret_cast<WASI*>(BaseObject::FromJSObject(receiver));
if (UNLIKELY(wasi == nullptr)) return UVWASI_EINVAL;

if (UNLIKELY(options.wasm_memory == nullptr)) return UVWASI_EINVAL;
uint8_t* memory = nullptr;
CHECK(LIKELY(options.wasm_memory->getStorageIfAligned(&memory)));

return F(*wasi, {(char*)memory, options.wasm_memory->length()}, args...);
}

namespace {
template <typename VT>
static uvwasi_errno_t CheckType(Local<Value> v);

template <typename VT>
static VT ConvertType(Local<Value> V);

template <>
uvwasi_errno_t CheckType<uint32_t>(Local<Value> value) {
if (!value->IsUint32()) return UVWASI_EINVAL;
return UVWASI_ESUCCESS;
}

template <>
uint32_t ConvertType(Local<Value> value) {
return value.As<Uint32>()->Value();
}

template <>
uvwasi_errno_t CheckType<uint64_t>(Local<Value> value) {
if (!value->IsBigInt()) return UVWASI_EINVAL;
return UVWASI_ESUCCESS;
}

void WASI::ArgsGet(const FunctionCallbackInfo<Value>& args) {
template <>
uint64_t ConvertType(Local<Value> value) {
Local<BigInt> js_value = value.As<BigInt>();
bool lossless;
return js_value->Uint64Value(&lossless);
}

template <>
uvwasi_errno_t CheckType<int64_t>(Local<Value> value) {
if (!value->IsBigInt()) return UVWASI_EINVAL;
return UVWASI_ESUCCESS;
}

template <>
int64_t ConvertType(Local<Value> value) {
Local<BigInt> js_value = value.As<BigInt>();
bool lossless;
return js_value->Int64Value(&lossless);
}
} // namespace

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunsequenced"
template <auto F, typename R, typename... Args>
void WASI::WasiFunction<F, R, Args...>::SlowCallback(
const FunctionCallbackInfo<Value>& args) {
WASI* wasi;
uint32_t argv_offset;
uint32_t argv_buf_offset;
char* memory;
size_t mem_size;
RETURN_IF_BAD_ARG_COUNT(args, 2);
CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, argv_offset);
CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, argv_buf_offset);
RETURN_IF_BAD_ARG_COUNT(args, sizeof...(Args));
{
int counter = 0;
uvwasi_errno_t results[sizeof...(Args)] = {
(CheckType<Args>(args[counter++]))...};
for (size_t i = 0; i < sizeof...(Args); i += 1) {
if (results[i] != UVWASI_ESUCCESS) {
args.GetReturnValue().Set(results[i]);
return;
}
}
}
ASSIGN_INITIALIZED_OR_RETURN_UNWRAP(&wasi, args.This());
Debug(wasi, "args_get(%d, %d)\n", argv_offset, argv_buf_offset);
GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size);
CHECK_BOUNDS_OR_RETURN(args,
mem_size,
argv_buf_offset,
wasi->uvw_.argv_buf_size);
CHECK_BOUNDS_OR_RETURN(args,
mem_size,
argv_offset,
wasi->uvw_.argc * UVWASI_SERDES_SIZE_uint32_t);
std::vector<char*> argv(wasi->uvw_.argc);
char* argv_buf = &memory[argv_buf_offset];
uvwasi_errno_t err = uvwasi_args_get(&wasi->uvw_, argv.data(), argv_buf);

{
int counter = 0;
args.GetReturnValue().Set(
F(*wasi, {memory, mem_size}, ConvertType<Args>(args[counter++])...));
}
}
#pragma clang diagnostic pop

template <auto F, typename R, typename... Args>
static void SetFunction(R (*)(WASI&, WasmMemory, Args...),
Environment* env,
const char* name,
Local<FunctionTemplate> tmpl) {
WASI::WasiFunction<F, R, Args...>::SetFunction(env, name, tmpl);
}

uint32_t WASI::ArgsGet(WASI& wasi,
WasmMemory memory,
uint32_t argv_offset,
uint32_t argv_buf_offset) {
Debug(&wasi, "args_get(%d, %d)\n", argv_offset, argv_buf_offset);

CHECK_BOUNDS_OR_RETURN2(
memory.size, argv_buf_offset, wasi.uvw_.argv_buf_size);
CHECK_BOUNDS_OR_RETURN2(
memory.size, argv_offset, wasi.uvw_.argc * UVWASI_SERDES_SIZE_uint32_t);
std::vector<char*> argv(wasi.uvw_.argc);
char* argv_buf = &memory.data[argv_buf_offset];
uvwasi_errno_t err = uvwasi_args_get(&wasi.uvw_, argv.data(), argv_buf);

if (err == UVWASI_ESUCCESS) {
for (size_t i = 0; i < wasi->uvw_.argc; i++) {
for (size_t i = 0; i < wasi.uvw_.argc; i++) {
uint32_t offset =
static_cast<uint32_t>(argv_buf_offset + (argv[i] - argv[0]));
uvwasi_serdes_write_uint32_t(memory,
argv_offset +
(i * UVWASI_SERDES_SIZE_uint32_t),
offset);
uvwasi_serdes_write_uint32_t(
memory.data, argv_offset + (i * UVWASI_SERDES_SIZE_uint32_t), offset);
}
}

args.GetReturnValue().Set(err);
return err;
}


void WASI::ArgsSizesGet(const FunctionCallbackInfo<Value>& args) {
WASI* wasi;
uint32_t argc_offset;
Expand Down Expand Up @@ -454,25 +567,16 @@ void WASI::EnvironSizesGet(const FunctionCallbackInfo<Value>& args) {
args.GetReturnValue().Set(err);
}


void WASI::FdAdvise(const FunctionCallbackInfo<Value>& args) {
WASI* wasi;
uint32_t fd;
uint64_t offset;
uint64_t len;
uint8_t advice;
RETURN_IF_BAD_ARG_COUNT(args, 4);
CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd);
UNWRAP_BIGINT_OR_RETURN(args, args[1], Uint64, offset);
UNWRAP_BIGINT_OR_RETURN(args, args[2], Uint64, len);
CHECK_TO_TYPE_OR_RETURN(args, args[3], Uint32, advice);
ASSIGN_INITIALIZED_OR_RETURN_UNWRAP(&wasi, args.This());
Debug(wasi, "fd_advise(%d, %d, %d, %d)\n", fd, offset, len, advice);
uvwasi_errno_t err = uvwasi_fd_advise(&wasi->uvw_, fd, offset, len, advice);
args.GetReturnValue().Set(err);
uint32_t WASI::FdAdvise(WASI& wasi,
WasmMemory,
uint32_t fd,
uint64_t offset,
uint64_t len,
uint32_t advice) {
Debug(&wasi, "fd_advise(%d, %d, %d, %d)\n", fd, offset, len, advice);
return uvwasi_fd_advise(&wasi.uvw_, fd, offset, len, advice);
}


void WASI::FdAllocate(const FunctionCallbackInfo<Value>& args) {
WASI* wasi;
uint32_t fd;
Expand Down Expand Up @@ -892,40 +996,24 @@ void WASI::FdRenumber(const FunctionCallbackInfo<Value>& args) {
args.GetReturnValue().Set(err);
}


void WASI::FdSeek(const FunctionCallbackInfo<Value>& args) {
WASI* wasi;
uint32_t fd;
int64_t offset;
uint8_t whence;
uint32_t newoffset_ptr;
char* memory;
size_t mem_size;
RETURN_IF_BAD_ARG_COUNT(args, 4);
CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd);
UNWRAP_BIGINT_OR_RETURN(args, args[1], Int64, offset);
CHECK_TO_TYPE_OR_RETURN(args, args[2], Uint32, whence);
CHECK_TO_TYPE_OR_RETURN(args, args[3], Uint32, newoffset_ptr);
ASSIGN_INITIALIZED_OR_RETURN_UNWRAP(&wasi, args.This());
Debug(wasi, "fd_seek(%d, %d, %d, %d)\n", fd, offset, whence, newoffset_ptr);
GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size);
CHECK_BOUNDS_OR_RETURN(args,
mem_size,
newoffset_ptr,
UVWASI_SERDES_SIZE_filesize_t);
uint32_t WASI::FdSeek(WASI& wasi,
WasmMemory memory,
uint32_t fd,
int64_t offset,
uint32_t whence,
uint32_t newoffset_ptr) {
Debug(&wasi, "fd_seek(%d, %d, %d, %d)\n", fd, offset, whence, newoffset_ptr);
CHECK_BOUNDS_OR_RETURN2(
memory.size, newoffset_ptr, UVWASI_SERDES_SIZE_filesize_t);
uvwasi_filesize_t newoffset;
uvwasi_errno_t err = uvwasi_fd_seek(&wasi->uvw_,
fd,
offset,
whence,
&newoffset);
uvwasi_errno_t err =
uvwasi_fd_seek(&wasi.uvw_, fd, offset, whence, &newoffset);
if (err == UVWASI_ESUCCESS)
uvwasi_serdes_write_filesize_t(memory, newoffset_ptr, newoffset);
uvwasi_serdes_write_filesize_t(memory.data, newoffset_ptr, newoffset);

args.GetReturnValue().Set(err);
return err;
}


void WASI::FdSync(const FunctionCallbackInfo<Value>& args) {
WASI* wasi;
uint32_t fd;
Expand Down Expand Up @@ -1673,13 +1761,13 @@ static void Initialize(Local<Object> target,
tmpl->InstanceTemplate()->SetInternalFieldCount(WASI::kInternalFieldCount);
tmpl->Inherit(BaseObject::GetConstructorTemplate(env));

env->SetProtoMethod(tmpl, "args_get", WASI::ArgsGet);
SetFunction<WASI::ArgsGet>(WASI::ArgsGet, env, "args_get", tmpl);
env->SetProtoMethod(tmpl, "args_sizes_get", WASI::ArgsSizesGet);
env->SetProtoMethod(tmpl, "clock_res_get", WASI::ClockResGet);
env->SetProtoMethod(tmpl, "clock_time_get", WASI::ClockTimeGet);
env->SetProtoMethod(tmpl, "environ_get", WASI::EnvironGet);
env->SetProtoMethod(tmpl, "environ_sizes_get", WASI::EnvironSizesGet);
env->SetProtoMethod(tmpl, "fd_advise", WASI::FdAdvise);
SetFunction<WASI::FdAdvise>(WASI::FdAdvise, env, "fd_advise", tmpl);
env->SetProtoMethod(tmpl, "fd_allocate", WASI::FdAllocate);
env->SetProtoMethod(tmpl, "fd_close", WASI::FdClose);
env->SetProtoMethod(tmpl, "fd_datasync", WASI::FdDatasync);
Expand All @@ -1696,7 +1784,7 @@ static void Initialize(Local<Object> target,
env->SetProtoMethod(tmpl, "fd_read", WASI::FdRead);
env->SetProtoMethod(tmpl, "fd_readdir", WASI::FdReaddir);
env->SetProtoMethod(tmpl, "fd_renumber", WASI::FdRenumber);
env->SetProtoMethod(tmpl, "fd_seek", WASI::FdSeek);
SetFunction<WASI::FdSeek>(WASI::FdSeek, env, "fd_seek", tmpl);
env->SetProtoMethod(tmpl, "fd_sync", WASI::FdSync);
env->SetProtoMethod(tmpl, "fd_tell", WASI::FdTell);
env->SetProtoMethod(tmpl, "fd_write", WASI::FdWrite);
Expand Down
26 changes: 23 additions & 3 deletions src/node_wasi.h
Expand Up @@ -10,6 +10,10 @@
namespace node {
namespace wasi {

struct WasmMemory {
char* data;
size_t size;
};

class WASI : public BaseObject,
public mem::NgLibMemoryManager<WASI, uvwasi_mem_t> {
Expand All @@ -23,13 +27,14 @@ class WASI : public BaseObject,
SET_MEMORY_INFO_NAME(WASI)
SET_SELF_SIZE(WASI)

static void ArgsGet(const v8::FunctionCallbackInfo<v8::Value>& args);
static uint32_t ArgsGet(WASI&, WasmMemory, uint32_t, uint32_t);
static void ArgsSizesGet(const v8::FunctionCallbackInfo<v8::Value>& args);
static void ClockResGet(const v8::FunctionCallbackInfo<v8::Value>& args);
static void ClockTimeGet(const v8::FunctionCallbackInfo<v8::Value>& args);
static void EnvironGet(const v8::FunctionCallbackInfo<v8::Value>& args);
static void EnvironSizesGet(const v8::FunctionCallbackInfo<v8::Value>& args);
static void FdAdvise(const v8::FunctionCallbackInfo<v8::Value>& args);
static uint32_t FdAdvise(
WASI&, WasmMemory, uint32_t, uint64_t, uint64_t, uint32_t);
static void FdAllocate(const v8::FunctionCallbackInfo<v8::Value>& args);
static void FdClose(const v8::FunctionCallbackInfo<v8::Value>& args);
static void FdDatasync(const v8::FunctionCallbackInfo<v8::Value>& args);
Expand All @@ -49,7 +54,8 @@ class WASI : public BaseObject,
static void FdRead(const v8::FunctionCallbackInfo<v8::Value>& args);
static void FdReaddir(const v8::FunctionCallbackInfo<v8::Value>& args);
static void FdRenumber(const v8::FunctionCallbackInfo<v8::Value>& args);
static void FdSeek(const v8::FunctionCallbackInfo<v8::Value>& args);
static uint32_t FdSeek(
WASI&, WasmMemory, uint32_t, int64_t, uint32_t, uint32_t);
static void FdSync(const v8::FunctionCallbackInfo<v8::Value>& args);
static void FdTell(const v8::FunctionCallbackInfo<v8::Value>& args);
static void FdWrite(const v8::FunctionCallbackInfo<v8::Value>& args);
Expand Down Expand Up @@ -82,6 +88,20 @@ class WASI : public BaseObject,
void IncreaseAllocatedSize(size_t size);
void DecreaseAllocatedSize(size_t size);

template <auto F, typename R, typename... Args>
class WasiFunction {
public:
static void SetFunction(Environment*,
const char*,
v8::Local<v8::FunctionTemplate>);

private:
static R FastCallback(v8::Local<v8::Object> receiver,
Args...,
v8::FastApiCallbackOptions&);
static void SlowCallback(const v8::FunctionCallbackInfo<v8::Value>&);
};

private:
~WASI() override;
inline void readUInt8(char* memory, uint8_t* value, uint32_t offset);
Expand Down
5 changes: 3 additions & 2 deletions test/wasi/Makefile
@@ -1,12 +1,13 @@
CC = /opt/wasi-sdk/bin/clang
TARGET = wasm32-unknown-wasi
TARGET = wasm32-wasi
SYSROOT =
CFLAGS = -D_WASI_EMULATED_PROCESS_CLOCKS -lwasi-emulated-process-clocks

OBJ = $(patsubst c/%.c, wasm/%.wasm, $(wildcard c/*.c))
all: $(OBJ)

wasm/%.wasm : c/%.c
$(CC) $< --target=$(TARGET) --sysroot=$(SYSROOT) -s -o $@
$(CC) $< $(CFLAGS) --target=$(TARGET) --sysroot=$(SYSROOT) -s -o $@

.PHONY clean:
rm -f $(OBJ)
1 change: 1 addition & 0 deletions test/wasi/test-wasi.js
Expand Up @@ -50,6 +50,7 @@ if (process.argv[2] === 'wasi-child') {
opts.input = options.stdin;

const child = cp.spawnSync(process.execPath, [
...process.argv.slice(1, -1),
'--experimental-wasi-unstable-preview1',
__filename,
'wasi-child',
Expand Down
Binary file modified test/wasi/wasm/cant_dotdot.wasm
Binary file not shown.
Binary file modified test/wasi/wasm/clock_getres.wasm
Binary file not shown.
Binary file modified test/wasi/wasm/create_symlink.wasm
Binary file not shown.
Binary file modified test/wasi/wasm/exitcode.wasm
Binary file not shown.
Binary file modified test/wasi/wasm/fd_prestat_get_refresh.wasm
Binary file not shown.
Binary file modified test/wasi/wasm/follow_symlink.wasm
Binary file not shown.
Binary file modified test/wasi/wasm/freopen.wasm
Binary file not shown.
Binary file modified test/wasi/wasm/ftruncate.wasm
Binary file not shown.
Binary file modified test/wasi/wasm/getentropy.wasm
Binary file not shown.
Binary file modified test/wasi/wasm/getrusage.wasm
Binary file not shown.
Binary file modified test/wasi/wasm/gettimeofday.wasm
Binary file not shown.
Binary file modified test/wasi/wasm/link.wasm
Binary file not shown.
Binary file modified test/wasi/wasm/main_args.wasm
Binary file not shown.
Binary file modified test/wasi/wasm/notdir.wasm
Binary file not shown.
Binary file modified test/wasi/wasm/poll.wasm
Binary file not shown.
Binary file modified test/wasi/wasm/preopen_populates.wasm
Binary file not shown.
Binary file modified test/wasi/wasm/read_file.wasm
Binary file not shown.
Binary file modified test/wasi/wasm/read_file_twice.wasm
Binary file not shown.
Binary file modified test/wasi/wasm/readdir.wasm
Binary file not shown.
Binary file modified test/wasi/wasm/stat.wasm
Binary file not shown.
Binary file modified test/wasi/wasm/stdin.wasm
Binary file not shown.
Binary file modified test/wasi/wasm/symlink_escape.wasm
Binary file not shown.
Binary file modified test/wasi/wasm/symlink_loop.wasm
Binary file not shown.
Binary file modified test/wasi/wasm/write_file.wasm
Binary file not shown.

0 comments on commit b3f59cf

Please sign in to comment.