mirror of
https://github.com/pykeio/ort
synced 2026-04-25 16:34:55 +02:00
chore: remove wasm32-unknown-unknown support
This commit is contained in:
13
.github/workflows/test.yml
vendored
13
.github/workflows/test.yml
vendored
@@ -53,19 +53,6 @@ jobs:
|
||||
cargo test -p ort --verbose --features fetch-models -- --test-threads 1
|
||||
# Test examples that use in-tree graphs (do NOT run any of the examples that download ~700 MB graphs from pyke parcel...)
|
||||
cargo run --example custom-ops
|
||||
test-wasm:
|
||||
name: Test WebAssembly
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install nightly Rust toolchain
|
||||
uses: dtolnay/rust-toolchain@nightly
|
||||
- name: Install wasm-pack
|
||||
run: cargo install wasm-pack
|
||||
- name: Run tests
|
||||
working-directory: examples/webassembly
|
||||
run: |
|
||||
wasm-pack test --node
|
||||
# Disable cross-compile until cross updates aarch64-unknown-linux-gnu to Ubuntu 22.04
|
||||
# ref https://github.com/cross-rs/cross/pull/973
|
||||
#cross-compile:
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -187,7 +187,6 @@ WixTools/
|
||||
**/*.onnx
|
||||
**/*.ort
|
||||
**/*.pbseq
|
||||
!examples/webassembly/**/*.ort
|
||||
!tests/data/*.onnx
|
||||
!tests/data/*.ort
|
||||
|
||||
|
||||
10
Cargo.toml
10
Cargo.toml
@@ -8,8 +8,7 @@ members = [
|
||||
'examples/yolov8',
|
||||
'examples/modnet',
|
||||
'examples/sentence-transformers',
|
||||
'examples/training',
|
||||
'examples/webassembly'
|
||||
'examples/training'
|
||||
]
|
||||
default-members = [
|
||||
'.',
|
||||
@@ -49,7 +48,7 @@ codegen-units = 1
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = [ "ndarray", "half", "training", "operator-libraries", "fetch-models", "load-dynamic", "copy-dylibs" ]
|
||||
targets = ["x86_64-unknown-linux-gnu", "wasm32-unknown-unknown"]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
rustdoc-args = [ "--cfg", "docsrs" ]
|
||||
|
||||
[features]
|
||||
@@ -99,10 +98,6 @@ libc = { version = "0.2", optional = true }
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
winapi = { version = "0.3", optional = true, features = [ "std", "libloaderapi" ] }
|
||||
|
||||
[target.'cfg(target_family = "wasm")'.dependencies]
|
||||
js-sys = "0.3"
|
||||
web-sys = "0.3"
|
||||
|
||||
[dev-dependencies]
|
||||
anyhow = "1.0"
|
||||
ureq = "2.1"
|
||||
@@ -112,7 +107,6 @@ tracing-subscriber = { version = "0.3", default-features = false, features = [ "
|
||||
glassbench = "0.4"
|
||||
tokio = { version = "1.36", features = [ "test-util" ] }
|
||||
tokio-test = "0.4.3"
|
||||
wasm-bindgen-test = "0.3"
|
||||
|
||||
[[bench]]
|
||||
name = "squeezenet"
|
||||
|
||||
@@ -12,21 +12,19 @@ Here are the supported platforms and binary availability status, as of v2.0.0-rc
|
||||
* ⭕ - Supported. Precompiled binaries not available.
|
||||
* ❌ - Not supported.
|
||||
|
||||
| Platform | x86 | x86-64 | ARMv7 | ARM64 | WASM32 |
|
||||
|:-------- |:------- |:------ |:------ |:------ |:------ |
|
||||
| **Windows** | ⭕ | 🟢<sup>\*</sup> | ⭕ | 🔷<sup>\*</sup> | ❌ |
|
||||
| **Linux** | ⭕ | 🟢† | ⭕ | 🔷‡ | ❌ |
|
||||
| **macOS** | ❌ | 🔷§ | ❌ | 🔷 | ❌ |
|
||||
| **iOS** | ❌ | ❌ | ❌ | ⭕ | ❌ |
|
||||
| **Android** | ❌ | ❌ | ⭕ | ⭕ | ❌ |
|
||||
| **Web** | ❌ | ❌ | ❌ | ❌ | 🔷¶ |
|
||||
| Platform | x86 | x86-64 | ARMv7 | ARM64 |
|
||||
|:-------- |:------- |:------ |:------ |:------ |
|
||||
| **Windows** | ⭕ | 🟢<sup>\*</sup> | ⭕ | 🔷<sup>\*</sup> |
|
||||
| **Linux** | ⭕ | 🟢† | ⭕ | 🔷‡ |
|
||||
| **macOS** | ❌ | 🔷§ | ❌ | 🔷 |
|
||||
| **iOS** | ❌ | ❌ | ❌ | ⭕ |
|
||||
| **Android** | ❌ | ❌ | ⭕ | ⭕ |
|
||||
|
||||
<div style={{ opacity: 0.5, display: 'flex', flexDirection: 'column', fontSize: '0.75rem' }}>
|
||||
<p>\* A recent version of Windows 10/11 & Visual Studio 2022 are required for pyke binaries.</p>
|
||||
<p>† glibc ≥ 2.31 (Ubuntu ≥ 20.04) required for pyke binaries.</p>
|
||||
<p>‡ glibc ≥ 2.35 (Ubuntu ≥ 22.04) required for pyke binaries.</p>
|
||||
<p>§ macOS ≥ 10.15 required.</p>
|
||||
<p>¶ WASM supports a limited subset of ONNX Runtime features. For more info, see [the docs on WebAssembly support](/setup/webassembly).</p>
|
||||
</div>
|
||||
|
||||
If your platform is marked as 🟢 or 🔷, you're in luck! Almost no setup will be required to get `ort` up and running.
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
---
|
||||
title: WebAssembly
|
||||
description: Deploy ONNX models to the web
|
||||
---
|
||||
|
||||
WebAssembly support in `ort` is currently experimental. If you experience any issues using `ort` in WebAssembly, please [open an issue](https://github.com/pykeio/ort/issues/new).
|
||||
|
||||
By nature, some features of ONNX Runtime are not available in the web. These include:
|
||||
- **Support for `.onnx` models.** You instead need to [convert `.onnx` models to the `.ort` format](https://onnxruntime.ai/docs/performance/model-optimizations/ort-format-models.html).
|
||||
- **Runtime graph optimizations**, aka `SessionBuilder::with_optimization_level`. You can statically optimize the graph using the `.ort` conversion tool, though.
|
||||
- **Loading models with `commit_from_file`/`commit_from_url`.** You can create models from a slice of bytes in memory with `SessionBuilder::commit_from_memory` or `SessionBuilder::commit_from_memory_directly`.
|
||||
|
||||
Additionally, you'll need to call `ort::wasm::initialize()` at the earliest possible point in your code, before you use any `ort` APIs:
|
||||
```rust filename="main.rs" copy
|
||||
use ort::Session;
|
||||
|
||||
static MODEL_BYTES: &[u8] = include_bytes!("../model.ort");
|
||||
|
||||
fn main() -> anyhow::Result<()> {
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
ort::wasm::initialize();
|
||||
|
||||
let session = Session::builder()?.commit_from_memory_directly(MODEL_BYTES)?;
|
||||
...
|
||||
}
|
||||
```
|
||||
@@ -1,26 +0,0 @@
|
||||
[package]
|
||||
publish = false
|
||||
name = "example-webassembly"
|
||||
version = "0.0.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
name = "ortwasm"
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
ort = { path = "../../" }
|
||||
ndarray = "0.16"
|
||||
wasm-bindgen = "0.2.92"
|
||||
web-sys = "0.3"
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = "0.3"
|
||||
tracing-subscriber-wasm = "0.1"
|
||||
image = { version = "0.25", default-features = false, features = [ "jpeg" ]}
|
||||
|
||||
[dev-dependencies]
|
||||
wasm-bindgen-test = "0.3"
|
||||
console_error_panic_hook = "0.1"
|
||||
|
||||
[features]
|
||||
load-dynamic = [ "ort/load-dynamic" ]
|
||||
@@ -1,75 +0,0 @@
|
||||
use image::{ImageBuffer, Luma, Pixel};
|
||||
use ort::{ArrayExtensions, Session};
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
static IMAGE_BYTES: &[u8] = include_bytes!("../../../tests/data/mnist_5.jpg");
|
||||
static MODEL_BYTES: &[u8] = include_bytes!("mnist.ort");
|
||||
|
||||
pub fn upsample_inner() -> ort::Result<()> {
|
||||
let session = Session::builder()?
|
||||
.commit_from_memory_directly(MODEL_BYTES)
|
||||
.expect("Could not read model from memory");
|
||||
|
||||
// NOTE: An earlier nightly version of Rust 1.78 includes a patch required for ONNX Runtime to link properly, but a
|
||||
// later version enables debug assertions in `dlmalloc`, which surfaces an allocation bug in the `image` crate:
|
||||
// https://github.com/rustwasm/wasm-pack/issues/1389 Because of this, using `image::load_from_memory` crashes.
|
||||
// For demonstration purposes, we're replacing the image loading code shown below with zeros(). In a real application,
|
||||
// you can get the image from another source, like an HTML canvas.
|
||||
//
|
||||
// let image_buffer: ImageBuffer<Luma<u8>, Vec<u8>> = image::load_from_memory(IMAGE_BYTES).unwrap().to_luma8();
|
||||
// let array = ndarray::Array::from_shape_fn((1, 1, 28, 28), |(_, c, j, i)| {
|
||||
// let pixel = image_buffer.get_pixel(i as u32, j as u32);
|
||||
// let channels = pixel.channels();
|
||||
// (channels[c] as f32) / 255.0
|
||||
// });
|
||||
let array = ndarray::Array4::<f32>::zeros((1, 1, 28, 28));
|
||||
|
||||
let outputs = session.run(ort::inputs![array]?)?;
|
||||
|
||||
let mut probabilities: Vec<(usize, f32)> = outputs[0]
|
||||
.try_extract_tensor()?
|
||||
.softmax(ndarray::Axis(1))
|
||||
.iter()
|
||||
.copied()
|
||||
.enumerate()
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
probabilities.sort_unstable_by(|a, b| b.1.partial_cmp(&a.1).unwrap());
|
||||
|
||||
assert_eq!(probabilities[0].0, 5, "Expecting class '5' (got {})", probabilities[0].0);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
macro_rules! console_log {
|
||||
($($t:tt)*) => (web_sys::console::log_1(&format_args!($($t)*).to_string().into()))
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn upsample() {
|
||||
if let Err(e) = upsample_inner() {
|
||||
console_log!("Error occurred while performing inference: {e:?}");
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[wasm_bindgen_test::wasm_bindgen_test]
|
||||
fn run_test() {
|
||||
use tracing::Level;
|
||||
use tracing_subscriber::fmt;
|
||||
use tracing_subscriber_wasm::MakeConsoleWriter;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
ort::wasm::initialize();
|
||||
|
||||
fmt()
|
||||
.with_ansi(false)
|
||||
.with_max_level(Level::DEBUG)
|
||||
.with_writer(MakeConsoleWriter::default().map_trace_level_to(Level::DEBUG))
|
||||
.without_time()
|
||||
.init();
|
||||
|
||||
std::panic::set_hook(Box::new(console_error_panic_hook::hook));
|
||||
|
||||
upsample();
|
||||
}
|
||||
Binary file not shown.
@@ -215,15 +215,6 @@ fn prepare_libort_dir() -> (PathBuf, bool) {
|
||||
println!("cargo:rustc-link-lib=static=noexcep_operators");
|
||||
}
|
||||
|
||||
if target_arch == "wasm32" {
|
||||
for lib in &["webassembly", "providers_js"] {
|
||||
let lib_path = lib_dir.join(platform_format_lib(&format!("onnxruntime_{lib}")));
|
||||
if lib_path.exists() {
|
||||
println!("cargo:rustc-link-lib=static=onnxruntime_{lib}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let protobuf_build = transform_dep(external_lib_dir.join("protobuf-build"), &profile);
|
||||
add_search_dir(&protobuf_build);
|
||||
for lib in ["protobuf-lited", "protobuf-lite", "protobuf"] {
|
||||
@@ -245,17 +236,15 @@ fn prepare_libort_dir() -> (PathBuf, bool) {
|
||||
println!("cargo:rustc-link-lib=static=nsync_cpp");
|
||||
}
|
||||
|
||||
if target_arch != "wasm32" {
|
||||
add_search_dir(transform_dep(external_lib_dir.join("pytorch_cpuinfo-build"), &profile));
|
||||
let clog_path = transform_dep(external_lib_dir.join("pytorch_cpuinfo-build").join("deps").join("clog"), &profile);
|
||||
if clog_path.exists() {
|
||||
add_search_dir(clog_path);
|
||||
} else {
|
||||
add_search_dir(transform_dep(external_lib_dir.join("pytorch_clog-build"), &profile));
|
||||
}
|
||||
println!("cargo:rustc-link-lib=static=cpuinfo");
|
||||
println!("cargo:rustc-link-lib=static=clog");
|
||||
add_search_dir(transform_dep(external_lib_dir.join("pytorch_cpuinfo-build"), &profile));
|
||||
let clog_path = transform_dep(external_lib_dir.join("pytorch_cpuinfo-build").join("deps").join("clog"), &profile);
|
||||
if clog_path.exists() {
|
||||
add_search_dir(clog_path);
|
||||
} else {
|
||||
add_search_dir(transform_dep(external_lib_dir.join("pytorch_clog-build"), &profile));
|
||||
}
|
||||
println!("cargo:rustc-link-lib=static=cpuinfo");
|
||||
println!("cargo:rustc-link-lib=static=clog");
|
||||
|
||||
add_search_dir(transform_dep(external_lib_dir.join("re2-build"), &profile));
|
||||
println!("cargo:rustc-link-lib=static=re2");
|
||||
|
||||
@@ -19,7 +19,7 @@ pub type ortchar = c_ushort;
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
pub type ortchar = c_char;
|
||||
|
||||
#[cfg(any(target_arch = "x86_64", target_arch = "x86", target_arch = "wasm32"))]
|
||||
#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
|
||||
pub type size_t = usize;
|
||||
#[cfg(all(target_arch = "aarch64", target_os = "windows"))]
|
||||
pub type size_t = c_ulonglong;
|
||||
|
||||
@@ -39,8 +39,7 @@ impl ExecutionProvider for XNNPACKExecutionProvider {
|
||||
cfg!(any(
|
||||
target_arch = "aarch64",
|
||||
all(target_arch = "arm", any(target_os = "linux", target_os = "android")),
|
||||
target_arch = "x86_64",
|
||||
target_arch = "wasm32"
|
||||
target_arch = "x86_64"
|
||||
))
|
||||
}
|
||||
|
||||
|
||||
@@ -27,9 +27,6 @@ pub(crate) mod tensor;
|
||||
pub(crate) mod training;
|
||||
pub(crate) mod util;
|
||||
pub(crate) mod value;
|
||||
#[cfg_attr(docsrs, doc(cfg(target_arch = "wasm32")))]
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub mod wasm;
|
||||
|
||||
#[cfg(feature = "load-dynamic")]
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -2,11 +2,10 @@
|
||||
use std::ffi::CString;
|
||||
#[cfg(feature = "fetch-models")]
|
||||
use std::fmt::Write;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use std::path::Path;
|
||||
use std::{
|
||||
any::Any,
|
||||
marker::PhantomData,
|
||||
path::Path,
|
||||
ptr::{self, NonNull},
|
||||
rc::Rc,
|
||||
sync::{atomic::Ordering, Arc}
|
||||
@@ -162,8 +161,6 @@ impl SessionBuilder {
|
||||
/// newly optimized model to the given path (for 'offline' graph optimization).
|
||||
///
|
||||
/// Note that the file will only be created after the model is committed.
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(not(target_arch = "wasm32"))))]
|
||||
pub fn with_optimized_model_path<S: AsRef<str>>(self, path: S) -> Result<Self> {
|
||||
#[cfg(windows)]
|
||||
let path = path.as_ref().encode_utf16().chain([0]).collect::<Vec<_>>();
|
||||
@@ -175,8 +172,6 @@ impl SessionBuilder {
|
||||
|
||||
/// Enables profiling. Profile information will be writen to `profiling_file` after profiling completes.
|
||||
/// See [`Session::end_profiling`].
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(not(target_arch = "wasm32"))))]
|
||||
pub fn with_profiling<S: AsRef<str>>(self, profiling_file: S) -> Result<Self> {
|
||||
#[cfg(windows)]
|
||||
let profiling_file = profiling_file.as_ref().encode_utf16().chain([0]).collect::<Vec<_>>();
|
||||
@@ -205,8 +200,8 @@ impl SessionBuilder {
|
||||
}
|
||||
|
||||
/// Registers a custom operator library at the given library path.
|
||||
#[cfg(all(feature = "operator-libraries", not(target_arch = "wasm32")))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all(feature = "operator-libraries", not(target_arch = "wasm32")))))]
|
||||
#[cfg(feature = "operator-libraries")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "operator-libraries")))]
|
||||
pub fn with_operator_library(mut self, lib_path: impl AsRef<str>) -> Result<Self> {
|
||||
let path_cstr = CString::new(lib_path.as_ref())?;
|
||||
|
||||
@@ -232,8 +227,6 @@ impl SessionBuilder {
|
||||
}
|
||||
|
||||
/// Enables [`onnxruntime-extensions`](https://github.com/microsoft/onnxruntime-extensions) custom operators.
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(not(target_arch = "wasm32"))))]
|
||||
pub fn with_extensions(self) -> Result<Self> {
|
||||
let status = ortsys![unsafe EnableOrtCustomOps(self.session_options_ptr.as_ptr())];
|
||||
status_to_result(status).map_err(Error::CreateSessionOptions)?;
|
||||
@@ -248,8 +241,8 @@ impl SessionBuilder {
|
||||
}
|
||||
|
||||
/// Downloads a pre-trained ONNX model from the given URL and builds the session.
|
||||
#[cfg(all(feature = "fetch-models", not(target_arch = "wasm32")))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all(feature = "fetch-models", not(target_arch = "wasm32")))))]
|
||||
#[cfg(feature = "fetch-models")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "fetch-models")))]
|
||||
pub fn commit_from_url(self, model_url: impl AsRef<str>) -> Result<Session> {
|
||||
let mut download_dir = ort_sys::internal::dirs::cache_dir()
|
||||
.expect("could not determine cache directory")
|
||||
@@ -299,8 +292,6 @@ impl SessionBuilder {
|
||||
}
|
||||
|
||||
/// Loads an ONNX model from a file and builds the session.
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(not(target_arch = "wasm32"))))]
|
||||
pub fn commit_from_file<P>(mut self, model_filepath_ref: P) -> Result<Session>
|
||||
where
|
||||
P: AsRef<Path>
|
||||
|
||||
236
src/wasm.rs
236
src/wasm.rs
@@ -1,236 +0,0 @@
|
||||
//! Utilities for using `ort` in WebAssembly.
|
||||
//!
|
||||
//! You **must** call `ort::wasm::initialize()` before using any `ort` APIs in WASM:
|
||||
//! ```
|
||||
//! # use ort::Session;
|
||||
//! # static MODEL_BYTES: &[u8] = include_bytes!("../tests/data/upsample.ort");
|
||||
//! # fn main() -> ort::Result<()> {
|
||||
//! #[cfg(target_arch = "wasm32")]
|
||||
//! ort::wasm::initialize();
|
||||
//!
|
||||
//! let session = Session::builder()?.commit_from_memory_directly(MODEL_BYTES)?;
|
||||
//! # Ok(())
|
||||
//! # }
|
||||
//! ```
|
||||
|
||||
use std::{
|
||||
alloc::{self, Layout},
|
||||
arch::wasm32,
|
||||
ptr, slice, str
|
||||
};
|
||||
|
||||
mod fmt_shims {
|
||||
// localized time string formatting functions
|
||||
// TODO: remove any remaining codepaths to these
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn strftime_l(_s: *mut u8, _l: usize, _m: *const u8, _t: *const (), _lt: *const ()) -> usize {
|
||||
unimplemented!()
|
||||
}
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn _tzset_js(_timezone: *mut u32, _daylight: *const i32, _name: *const u8, _dst_name: *mut u8) {
|
||||
unimplemented!()
|
||||
}
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn _mktime_js(_tm: *mut ()) -> ! {
|
||||
unimplemented!()
|
||||
}
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn _localtime_js(_time_t: i64, _tm: *mut ()) -> ! {
|
||||
unimplemented!()
|
||||
}
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn _gmtime_js(_time_t: i64, _tm: *mut ()) -> ! {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) mod libc_shims {
|
||||
use super::*;
|
||||
|
||||
// Rust, unlike C, requires us to know the exact layout of an allocation in order to deallocate it, so we need to
|
||||
// store this data at the beginning of the allocation for us to be able to pick up on deallocation:
|
||||
//
|
||||
// ┌---- actual allocated pointer
|
||||
// ▼
|
||||
// +-------------+-------+------+----------------+
|
||||
// | ...padding | align | size | data... |
|
||||
// | -align..-8 | -8 | -4 | 0..size |
|
||||
// +-------------+- -----+------+----------------+
|
||||
// ▲
|
||||
// pointer returned to C ---┘
|
||||
//
|
||||
// This does unfortunately mean we waste a little extra memory (note that most allocators *also* store the layout
|
||||
// information in a similar manner, but we can't access it).
|
||||
|
||||
const _: () = assert!(std::mem::size_of::<usize>() == 4, "32-bit pointer width (wasm32) required");
|
||||
|
||||
unsafe fn alloc_inner<const ZERO: bool>(size: usize, align: usize) -> *mut u8 {
|
||||
// need enough space to store the size & alignment bytes
|
||||
let align = align.max(8);
|
||||
|
||||
let layout = Layout::from_size_align_unchecked(size + align, align);
|
||||
let ptr = if ZERO { alloc::alloc_zeroed(layout) } else { alloc::alloc(layout) };
|
||||
ptr::copy_nonoverlapping(size.to_le_bytes().as_ptr(), ptr.add(align - 4), 4);
|
||||
ptr::copy_nonoverlapping(align.to_le_bytes().as_ptr(), ptr.add(align - 8), 4);
|
||||
ptr.add(align)
|
||||
}
|
||||
|
||||
unsafe fn free_inner(ptr: *mut u8) {
|
||||
// something likes to free(NULL) a lot, which is valid in C (because of course it is...)
|
||||
if ptr.is_null() {
|
||||
return;
|
||||
}
|
||||
|
||||
let size = usize::from_le_bytes(slice::from_raw_parts_mut(ptr.sub(4), 4).try_into().unwrap_unchecked());
|
||||
let align = usize::from_le_bytes(slice::from_raw_parts_mut(ptr.sub(8), 4).try_into().unwrap_unchecked());
|
||||
let layout = Layout::from_size_align_unchecked(size + align, align);
|
||||
alloc::dealloc(ptr.sub(align), layout);
|
||||
}
|
||||
|
||||
const DEFAULT_ALIGNMENT: usize = 32;
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn malloc(size: usize) -> *mut u8 {
|
||||
alloc_inner::<false>(size, DEFAULT_ALIGNMENT)
|
||||
}
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn __libc_malloc(size: usize) -> *mut u8 {
|
||||
alloc_inner::<false>(size, DEFAULT_ALIGNMENT)
|
||||
}
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn __libc_calloc(n: usize, size: usize) -> *mut u8 {
|
||||
alloc_inner::<true>(size * n, DEFAULT_ALIGNMENT)
|
||||
}
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn free(ptr: *mut u8) {
|
||||
free_inner(ptr)
|
||||
}
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn __libc_free(ptr: *mut u8) {
|
||||
free_inner(ptr)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn posix_memalign(ptr: *mut *mut u8, align: usize, size: usize) -> i32 {
|
||||
*ptr = alloc_inner::<false>(size, align);
|
||||
0
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn realloc(ptr: *mut u8, newsize: usize) -> *mut u8 {
|
||||
let size = usize::from_le_bytes(slice::from_raw_parts_mut(ptr.sub(4), 4).try_into().unwrap_unchecked());
|
||||
let align = usize::from_le_bytes(slice::from_raw_parts_mut(ptr.sub(8), 4).try_into().unwrap_unchecked());
|
||||
let layout = Layout::from_size_align_unchecked(size + align, align);
|
||||
let ptr = alloc::realloc(ptr.sub(align), layout, newsize);
|
||||
ptr::copy_nonoverlapping(size.to_le_bytes().as_ptr(), ptr.add(align - 4), 4);
|
||||
ptr.add(align)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn abort() -> ! {
|
||||
std::process::abort()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "wasi"))]
|
||||
mod wasi_shims {
|
||||
#[allow(non_camel_case_types)]
|
||||
type __wasi_errno_t = u16;
|
||||
|
||||
const __WASI_ENOTSUP: __wasi_errno_t = 58;
|
||||
|
||||
// mock filesystem for non-WASI platforms - most of the codepaths to any FS operations should've been removed, but we
|
||||
// return ENOTSUP just to be safe
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn __wasi_environ_sizes_get(argc: *mut usize, argv_buf_size: *mut usize) -> __wasi_errno_t {
|
||||
*argc = 0;
|
||||
*argv_buf_size = 0;
|
||||
__WASI_ENOTSUP
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn __wasi_environ_get(_environ: *mut *mut u8, _buf: *mut u8) -> __wasi_errno_t {
|
||||
__WASI_ENOTSUP
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn __wasi_fd_seek(_fd: u32, _offset: i64, _whence: u8, _new_offset: *mut u64) -> __wasi_errno_t {
|
||||
__WASI_ENOTSUP
|
||||
}
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn __wasi_fd_write(_fd: u32, _iovs: *const (), _iovs_len: usize, _nwritten: *mut usize) -> __wasi_errno_t {
|
||||
__WASI_ENOTSUP
|
||||
}
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn __wasi_fd_read(_fd: u32, _iovs: *const (), _iovs_len: usize, _nread: *mut usize) -> __wasi_errno_t {
|
||||
__WASI_ENOTSUP
|
||||
}
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn __wasi_fd_close(_fd: u32) -> __wasi_errno_t {
|
||||
__WASI_ENOTSUP
|
||||
}
|
||||
}
|
||||
|
||||
mod emscripten_shims {
|
||||
use super::*;
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn emscripten_memcpy_js(dst: *mut (), src: *const (), n: usize) {
|
||||
std::ptr::copy_nonoverlapping(src, dst, n)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn emscripten_get_now() -> f64 {
|
||||
js_sys::Date::now()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn emscripten_get_heap_max() -> usize {
|
||||
wasm32::memory_size(0) << 16
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn emscripten_date_now() -> f64 {
|
||||
js_sys::Date::now()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn _emscripten_get_now_is_monotonic() -> i32 {
|
||||
0
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn emscripten_builtin_malloc(size: usize) -> *mut u8 {
|
||||
alloc::alloc_zeroed(Layout::from_size_align_unchecked(size, 32))
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[tracing::instrument]
|
||||
pub unsafe extern "C" fn emscripten_errn(str: *const u8, len: usize) {
|
||||
let c = str::from_utf8_unchecked(slice::from_raw_parts(str, len));
|
||||
tracing::error!("Emscripten error: {c}");
|
||||
}
|
||||
|
||||
// despite disabling exceptions literally everywhere when compiling, we still have to stub this...
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn __cxa_throw(_ptr: *const (), _type: *const (), _destructor: *const ()) -> ! {
|
||||
std::process::abort();
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[export_name = "_initialize"]
|
||||
pub fn initialize() {
|
||||
// The presence of an `_initialize` function prevents the linker from calling `__wasm_call_ctors` at the top of every
|
||||
// function - including the functions `wasm-bindgen` interprets to generate JS glue code. `__wasm_call_ctors` calls
|
||||
// complex functions that wbg's interpreter isn't equipped to handle, which was preventing wbg from outputting
|
||||
// anything.
|
||||
// I'm not entirely sure what `__wasm_call_ctors` is initializing, but it seems to have something to do with C++
|
||||
// vtables, and it's crucial for proper operation.
|
||||
extern "C" {
|
||||
fn __wasm_call_ctors();
|
||||
}
|
||||
unsafe { __wasm_call_ctors() };
|
||||
}
|
||||
Reference in New Issue
Block a user