Skip to content

Commit

Permalink
Implement clock related functions in wasi
Browse files Browse the repository at this point in the history
Improve pointer handling of wasi functions.

Signed-off-by: Zoltan Herczeg [email protected]
  • Loading branch information
zherczeg authored and clover2123 committed Nov 26, 2024
1 parent e5f6c55 commit 20d770b
Show file tree
Hide file tree
Showing 5 changed files with 133 additions and 44 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/actions.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ jobs:
- name: Install Packages
run: |
brew update
brew install cmake ninja pkg-config
brew install cmake ninja
- name: Build arm64
env:
BUILD_OPTIONS: -DWALRUS_MODE=debug -DWALRUS_OUTPUT=shell -GNinja
Expand Down
11 changes: 11 additions & 0 deletions src/runtime/DefinedFunctionTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class DefinedFunctionTypes {
I32R,
I32_RI32,
I32I32_RI32,
I32I64I32_RI32,
I32I32I32I32_RI32,
I32I64I32I32_RI32,
I32I32I32I32I32I64I64I32I32_RI32,
Expand Down Expand Up @@ -82,6 +83,16 @@ class DefinedFunctionTypes {
result->push_back(Value::Type::I32);
m_vector[index++] = new FunctionType(param, result);
}
{
// I32I64I32_RI32
param = new ValueTypeVector();
result = new ValueTypeVector();
param->push_back(Value::Type::I32);
param->push_back(Value::Type::I64);
param->push_back(Value::Type::I32);
result->push_back(Value::Type::I32);
m_vector[index++] = new FunctionType(param, result);
}
{
// I32I32I32I32_RI32
param = new ValueTypeVector();
Expand Down
135 changes: 93 additions & 42 deletions src/wasi/WASI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,47 @@ namespace Walrus {
uvwasi_t* WASI::g_uvwasi;
WASI::WasiFuncInfo WASI::g_wasiFunctions[WasiFuncIndex::FuncEnd];

static void* get_memory_pointer(Instance* instance, Value& value, size_t size)
{
Memory* memory = instance->memory(0);
uint32_t offset = value.asI32();

// The uvwasi module always checks nullptr arguments.
if (memory->sizeInByte() < size || memory->sizeInByte() - size < offset) {
return nullptr;
}

return memory->buffer() + offset;
}

class TemporaryData {
public:
TemporaryData(size_t size)
{
if (size <= sizeof(m_stackData)) {
m_data = m_stackData;
} else {
m_data = malloc(size);
}
}

~TemporaryData()
{
if (m_data != nullptr && m_data != m_stackData) {
free(m_data);
}
}

void* data()
{
return m_data;
}

private:
void* m_stackData[8];
void* m_data;
};

void WASI::initialize(uvwasi_t* uvwasi)
{
ASSERT(!!uvwasi);
Expand Down Expand Up @@ -65,6 +106,20 @@ void WASI::proc_raise(ExecutionState& state, Value* argv, Value* result, Instanc
result[0] = Value(uvwasi_proc_raise(WASI::g_uvwasi, argv[0].asI32()));
}

void WASI::clock_res_get(ExecutionState& state, Value* argv, Value* result, Instance* instance)
{
uvwasi_timestamp_t* out_addr = reinterpret_cast<uvwasi_timestamp_t*>(get_memory_pointer(instance, argv[1], sizeof(uvwasi_timestamp_t)));

result[0] = Value(uvwasi_clock_res_get(WASI::g_uvwasi, argv[0].asI32(), out_addr));
}

void WASI::clock_time_get(ExecutionState& state, Value* argv, Value* result, Instance* instance)
{
uvwasi_timestamp_t* out_addr = reinterpret_cast<uvwasi_timestamp_t*>(get_memory_pointer(instance, argv[2], sizeof(uvwasi_timestamp_t)));

result[0] = Value(uvwasi_clock_time_get(WASI::g_uvwasi, argv[0].asI32(), argv[1].asI64(), out_addr));
}

void WASI::fd_write(ExecutionState& state, Value* argv, Value* result, Instance* instance)
{
uint32_t fd = argv[0].asI32();
Expand Down Expand Up @@ -120,77 +175,73 @@ void WASI::fd_seek(ExecutionState& state, Value* argv, Value* result, Instance*
uint32_t fd = argv[0].asI32();
uint64_t fileDelta = argv[1].asI32();
uint32_t whence = argv[2].asI32();
uint64_t newOffset = argv[3].asI32();

uvwasi_filesize_t out_addr = *(instance->memory(0)->buffer() + newOffset);
uvwasi_filesize_t* file_size = reinterpret_cast<uvwasi_filesize_t*>(get_memory_pointer(instance, argv[3], sizeof(uvwasi_filesize_t)));

result[0] = Value(static_cast<int16_t>(uvwasi_fd_seek(WASI::g_uvwasi, fd, fileDelta, whence, &out_addr)));
*(instance->memory(0)->buffer() + newOffset) = out_addr;
result[0] = Value(uvwasi_fd_seek(WASI::g_uvwasi, fd, fileDelta, whence, file_size));
}

void WASI::path_open(ExecutionState& state, Value* argv, Value* result, Instance* instance)
{
uint32_t fd = argv[0].asI32();
uint32_t dirflags = argv[1].asI32();
uint32_t path_offset = argv[2].asI32();
uint32_t len = argv[3].asI32();
uint32_t length = argv[3].asI32();
uint32_t oflags = argv[4].asI32();
uint64_t rights = argv[5].asI64();
uint64_t right_inheriting = argv[6].asI64();
uint32_t fdflags = argv[7].asI32();
uint32_t ret_fd_offset = argv[8].asI32();

uvwasi_fd_t* ret_fd = reinterpret_cast<uvwasi_fd_t*>(instance->memory(0)->buffer() + ret_fd_offset);

const char* path = reinterpret_cast<char*>(instance->memory(0)->buffer() + path_offset);

result[0] = Value(static_cast<uint16_t>(
uvwasi_path_open(WASI::g_uvwasi,
fd,
dirflags,
path,
len,
oflags,
rights,
right_inheriting,
fdflags,
ret_fd)));
const char* path = reinterpret_cast<char*>(get_memory_pointer(instance, argv[2], length));
uvwasi_fd_t* ret_fd = reinterpret_cast<uvwasi_fd_t*>(get_memory_pointer(instance, argv[8], sizeof(uvwasi_fd_t)));

result[0] = Value(uvwasi_path_open(WASI::g_uvwasi, fd, dirflags, path, length,
oflags, rights, right_inheriting, fdflags, ret_fd));
}

void WASI::environ_sizes_get(ExecutionState& state, Value* argv, Value* result, Instance* instance)
{
uint32_t count = argv[0].asI32();
uint32_t buf = argv[1].asI32();

uvwasi_size_t* uvCount = reinterpret_cast<uvwasi_size_t*>(instance->memory(0)->buffer() + count);
uvwasi_size_t* uvBufSize = reinterpret_cast<uvwasi_size_t*>(instance->memory(0)->buffer() + buf);
uvwasi_size_t* uvCount = reinterpret_cast<uvwasi_size_t*>(get_memory_pointer(instance, argv[0], sizeof(uint32_t)));
uvwasi_size_t* uvBufSize = reinterpret_cast<uvwasi_size_t*>(get_memory_pointer(instance, argv[1], sizeof(uint32_t)));

result[0] = Value(static_cast<uint16_t>(uvwasi_environ_sizes_get(WASI::g_uvwasi, uvCount, uvBufSize)));
result[0] = Value(uvwasi_environ_sizes_get(WASI::g_uvwasi, uvCount, uvBufSize));
}

void WASI::environ_get(ExecutionState& state, Value* argv, Value* result, Instance* instance)
{
uint32_t env = argv[0].asI32();
uint32_t environBuf = argv[1].asI32();
uvwasi_size_t count;
uvwasi_size_t size;
uvwasi_environ_sizes_get(WASI::g_uvwasi, &count, &size);

uint32_t* uvEnviron = reinterpret_cast<uint32_t*>(get_memory_pointer(instance, argv[0], count * sizeof(uint32_t)));
char* uvEnvironBuf = reinterpret_cast<char*>(get_memory_pointer(instance, argv[1], size));

if (uvEnviron == nullptr || uvEnvironBuf == nullptr) {
result[0] = Value(WasiErrNo::inval);
return;
}

char** uvEnviron = reinterpret_cast<char**>(instance->memory(0)->buffer() + env);
char* uvEnvironBuf = reinterpret_cast<char*>(instance->memory(0)->buffer() + environBuf);
TemporaryData pointers(count * sizeof(void*));

result[0] = Value(static_cast<uint16_t>(uvwasi_environ_get(WASI::g_uvwasi, uvEnviron, uvEnvironBuf)));
if (pointers.data() == nullptr) {
result[0] = Value(WasiErrNo::inval);
return;
}

char** data = reinterpret_cast<char**>(pointers.data());
result[0] = Value(static_cast<uint16_t>(uvwasi_environ_get(WASI::g_uvwasi, data, uvEnvironBuf)));

char* buffer = reinterpret_cast<char*>(instance->memory(0)->buffer());
for (uvwasi_size_t i = 0; i < count; i++) {
uvEnviron[i] = data[i] - buffer;
}
}

void WASI::random_get(ExecutionState& state, Value* argv, Value* result, Instance* instance)
{
ASSERT(argv[0].type() == Value::I32);
ASSERT(argv[1].type() == Value::I32);
if (uint64_t(argv[0].asI32()) + argv[1].asI32() >= instance->memory(0)->sizeInByte()) {
result[0] = Value(WasiErrNo::inval);
}
uint32_t length = argv[1].asI32();
void* buf = get_memory_pointer(instance, argv[0], length);

void* buf = (void*)(instance->memory(0)->buffer() + argv[0].asI32());
uvwasi_size_t length = argv[1].asI32();
result[0] = Value(uvwasi_random_get(WASI::g_uvwasi, buf, length));
}

} // namespace Walrus

#endif
6 changes: 5 additions & 1 deletion src/wasi/WASI.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ class WASI {
#define FOR_EACH_WASI_FUNC(F) \
F(proc_exit, I32R) \
F(proc_raise, I32_RI32) \
F(clock_res_get, I32I32_RI32) \
F(clock_time_get, I32I64I32_RI32) \
F(random_get, I32I32_RI32) \
F(fd_write, I32I32I32I32_RI32) \
F(fd_read, I32I32I32I32_RI32) \
Expand All @@ -52,7 +54,7 @@ class WASI {
ERR(toobig, "argument list too long") \
ERR(acces, "permission denied") \
ERR(addrinuse, "addres in use") \
ERR(addrnotavauil, "addres not abailable") \
ERR(addrnotavauil, "addres not available") \
ERR(afnosupport, "address family not supported") \
ERR(again, "resource unavailable, or operation would block") \
ERR(already, "connection already in progress") \
Expand Down Expand Up @@ -152,6 +154,8 @@ class WASI {
// wasi functions
static void proc_exit(ExecutionState& state, Value* argv, Value* result, Instance* instance);
static void proc_raise(ExecutionState& state, Value* argv, Value* result, Instance* instance);
static void clock_res_get(ExecutionState& state, Value* argv, Value* result, Instance* instance);
static void clock_time_get(ExecutionState& state, Value* argv, Value* result, Instance* instance);
static void fd_write(ExecutionState& state, Value* argv, Value* result, Instance* instance);
static void fd_read(ExecutionState& state, Value* argv, Value* result, Instance* instance);
static void fd_close(ExecutionState& state, Value* argv, Value* result, Instance* instance);
Expand Down
23 changes: 23 additions & 0 deletions test/wasi/clock.wast
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
(module
(import "wasi_snapshot_preview1" "clock_res_get" (func $clock_res_get (param i32 i32) (result i32)))
(import "wasi_snapshot_preview1" "clock_time_get" (func $clock_time_get (param i32 i64 i32) (result i32)))
(import "wasi_snapshot_preview1" "proc_exit" (func $proc_exit (param i32)))
(memory 1)
(export "_start" (func $_start))
(func $_start
(call $clock_res_get (i32.const 0) (i32.const 0))
(if
(then
i32.const 1
call $proc_exit
)
)

(call $clock_time_get (i32.const 0) (i64.const 1000000) (i32.const 0))
i32.eqz
i32.eqz
call $proc_exit
)
)

(assert_return (invoke "_start"))

0 comments on commit 20d770b

Please sign in to comment.