Преглед изворни кода

wamr-wasi-extensions: add a cmake package to provide our wasi extension (#4344)

* wasi_ephemeral_nn.h: add a convenience wrapper header
* wamr-wasi-extensions: add a cmake package to provide our wasi extension

the sample app was tested with:
* wasmtime
* iwasm with https://github.com/bytecodealliance/wasm-micro-runtime/pull/4308

currently only contains wasi-nn.
maybe it makes sense to add lib-socket things as well.

cf. https://github.com/bytecodealliance/wasm-micro-runtime/issues/4288
YAMAMOTO Takashi пре 7 месеци
родитељ
комит
3a087c4244

+ 7 - 0
core/iwasm/libraries/wasi-nn/include/wasi_ephemeral_nn.h

@@ -0,0 +1,7 @@
+/*
+ * Copyright (C) 2025 Midokura Japan KK.  All rights reserved.
+ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+ */
+
+#define WASM_ENABLE_WASI_EPHEMERAL_NN 1
+#include "wasi_nn.h"

+ 8 - 0
wamr-wasi-extensions/CMakeLists.txt

@@ -0,0 +1,8 @@
+# Copyright (C) 2025 Midokura Japan KK.  All rights reserved.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+cmake_minimum_required (VERSION 3.14)
+
+project(wamr-wasi-extensions LANGUAGES C)
+
+add_subdirectory(nn)

+ 25 - 0
wamr-wasi-extensions/nn/CMakeLists.txt

@@ -0,0 +1,25 @@
+# Copyright (C) 2025 Midokura Japan KK.  All rights reserved.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+add_library(wamr-wasi-nn INTERFACE)
+
+set(wasi_nn_header_dir ${CMAKE_CURRENT_SOURCE_DIR}/../../core/iwasm/libraries/wasi-nn/include)
+
+set(headers
+    ${wasi_nn_header_dir}/wasi_ephemeral_nn.h
+    ${wasi_nn_header_dir}/wasi_nn.h
+    ${wasi_nn_header_dir}/wasi_nn_types.h
+)
+
+set_property(TARGET wamr-wasi-nn PROPERTY PUBLIC_HEADER ${headers})
+
+target_include_directories(wamr-wasi-nn
+                           INTERFACE
+                           $<BUILD_INTERFACE:${wasi_nn_header_dir}>
+                           $<INSTALL_INTERFACE:include>)
+
+install(TARGETS wamr-wasi-nn
+        EXPORT wamr-wasi-nn-config
+        PUBLIC_HEADER DESTINATION include/wamr)
+install(EXPORT wamr-wasi-nn-config
+        DESTINATION lib/cmake/wamr-wasi-nn)

+ 13 - 0
wamr-wasi-extensions/samples/nn/CMakeLists.txt

@@ -0,0 +1,13 @@
+# Copyright (C) 2025 Midokura Japan KK.  All rights reserved.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+cmake_minimum_required(VERSION 3.14)
+
+set(CMAKE_C_STANDARD 99)
+set(CMAKE_C_STANDARD_REQUIRED YES)
+set(CMAKE_C_EXTENSIONS NO)
+
+project(nn-classification-openvino LANGUAGES C)
+add_executable(nn-classification-openvino "app.c")
+find_package(wamr-wasi-nn REQUIRED)
+target_link_libraries(nn-classification-openvino wamr-wasi-nn)

+ 169 - 0
wamr-wasi-extensions/samples/nn/app.c

@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2025 Midokura Japan KK.  All rights reserved.
+ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+ */
+
+#include <sys/stat.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <wamr/wasi_ephemeral_nn.h>
+
+/*
+ * what this application does is basically same as:
+ * https://github.com/bytecodealliance/wasmtime/tree/efa236e58d09570baaf27865da33fb852fcf40a5/crates/wasi-nn/examples/classification-example
+ *
+ * map_file/unmap_file are copy-and-pasted from:
+ * https://github.com/yamt/toywasm/blob/0eaad8cacd0cc7692946ff19b25994f106113be8/lib/fileio.c
+ */
+
+int
+map_file(const char *path, void **pp, size_t *sizep)
+{
+    void *p;
+    size_t size;
+    ssize_t ssz;
+    int fd;
+    int ret;
+
+    fd = open(path, O_RDONLY);
+    if (fd == -1) {
+        ret = errno;
+        assert(ret != 0);
+        return ret;
+    }
+    struct stat st;
+    ret = fstat(fd, &st);
+    if (ret == -1) {
+        ret = errno;
+        assert(ret != 0);
+        close(fd);
+        return ret;
+    }
+    size = st.st_size;
+    if (size > 0) {
+        p = malloc(size);
+    }
+    else {
+        /* Avoid a confusing error */
+        p = malloc(1);
+    }
+    if (p == NULL) {
+        close(fd);
+        return ENOMEM;
+    }
+    ssz = read(fd, p, size);
+    if (ssz != size) {
+        ret = errno;
+        assert(ret != 0);
+        close(fd);
+        return ret;
+    }
+    close(fd);
+    *pp = p;
+    *sizep = size;
+    return 0;
+}
+
+void
+unmap_file(void *p, size_t sz)
+{
+    free(p);
+}
+
+static void
+print_result(const float *result, size_t sz)
+{
+    /*
+     * just dump the raw result.
+     * you can postprocess the output with eg. "sort -k2nr | head"
+     */
+    int i;
+    for (i = 0; i < sz / sizeof(float); i++) {
+        printf("%d %f\n", i, result[i]);
+    }
+}
+
+int
+main(int argc, char **argv)
+{
+    wasi_nn_error nnret;
+    int ret;
+    void *xml;
+    size_t xmlsz;
+    ret = map_file("fixture/model.xml", &xml, &xmlsz);
+    if (ret != 0) {
+        fprintf(stderr, "failed to load fixture/model.xml: %s\n",
+                strerror(ret));
+        exit(1);
+    }
+    void *weights;
+    size_t weightssz;
+    ret = map_file("fixture/model.bin", &weights, &weightssz);
+    if (ret != 0) {
+        fprintf(stderr, "failed to load fixture/model.bin: %s\n",
+                strerror(ret));
+        exit(1);
+    }
+    /* note: openvino takes two buffers, namely IR and weights */
+    graph_builder builders[2] = { {
+                                      .buf = xml,
+                                      .size = xmlsz,
+                                  },
+                                  {
+                                      .buf = weights,
+                                      .size = weightssz,
+                                  } };
+    graph g;
+    nnret = load(builders, 2, openvino, cpu, &g);
+    unmap_file(xml, xmlsz);
+    unmap_file(weights, weightssz);
+    if (nnret != success) {
+        fprintf(stderr, "load failed with %d\n", (int)nnret);
+        exit(1);
+    }
+    graph_execution_context ctx;
+    nnret = init_execution_context(g, &ctx);
+    if (nnret != success) {
+        fprintf(stderr, "init_execution_context failed with %d\n", (int)nnret);
+        exit(1);
+    }
+    void *tensordata;
+    size_t tensordatasz;
+    ret = map_file("fixture/tensor.bgr", &tensordata, &tensordatasz);
+    if (ret != 0) {
+        fprintf(stderr, "failed to load fixture/tensor.bgr: %s\n",
+                strerror(ret));
+        exit(1);
+    }
+    tensor tensor = {
+        .dimensions = { .buf = (uint32_t[]){1, 3, 224, 224,}, .size = 4, },
+        .type = fp32,
+        .data = tensordata,
+    };
+    nnret = set_input(ctx, 0, &tensor);
+    unmap_file(tensordata, tensordatasz);
+    if (nnret != success) {
+        fprintf(stderr, "set_input failed with %d\n", (int)nnret);
+        exit(1);
+    }
+    nnret = compute(ctx);
+    if (nnret != success) {
+        fprintf(stderr, "compute failed with %d\n", (int)nnret);
+        exit(1);
+    }
+    float result[1001];
+    uint32_t resultsz;
+    nnret = get_output(ctx, 0, (void *)result, sizeof(result), &resultsz);
+    if (nnret != success) {
+        fprintf(stderr, "get_output failed with %d\n", (int)nnret);
+        exit(1);
+    }
+    print_result(result, resultsz);
+}

+ 19 - 0
wamr-wasi-extensions/test.sh

@@ -0,0 +1,19 @@
+#! /bin/sh
+
+# Copyright (C) 2025 Midokura Japan KK.  All rights reserved.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+PREFIX=/tmp/wamr
+WASI_SDK=${WASI_SDK:-/opt/wasi-sdk}
+
+cmake -B build-lib \
+-DCMAKE_TOOLCHAIN_FILE=${WASI_SDK}/share/cmake/wasi-sdk.cmake \
+-DCMAKE_INSTALL_PREFIX=${PREFIX} \
+.
+cmake --build build-lib -t install
+
+cmake -B build-app-nn \
+-DCMAKE_TOOLCHAIN_FILE=${WASI_SDK}/share/cmake/wasi-sdk.cmake \
+-DCMAKE_PREFIX_PATH=${PREFIX} \
+samples/nn
+cmake --build build-app-nn